home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / asmutil / a86v322.zip / TCOLS.8 < prev    next >
Text File  |  1987-11-24  |  18KB  |  407 lines

  1. ;---------------
  2. ;   TCOLS
  3. ;---------------
  4.  
  5.   JMP TCOLS
  6.  
  7. DEFAULT MACRO
  8.   DEFT_#1 EQU #2
  9.   DB ' (default #2)',0D,0A
  10. #EM
  11.  
  12. DOC_MSG:
  13.  
  14. DB 0D,0A
  15. DB 'TCOLS V1.0  (C)1986 Eric Isaacson, 416 E. Univ.St., BMG IN 47401',0D,0A
  16. DB '  Permission to use granted only to registered A86 users.',0D,0A,0D,0A
  17.  
  18.   DB 'TCOLS converts single-column input into paged, multi-column output.',0D,0A
  19.   DB 'Usage: TCOLS <in >out #1 #2 #3 #4 #5          , where',0D,0A
  20.   DB '  #1 is the number of major columns you want each page split into',0D,0A
  21.   DB '  #2 is the number of lines to skip between each page'
  22.   DEFAULT  SKIPCT,6
  23.   DB '  #3 is the number of chars per line the printer is now set to'
  24.   DEFAULT  PWIDTH,80
  25.   DB '  #4 is the number of lines per page the printer is now set to'
  26.   DEFAULT  LPAGE,66
  27.   DB '  #5 is which line within the first page the printer is now at'
  28.   DEFAULT  LSTART,0
  29.   DB          0D,0A
  30.  
  31. DB 'Examples:',0D,0A
  32. DB 'TCOLS <MYPROG.XRF >PRN 4 6 96 88',0D,0A
  33. DB '   sends the file MYPROG.XRF to the printer, split into 4 columns, where'
  34. DB            0D,0A
  35. DB '   the printer is set to 96 characters a line, 8 lines per inch.',0D,0A
  36. DB 'TCOLS <NARROW.LST 6 0 80 23 22 | MORE',0D,0A
  37. DB '   provides 6-column screen-paged output.',0D,0A
  38. DB 'NOTE for readability, TCOLS will convert underscores to hyphens when the'
  39. DB            0D,0A
  40. DB '   lines per page is greater than 80.',0D,0A
  41.  
  42. DOC_LEN EQU $-DOC_MSG
  43.  
  44. DATA SEGMENT
  45.   ORG 01000
  46. BUF:
  47.              DB 04000 DUP (?)
  48. OUTBUF:
  49.              DB 02000 DUP (?)
  50. OUTBUF_LIM:
  51.              DB 0100 DUP (?)
  52.              DW ?             ; in case the line-overflow logic scans back
  53. SOURCE_BUF:
  54.              DB 04002 DUP (?)
  55.   WIDTH      DW ?   ; number of characters in an major output column
  56.   NCOLS      DB ?   ; number of major output columns on a page
  57.   LPAGE      DB ?   ; total number of lines on a page (including skipped)
  58.   PWIDTH     DW ?   ; total width in characters of a printed page
  59.   BUFEND     DW ?   ; pointer reached when a buffered page is complete
  60.   SKIPCT     DB ?   ; number of lines skipped between pages
  61.   LSTART     DB ?   ; number of first-page lines already output before program
  62.   THISPAGE   DB ?   ; number of printed lines on this page
  63. DATA ENDS
  64.  
  65. MAIN:
  66. TCOLS:
  67.   CALL SCAN_ARGS    ; scan the command arguments
  68.   CALL READ_SOURCE  ; read the first block of source text
  69.   JZ >L2            ; exit if there was no source text
  70. L1:                 ; loop here for each page of output
  71.   CALL GATHER_PAGE  ; input a page, and arrange in into columns
  72.   PUSHF             ; save Z-flag, to see if there is more input
  73.   CALL OUTPUT_PAGE  ; process the columns-buffer into final output
  74.   CALL OFLUSH       ; flush the output buffer
  75.   POPF              ; restore Z flag, is there more output?
  76.   JNZ L1            ; loop if there is more output
  77. L2:
  78.   JMP GOOD_EXIT     ; all done, go back to operating system
  79.  
  80.  
  81. ; CHECK_DIGIT sees if there is another command-tail argument pointed to by
  82. ;   SI.  If there is, it had better start with a decimal digit, or else we
  83. ;   abort the program.  We return NZ if there is an argument; Z if there are
  84. ;   no more arguments (terminator 0FF is seen).
  85.  
  86. CHECK_DIGIT:
  87.   PUSH AX           ; preserve register across call
  88. L1:                 ; loop here to skip over blanks and control chars
  89.   LODSB             ; load the next character
  90.   CMP AL,' '        ; is it a blank or control char?
  91.   JBE L1            ; loop if yes, to skip the character
  92.   DEC SI            ; retreat SI back to the first argument-char
  93.   CMP AL,0FF        ; is it the command-tail terminator?
  94.   JE >L2            ; return Z if yes
  95.   SUB AL,'0'        ; reduce first character to 0--9 range if digit
  96.   CMP AL,10         ; is the character a digit?
  97.   JAE >E1           ; abort the program if not; NZ is set if yes
  98. L2:
  99.   POP AX            ; restore clobbered register
  100.   RET
  101.  
  102. E1:                 ; improper command tail in program invocation
  103.   MOV DX,DOC_MSG    ; point to the documentation-message
  104.   MOV CX,DOC_LEN    ; load size of message
  105.   JMP ERROR_EXIT    ; educate the user about this program
  106.  
  107.  
  108. ; CLEAR_PAGE calculates the number of lines to be printed on the coming
  109. ;   page, at sets up a blank columns-buffer based on that number.  We return
  110. ;   with DI pointing just beyond the blanked buffer.  If the calculations
  111. ;   indicate something is wrong, we abort the program.
  112.  
  113. CLEAR_PAGE:
  114.   MOV AL,LPAGE      ; fetch the total number of lines on a page
  115.   SUB AL,SKIPCT     ; subtract the number of lines that we skip
  116.   JBE E1            ; abort if we skip more lines than there are
  117.   SUB AL,LSTART     ; also subtract any first-page lines already output
  118.   JBE E1            ; abort if that subtraction has exhausted the count
  119.   MOV THISPAGE,AL   ; store as the number of lines on this page
  120.   MOV LSTART,0      ; cancel first-page count for the subsequent pages
  121.   MOV CX,PWIDTH     ; load the number of characters per line
  122.   MUL CL            ; calculate the number of characters in the columns buffer
  123.   XCHG CX,AX        ; swap the count into CX for blanking
  124.   MOV DI,BUF        ; point DI to the start of the columns buffer
  125.   MOV AL,' '        ; we will fill the buffer with blanks
  126.   REP STOSB         ; buffer is filled, DI points beyond the buffer
  127.   RET
  128.  
  129.  
  130. ; SCAN_ARGS scans the decimal arguments in the command tail at DS:080. All the
  131. ;   appropriate page-size variables are set according to these argument values.
  132.  
  133. SCAN_ARGS:
  134.   MOV SI,080        ; point to the command-tail buffer in the PSP
  135.   LODSB             ; load the size of the command tail
  136.   CBW               ; extend the size AL to AX
  137.   XCHG BX,AX        ; swap the size into BX, for indexing
  138.   MOV B[BX+SI],0FF  ; mark the end of the command-tail with terminator 0FF
  139.   CALL CHECK_DIGIT  ; any there any arguments in the command tail?
  140.   JZ E1             ; abort the program if there are not
  141.   CALL SCAN_DECIMAL ; input the first argument-- number of major columns
  142.   MOV NCOLS,AL      ; store it
  143.   XCHG BX,AX        ; also swap NCOLS into BL for later calculations
  144.   MOV AL,DEFT_SKIPCT; load the default number of lines skipped
  145.   CALL SCAN_DECIMAL ; read the next argument if there is any
  146.   MOV SKIPCT,AL     ; store the number of lines to skip between pages
  147.   MOV AL,DEFT_PWIDTH; load the default page width
  148.   CALL SCAN_DECIMAL ; read the next argument if there is any
  149.   MOV CL,AL         ; store in CL for the moment
  150.   DIV BL            ; divide by major columns count, to get chars per column
  151.   SUB CL,AH         ; subtract remainder from page width, insures even multiple
  152.   MOV AH,0          ; zero out the remainder
  153.   MOV WIDTH,AX      ; store the number of characters in an output column
  154.   CMP AL,2          ; this width had better be at least 2 chars
  155.   JB E1             ; abort the program if not
  156.   MOV AL,CL         ; AX is now the characters-per-line
  157.   MOV PWIDTH,AX     ; store the characters-per-line printer is set to
  158.   MOV AX,CX         ; re-fetch the characters-per-line
  159.   ADD AX,BUF        ; calculate beyond-first-line, reached when buffer full
  160.   MOV BUFEND,AX     ; store the value for later use
  161.   MOV AL,DEFT_LPAGE ; load defualt lines-per-page
  162.   CALL SCAN_DECIMAL ; read the next argument, if there is any
  163.   MOV LPAGE,AL      ; store the total number of lines on a printed page
  164.   MOV AL,DEFT_LSTART; load the default first-line-position
  165.   CALL SCAN_DECIMAL ; read the next argument, if any
  166.   MOV LSTART,AL     ; store the starting first-page position
  167.   RET
  168.  
  169.  
  170. ; GATHER_PAGE takes input text at DS:SI, and formats it into columns on a
  171. ;   single page, at BUF.  RZ if the input file end (0FFH marker) was seen.
  172.  
  173. GATHER_PAGE:
  174.   CALL CLEAR_PAGE   ; blank-fill the columns-buffer
  175.   MOV BX,DI         ; point BX beyond the columns-buffer
  176.   MOV BP,DX,DI,BUF  ; start BP=column ptr  DX=line ptr  DI=char ptr
  177. L1:                 ; loop here for each column entry
  178.   CALL GATHER_LINE  ; copy an input line to a column entry
  179.   JZ RET            ; return if there is no more input
  180.   CALL NEXT_LINE    ; advance pointers to the next column entry
  181.   JB L1             ; loop if there is another entry on this page
  182.   OR AL,0FF         ; page complete: set NZ to signal more input
  183.   RET
  184.  
  185.  
  186. ; NEXT_LINE sets DI to the next line of this column in the page display buffer.
  187. ;   It moves to the top of the next column as necessary.  RAE if the page is
  188. ;   full.
  189.  
  190. NEXT_LINE:
  191.   ADD DX,PWIDTH     ; advance column-pointer to same column on the next line
  192.   CMP DX,BX         ; have we run off the end of the buffer?
  193.   JB >L1            ; skip if we have not
  194.   ADD BX,WIDTH      ; one column is complete-- advance end-buffer-limit
  195.   ADD BP,WIDTH      ; advance the column-pointer to the next column
  196.   MOV DX,BP         ; set the line-ptr to this new column
  197.   CMP BP,BUFEND     ; have the columns run off the right end of the page?
  198.   JAE RET           ; return AE if they have-- page is full of columns
  199. L1:
  200.   MOV DI,DX         ; set char-output pointer to the new line-ptr value
  201.   RET
  202.  
  203.  
  204. ; GATHER_LINE copies a line of text from SI to DI.  RZ if the input file end
  205. ;    is seen.
  206.  
  207. GATHER_LINE:
  208.   MOV CX,WIDTH      ; set up the limit for output in this column-entry
  209. L0:                 ; loop here for each character output to column-entry
  210.   LODSB             ; fetch a character from the input
  211.   CMP AL,020        ; is it a control-character?
  212.   JB >L1            ; jump if yes
  213.   CMP AL,0FE        ; is it a marker?
  214.   JAE >L2           ; jump if yes
  215.   CMP AL,'_'        ; is it an underscore?
  216.   JE >L5            ; jump if yes
  217. L6:
  218.   STOSB             ; store the character in columns-buffer
  219.   LOOP L0           ; loop to look at the next character
  220.   DEC SI,2          ; column-entry has overflowed: retreat input pointer
  221.   MOV B[SI],0FE     ; mark input buffer with line-overflow marker
  222.   ES MOV B[DI-1],' '; cancel the last byte output-- it will go to next line
  223.   RET
  224.  
  225. L1:                 ; control-character seen on the input line
  226.   CMP AL,0A         ; is it the line-terminator?
  227.   JNE L0            ; if not then ignore it
  228.   TEST AL,AL        ; set NZ to signal end-of-file was not seen
  229.   RET
  230.  
  231. L2:                 ; a marker was seen in the input stream
  232.   JE >L4            ; jump if it was the line-overflow marker
  233.   CALL READ_SOURCE  ; it was the end-of-file marker: read more input
  234.   JNE L0            ; loop for more reading if there was more input
  235.   RET               ; input exhausted-- return Z to caller
  236.  
  237. L4:                 ; a line-overflow marker was seen in the input-stream
  238.   PUSH BX,CX,DI     ; preserve registers across indentation
  239.   MOV AL,0A         ; we will look for the linefeed for this line
  240.   MOV DI,SI         ; transfer source pointer to DI for linefeed-search
  241.   REPNE SCASB       ; find where the linefeed is
  242.   CMP B[DI-2],0D    ; was there also a carriage return?
  243.   IF E INC CX       ; if yes then don't count it
  244.   MOV BX,CX         ; preserve the count for reducing the stacked CX value
  245.   POP DI            ; restore columnar-output pointer
  246.   MOV AL,' '        ; load blank-- we are indenting to right-justify overflow
  247.   REP STOSB         ; now the overflow is indented
  248.   POP CX            ; restore original count
  249.   SUB CX,BX         ; reduce count by the number of blanks we indented
  250.   POP BX            ; restore clobbered register
  251.   JMP L0
  252.  
  253. L5:                 ; an underscore was seen in the input stream
  254.   CMP LPAGE,80      ; are we squeezing those lines tightly on a page?
  255.   JBE L6            ; jump if not, no need to convert
  256.   MOV AL,'-'        ; tight squeeze: change to hyphen for readability
  257.   JMP L6            ; re-join the main storage-loop
  258.  
  259.  
  260. ; SCAN_DECIMAL sets AX to the value of the next decimal-number argument
  261. ;   pointed to by SI, and advances SI beyond the argument.  If there are
  262. ;   no more arguments, we retain the input value AL as our return-value.
  263.  
  264. SCAN_DECIMAL:
  265.   MOV AH,0          ; high-byte of default value is always zero
  266.   CALL CHECK_DIGIT  ; is there an argument there?
  267.   JZ RET            ; return the default value if there was not
  268.   PUSH BX,DX        ; preserve registers across call
  269.   SUB BX,BX         ; initialize BX, we will accumulate value there
  270. L1:                 ; loop here for each digit of the number
  271.   LODSB             ; load the next digit
  272.   SUB AL,'0'        ; reduce the digit to a binary value
  273.   MOV DX,10         ; load multiplicand, it is also the digit-limit value
  274.   CMP AL,DL         ; is it in fact another decimal digit?
  275.   JAE >L2           ; jump if not to exit this procedure
  276.   CBW               ; extend value AL to AX
  277.   XCHG AX,BX        ; swap previous accumulation into AX, new digit into BX
  278.   MUL DX            ; multiply the previous accumulation by 10
  279.   ADD BX,AX         ; add into the new digit value
  280.   JMP L1            ; loop to accumulate another digit
  281.  
  282. L2:                 ; non-digit was seen
  283.   DEC SI            ; retreat back to the non-digit
  284.   XCHG AX,BX        ; swap the accumulated value into AX
  285.   POP DX,BX         ; restore clobbered registers
  286.   RET
  287.  
  288.  
  289. ; OUTPUT_PAGE transforms the column-buffer output into final-forn output,
  290. ;   with CRLFs inserted and trailing blanks removed.
  291.  
  292. OUTPUT_PAGE:
  293.   PUSH CX,DX,SI     ; preserve registers across call
  294.   MOV DI,OUTBUF     ; initialize the output pointer
  295.   MOV DX,PWIDTH     ; DX holds the count of each columnized line
  296.   MOV SI,BUF        ; initialize the column-buffer source pointer
  297.   MOV CL,THISPAGE   ; load the lines count into CL
  298.   MOV CH,0          ; extend the CL-count to CX
  299. L1:                 ; loop here to output each line
  300.   CALL OUTPUT_LINE  ; output the line
  301.   ADD SI,DX         ; advance the source pointer to the next columnized line
  302.   LOOP L1           ; loop to output the next line
  303.   POP SI,DX,CX      ; restore clobbered registers
  304.   MOV AH,SKIPCT     ; load the count of lines to skip
  305.   TEST AH,AH        ; are there any lines to skip?
  306.   JZ RET            ; return if not
  307. L2:                 ; loop here for each skipped line between pages
  308.   CALL CRLF_OUT     ; output a blank line
  309.   DEC AH            ; count down lines
  310.   JNZ L2            ; loop to output the next blank line
  311.   RET
  312.  
  313.  
  314. ; OUTPUT_LINE transforms a single line of columnized output into its final
  315. ;   form, with trailing blanks removed and a CRLF appended to the end of the
  316. ;   line.
  317.  
  318. OUTPUT_LINE:
  319.   PUSH CX,SI        ; preserve registers across call
  320.   MOV CX,DX         ; fetch the count of columnized-line bytes
  321.   PUSH DI           ; preserve the output pointer
  322.   MOV DI,SI         ; load DI with source pointer, for scanning
  323.   ADD DI,CX         ; advance DI beyond this source line
  324.   DEC DI            ; retreat to the last byte of this source line
  325.   MOV AL,' '        ; load blank, to scan for trailing blanks
  326.   STD               ; scanning will be backwards
  327.   REPE SCASB        ; scan through the trailing blanks
  328.   CLD               ; restore forward scanning
  329.   IF NE INC CX      ; do not include the last byte if it was a non-blank
  330.   POP DI            ; restore output pointer
  331.   REP MOVSB         ; CX was decremented by # of trailing blanks; now move line
  332.   POP SI,CX         ; restore clobbered registers
  333. CRLF_OUT:           ; output a CRLF to the end of a line
  334.   PUSH AX           ; preserve AX across call
  335.   MOV AX,0A0D       ; load CRLF, backwards according to 8086 storage conventions
  336.   STOSW             ; output the CRLF
  337.   POP AX            ; restore clobbered register
  338. CHECK_FLUSH:        ; check to see if we need to flush the output buffer
  339.   CMP DI,OUTBUF_LIM ; have we reached the buffer limit?
  340.   JB RET            ; do nothing if we have not reached the limit
  341. OFLUSH:             ; flush the output buffer
  342.   PUSH AX,BX,CX,DX  ; save registers across call
  343.   MOV BX,1          ; handle-number for standard output is 1
  344.   MOV DX,OUTBUF     ; point DX to the output buffer
  345.   MOV CX,DI         ; point CX beyond the bytes we have output
  346.   SUB CX,DX         ; calculate the number of bytes output
  347.   CALL MWRITE       ; send those bytes to standard output
  348.   MOV DI,DX         ; reset the output-pointer to the start of the buffer
  349.   POP DX,CX,BX,AX   ; restore clobbered registers
  350.   RET
  351.  
  352.  
  353. ; READ_SOURCE reads from standard input to SOURCE_BUF, and marks the end
  354. ;    of the received text with 0FF.  Return with SI pointing to SOURCE_BUF.
  355. ;    RZ if there was no more input.
  356.  
  357. READ_SOURCE:
  358.   PUSH AX,BX,CX,DX  ; preserve registers across call
  359.   SUB BX,BX         ; handle-value for standard input is zero
  360.   MOV DX,SOURCE_BUF ; point to the destination for our source-read
  361.   MOV CX,04000      ; load the size limit for the read
  362.   CALL MREAD        ; read from standard input into the source buffer
  363.   XCHG BX,AX        ; swap the actual number of bytes read into BX
  364.   MOV SI,DX         ; point SI to the bytes that were read
  365.   MOV B[BX+SI],0FF  ; terminate the bytes with an 0FF marker
  366.   TEST BX,BX        ; set Z if there were no bytes read
  367.   POP DX,CX,BX,AX   ; restore clobbered registers
  368.   RET
  369.  
  370.  
  371. ; MREAD reads from the open-file numbered BX, to the CX bytes at DX.  Return
  372. ;   with AX set to the number of bytes actually read.
  373.  
  374. MREAD:
  375.   MOV AH,03FH       ; MSDOS function number for READ
  376. MSDOS:
  377.   INT 021H          ; all MSDOS calls go through this interrupt
  378.   RET
  379.  
  380.  
  381. ; MWRITE writes CX bytes from DX to the open-file numbered BX.  Return Carry if
  382. ;   the write failed, with AX set to an error number.
  383.  
  384. MWRITE:
  385.   MOV AH,040H       ; MSDOS function number for WRITE
  386.   JMP MSDOS         ; jump to call the operating system
  387.  
  388.  
  389. ; EXIT exits the program back to the invoking process, with a status of AL.
  390.  
  391. GOOD_EXIT:
  392.   MOV AL,0          ; zero status means success
  393. EXIT:
  394.   MOV AH,04CH       ; MSDOS function number for EXIT
  395.   JMP MSDOS         ; jump to call the operating system
  396.  
  397.  
  398. ; ERROR_EXIT writes to the error-console the message of CX bytes at DS:DS;
  399. ;   then exits the program with a status of 1.
  400.  
  401. ERROR_EXIT:
  402.   MOV BX,2          ; handle-value for error-console is 2
  403.   CALL MWRITE       ; write message to the error-console
  404.   MOV AL,1          ; 1-code means failure
  405.   JMP EXIT          ; go back to operating system
  406.  
  407.