Category : Files from Magazines
Archive   : VOL8N7.ZIP
Filename : LASERLST.ASM
TITLE "LASERLST - List file to HP LaserJet"
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
ORG 100H
START: JMP MAIN ; go to start of program
HEADER_MSG DB "LaserLst 1.0 (c) 1989 Ziff Communications Co.", 0DH,0AH
DB "PC Magazine",254," Michael Holmes & Bob Flanders",0DH,0AH
CRLF DB 0DH,0AH
DOLLAR DB "$"
; ---------------------------------------------------------------------------
; Initialized work areas
; ---------------------------------------------------------------------------
ARG1 DW 0 ; addr of first argument
ARG2 DW 0 ; addr of second argument
PHANDLE DW 4 ; printer handle (stdprn)
TABCOL DW 8 ; tab width
NEWPAGE DB 0 ; new page requested flag
HPSTATE DB 0 ; printer state (1 = initialized)
TITLE_ DB 1BH,"&dD" ; title line for heading
TITLE_DTE DB "mm/dd/yyyy " ; ..and display
TITLE_TME DB "hh:mm Filename: "
TITLE_FLE DB 19 dup(32)
TITLE_DSP EQU $-TITLE_DTE
DB " Page"
TITLE_PGE DB "xxxx",0dh,0ah,0ah
DB 1BH,"&d@"
TITLE_LEN EQU $-TITLE_ ; length of title
;----------------------------------------------------------------------
; DTA structure for DOS "find matching" call
;----------------------------------------------------------------------
DTA EQU 80H ; dta offset
DTA_ATTR EQU BYTE PTR DTA+21 ; file attribute
DTA_TIME EQU WORD PTR DTA_ATTR+1 ; file time
DTA_DATE EQU WORD PTR DTA_TIME+2 ; file date
DTA_LSIZ EQU WORD PTR DTA_DATE+2 ; file lsw of size
DTA_HSIZ EQU WORD PTR DTA_LSIZ+2 ; file msw of size
DTA_NAME EQU BYTE PTR DTA_HSIZ+2 ; file name of file
DTA_LEN EQU DTA_NAME+15-DTA ; length of dta find entry
;----------------------------------------------------------------------
; Messages to user
;----------------------------------------------------------------------
FILENF DB "File not found.",0DH,0AH,"$"
PRTOERR DB "Could not open output file.",0DH,0AH,"$"
FORMAT DB 0DH,0AH,"SYNTAX:",09H,"LASERLST [d:][path]filename[.ext] "
DB "[outfile] [/Tn]",0DH, 0AH, 0AH
DB " ofile defaults to LPT1:",0DH,0AH
DB " n is tab width (16 max)",0DH,0AH,"$"
;----------------------------------------------------------------------
; HP control strings
;----------------------------------------------------------------------
STRING1 DB 1BH,"E",1BH,"&l1O",1BH,"(s17H",1BH,"&l5.14C"
DB 1BH,"&l71F",1BH,"&l6E",1BH,"(s-3B"
DB 1BH,"&a0R",1BH,"&a85M",1BH,"&a5L",0DH
STRING1_LEN EQU $-STRING1
STRING2 DB 0ch,1BH,"E"
STRING2_LEN EQU $-STRING2
STRING3 DB 1BH,"&a0R",1BH,"&a90M",1BH,"&a88L",0DH,0AH,0DH,0AH
STRING3_LEN EQU $-STRING3
STRING4 DB "|", 0DH,0AH
STRING5 DB 1BH,"&a0R",1BH,"&a171M",1BH,"&a91L",0DH
STRING5_LEN EQU $-STRING5
STRING6 DB 0CH,1BH,"&a0R",1BH,"&a85M",1BH,"&a5L",0DH
STRING6_LEN EQU $-STRING6
; ---------------------------------------------------------------------------
; MAIN - Mainline of program
; ---------------------------------------------------------------------------
MAIN PROC ; start of program
CALL INIT ; initialize program
MAIN10: CALL OPEN ; open the input file
JC MAIN80 ; if we can't .. try next
MAIN20: CALL READ ; read a block
CALL PRINT ; print the line
OR SI, SI ; q. end of file?
JNZ MAIN20 ; a. no .. get next line
MOV BX, FHANDLE ; bx = input file handle
MOV AH, 3EH ; ah = close file
INT 21H ; .. ask DOS to do it
MAIN80: MOV AH, 4FH ; ah = find next file
INT 21H ; q. file found?
JNC MAIN10 ; a. yes .. continue
MOV DX, OFFSET DOLLAR ; dx -> null message
CALL DIE ; .. say goodnight
MAIN ENDP
; ---------------------------------------------------------------------------
; INIT - Handle initialization
; ---------------------------------------------------------------------------
INIT PROC
CLD ; assure ascending
MOV AH, 19H ; ah = get current drive
INT 21H ; al = current drive
MOV EDRV, AL ; save entry drive
MOV SI, OFFSET EDIR ; si -> current directory area
MOV BYTE PTR [SI], '\' ; .. start with backslash
INC SI ; si -> next byte
XOR DL, DL ; dl = default drive
MOV AH, 47H ; ah = get current dir
INT 21H ; .. save in area
MOV DX, OFFSET HEADER_MSG ; dx -> header message
MOV AH, 9 ; ah = print ascii$ message
INT 21H ; .. ask DOS to do it
CALL PARMS ; check parameters
CMP ARG2, 0 ; q. arg2 specified?
JE INIT20 ; a. yes .. skip open
MOV DX, ARG2 ; dx -> arg2 value
MOV AH, 3CH ; ah = create file
XOR CX, CX ; .. cx = file attributes
INT 21H ; q. open the file ok?
JNC INIT10 ; a. yes .. continue
MOV DX, OFFSET PRTOERR ; dx -> open error message
CALL DIE ; .. you're dead, Jim.
INIT10: MOV PHANDLE, AX ; save printer handle
INIT20: CALL DFLPATH ; set up dir & drive
MOV AH, 4EH ; ah = find first
MOV DX, OFFSET FILENAME ; dx -> file to find
XOR CX, CX ; cx = search attribute
INT 21H ; q. find first file ok?
JNC INIT90 ; a. yes .. continue
MOV DX, OFFSET FILENF ; dx -> file not found
CALL DIE ; .. gasp you're final breath
INIT90: RET ; return to caller
INIT ENDP
; ---------------------------------------------------------------------------
; OPEN - Opens the next file to process and update title line
; Exit: Carry indicates file would not open.
; ---------------------------------------------------------------------------
OPEN PROC
MOV DX, OFFSET DTA_NAME ; dx -> file name
MOV AX, 3D00H ; al = open for read
INT 21H ; .. ask DOS to do it.
JNC OPEN05 ; ok .. continue
RET ; else .. return if error
OPEN05: MOV FHANDLE, AX ; save file handle
MOV LASTBUFF, 0 ; show last buffer not read
MOV SI, OFFSET DTA_NAME ; si -> file name
MOV DI, OFFSET TITLE_FLE ; di -> file name area
PUSH DI ; .. save it
MOV AL, ' ' ; al = blank
MOV CX, 15 ; .. amount to blank
REP STOSB ; .. clear the file name
POP DI ; .. restore output pointer
OPEN10: LODSB ; al = byte from file name
OR AL, AL ; q. end of name?
JZ OPEN20 ; a. yes .. end loop
STOSB ; store a byte
JMP OPEN10 ; .. and move next
OPEN20: MOV BL, '0' ; fill value
MOV AX, DS:DTA_DATE ; ax = date field
PUSH AX ; save for later
PUSH AX ; .. again
MOV CL, 5 ; cl = shift value
SHR AX, CL ; .. mm to lower bits
AND AX, 0FH ; .. upper bits off
MOV CX, 2 ; cx = number of characters
MOV DI, OFFSET TITLE_DTE ; di -> mm
CALL ITOA ; .. move in mm
POP AX ; ax = date
AND AX, 1FH ; al = dd
MOV CX, 2 ; cx = number of chars
MOV DI, OFFSET TITLE_DTE+3 ; di -> dd
CALL ITOA ; .. move into dd
POP AX ; ax = date
MOV CL, 9 ; cl = shift amount
SHR AX, CL ; .. ax = yy
ADD AX, 1980 ; ax = year
MOV CX, 4 ; cx = length of output
MOV DI, OFFSET TITLE_DTE+6 ; di -> yyyy
CALL ITOA ; .. move into yyyy
MOV AX, DS:DTA_TIME ; ax = time
PUSH AX ; .. save for later
MOV CL, 11 ; cl = shift value
SHR AX, CL ; .. ax = hh
MOV CX, 2 ; cx = length of output
MOV DI, OFFSET TITLE_TME ; di -> hh
CALL ITOA ; .. move into hh
POP AX ; ax = time
MOV CL, 5 ; cl = shift value
SHR AX, CL ; ax = mm
AND AX, 3FH ; .. upper bits off
MOV CX, 2 ; cx = length of output
MOV DI, OFFSET TITLE_TME+3 ; di -> mm
CALL ITOA ; .. move into mm
MOV AH, 40H ; ah = write to device
MOV BX, 1 ; bx = stdout device
MOV CX, TITLE_DSP ; cx = length to display
MOV DX, OFFSET TITLE_DTE ; dx -> part of header line
INT 21H ; issue dos call
MOV AH, 40H ; ah = write to device
MOV BX, 1 ; bx = stdout device
MOV CX, 2 ; cx = length to display
MOV DX, OFFSET CRLF ; dx ->
INT 21H ; issue dos call
CLC ; show all went ok
OPEN90: RET
OPEN ENDP
; ---------------------------------------------------------------------------
; READ - Read the next buffer full
; Exit: si -> next line, 0 if eof, cx = length of line
; ---------------------------------------------------------------------------
READ PROC
PUSH AX ; save registers
PUSH BX
PUSH DX
MOV SI, 0 ; si -> nothing
CMP LASTBUFF, 1 ; q. last buffer have 1ah?
JE READ90 ; a. yes .. end it all
MOV AH, 03FH ; ah = read file
MOV BX, FHANDLE ; bx = handle of file to read
MOV CX, BUFLEN ; cx = amount to read
MOV DX, OFFSET BUFFER ; dx -> buffer
INT 21H ; read, please
JC READ90 ; error .. return eof
OR AX, AX ; q. any read?
JZ READ90 ; a. yes .. return with something
MOV SI, OFFSET BUFFER ; si -> start of line
MOV CX, AX ; cx = nbr of characters read
MOV BX, AX ; .. and bx
MOV DI, SI ; di -> chars read
MOV AL, 1AH ; al = EOF indicator
REPNE SCASB ; q. eof found?
JNE READ80 ; a. no .. continue
INC CX ; .. increment count
NEG CX ; .. and negate it
MOV LASTBUFF, 1 ; .. and set lastbuff flag
READ80: ADD CX, BX ; q. any bytes this buffer?
JNZ READ90 ; a. yes .. continue
MOV SI, 0 ; a. else .. show end of file.
READ90: POP DX ; restore registers
POP BX ;
POP AX ;
RET ; ..and return to caller
READ ENDP
; ---------------------------------------------------------------------------
; PRINT - Print requested data
; Entry: si -> string to print or 0, cx = length of input string
; ---------------------------------------------------------------------------
PRINT PROC
OR SI, SI ; q. end of file call?
JNE PRINT10 ; a. no .. continue
CALL EPILOGUE ; put out trailing characters
RET ; ..and return to caller
PRINT10: PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
PUSH SI
CMP HPSTATE, 0 ; q. initial state?
JNE PRINT20 ; a. no .. continue
CALL PROLOGUE ; send out initial string
PRINT20: CALL EXPAND ; handle tab expansion
MOV DX, SI ; dx -> start of new buffer
MOV BX, CX ; bx = nbr of chars in buffer
XOR CX, CX ; cx = nbr of chars to print
MOV AH, USEDLEN ; ah = printed chars in this line
PRINT30: LODSB ; al = first character
CMP AL, 0CH ; q. formfeed?
JNE PRINT40 ; a. yes .. process it
CALL LINEPRT ; print the line
CALL PAGEBREAK ; do a page break
XOR AH, AH ; ah = nbr of columns used
DEC BX ; bx = decrement remaining count
INC DX ; dx -> next printable character
JMP PRINT70 ; ..and continue w/common code
PRINT40: CMP AL, 0AH ; q. linefeed?
JNE PRINT50 ; a. no .. continue
DEC BX ; bx = remaining chars in buffer
INC CX ; cx = nbr of characters to print
CALL LINEPRT ; print the line
XOR AH, AH ; ah = nbr of columns used
CALL BUMPLINE ; increment/test line counter
JMP PRINT70 ; ..and continue w/common code
PRINT50: CMP AL, 0DH ; q. carriage return?
JNE PRINT55 ; a. no .. continue
JMP SHORT PRINT57 ; ..and continue w/common code
PRINT55: CMP AL, 08H ; q. BackSpace?
JNE PRINT60 ; a. no .. continue
DEC AH ; move back a column
JNS PRINT65 ; .. but not beyond first
PRINT57: XOR AH, AH ; ah = start of line
JMP SHORT PRINT65 ; .. and continue
PRINT60: INC AH ; increment nbr of columns used
PRINT65: INC CX ; ..and nbr of chars to print
DEC BX ; decrement remaining char count
CMP AH, 81 ; q. reached end of line?
JL PRINT70 ; a. no .. continue
CALL LINEPRT ; print upto page width
PUSH CX ; save registers
PUSH DX ;
MOV DX, OFFSET CRLF ; dx ->
MOV CX, 2 ; cx = string length
CALL WRITE ; print the crlf to do line wrapping
POP DX ; restore registers
POP CX ;
XOR AH, AH ; ah = nbr of columns used
CALL BUMPLINE ; increment/test line counter
PRINT70: OR BX, BX ; q. anything left to check?
JNZ PRINT30 ; a. yes .. loop till done
CALL LINEPRT ; print the line
MOV USEDLEN, AH ; save nbr of columns used
POP SI ; restore registers
POP DX ;
POP CX ;
POP BX ;
POP AX ;
RET ; ..and return to caller
PRINT ENDP
; ---------------------------------------------------------------------------
; DIE - Display an error message and return to DOS
; Entry: dx -> error message ended in dollar sign.
; ---------------------------------------------------------------------------
DIE PROC
MOV AH, 9 ; ah = print string
INT 21H ; .. call dos to print error
MOV DL, EDRV ; dl = drive to select
MOV AH, 0EH ; ah = select drive
INT 21H ; .. select the drive
MOV DX, OFFSET EDIR ; dx -> directory
MOV AH, 3BH ; ah = CHDIR request
INT 21H ; .. ask DOS to do it
MOV AX, 4C00H ; ax = exit
INT 21H ; .. terminate routine
DIE ENDP
; ---------------------------------------------------------------------------
; PARMS - Parses the command line
; ---------------------------------------------------------------------------
PARMS PROC
CALL UPCASE ; upper case the parm area
MOV SI, 81H ; si -> parms area
PARMS10: LODSB ; get parameter character
CMP AL, '/' ; q. option?
JE PARMS80 ; a. yes .. check option
CMP AL, 0DH ; q. end of line?
JE PARMS50 ; a. yes .. exit
CMP AL, ' ' ; q. blank?
JNA PARMS10 ; a. yes .. skip
CALL ARG ; set the argument
JC PARMSERR ; .. die on an error
PARMS30: LODSB ; get next character
CMP AL, 0DH ; q. end of line?
JE PARMS50 ; a. yes .. process
CMP AL, '/' ; q. start of option?
JE PARMS80 ; a. yes .. process
CMP AL, ' ' ; q. end of PARMS?
JA PARMS30 ; a. no .. next char
CALL ENDPARM ; terminate parm string
JMP PARMS10 ; .. look for next
PARMS50: CALL ENDPARM ; terminate parm string
CMP ARG1, 0 ; q. PARMS 1 available?
JE PARMSERR ; a. no .. error
RET ; .. else .. return to caller
PARMS80: CALL ENDPARM ; terminate parm string
LODSB ; al = option character
CMP AL, 'T' ; q. Tab width?
JNE PARMSERR ; a. no .. error in option
CALL ATOI ; ax = tab width
CMP AX, 16 ; q. request greater than max?
JG PARMS10 ; a. yes .. unreasonable
MOV TABCOL, AX ; .. save tab width
JMP PARMS10 ; .. continue scan
PARMSERR: MOV DX, OFFSET FORMAT ; dx -> format message
CALL DIE ; abort
PARMS ENDP
; ---------------------------------------------------------------------------
; ARG - Setup pointers to the command line arguments
; Entry: si -> second character in argument.
; Exit: ARG1 or ARG2 pointers filled in.
; Carry set if more than 2 arguments detected.
; ---------------------------------------------------------------------------
ARG PROC
LEA BX, [SI-1] ; bx -> argument
CMP ARG1, 0 ; q. arg1 filled in?
JNE ARG10 ; a. yes .. check 2
MOV ARG1, BX ; save arg1 pointer
JMP SHORT ARG90 ; .. exit ok!
ARG10: CMP ARG2, 0 ; q. arg2 filled in?
JE ARG20 ; a. no .. fill it in
STC ; else .. error
RET ; .. and return to caller
ARG20: MOV ARG2, BX ; save arg2 pointer
ARG90: CLC ; show no error
RET ; return to caller
ARG ENDP
; ---------------------------------------------------------------------------
; ENDPARM - Handle parameters which end in a colon
; Entry: si -> first character past end of parameter
; ---------------------------------------------------------------------------
ENDPARM PROC
CMP BYTE PTR [SI-2], ':' ; q. argument end in a colon?
JNE ENDPARM10 ; a. no .. continue
MOV BYTE PTR [SI-2], 0 ; ..it doesn't any more
RET ; ..and return
ENDPARM10: MOV BYTE PTR [SI-1], 0 ; end the parameter
RET ; ..and return
ENDPARM ENDP
; ---------------------------------------------------------------------------
; DFLPATH - Setup the default drive and path
; ---------------------------------------------------------------------------
DFLPATH PROC
MOV DI, ARG1 ; di -> first arg
DFLPATH10: CMP BYTE PTR [DI+1], ':' ; q. drive specified?
JNE DFLPATH20 ; a. no .. use current drive
MOV DL, [DI] ; dl = drive to use
SUB DL, 'A' ; get requested drive number
MOV AH, 0EH ; set requested drive
INT 21H ; .. via dos
ADD DI, 2 ; di -> next part
DFLPATH20: PUSH DI ; save pointer
MOV BX, DI ; bx -> start of area
XOR AL, AL ; al = search for null
MOV CX, 128 ; very max to search
CLD
REPNE SCASB ; find end of arg
LEA SI, [DI-1] ; si -> nul
MOV CX, 0 ; cx = # chars to move
CMP SI, BX ; q. any file name
JE DFLPATH80 ; a. no .. error
DFLPATH30: DEC SI ; si -> prev char
CMP BYTE PTR [SI], '\' ; q. dir?
JE DFLPATH35 ; a. yes .. end of file name.
INC CX ; cx = char count
CMP SI, BX ; q. done?
JE DFLPATH37 ; a. yes .. move file name
JMP DFLPATH30 ; .. continue
DFLPATH35: INC SI ; si -> start of file name
DFLPATH37: OR CX, CX ; q. file name spec'd?
JZ DFLPATH80 ; a. no .. error
CMP CX, 12 ; q. too long?
JA DFLPATH85 ; a. yes .. error
PUSH SI ; save start pointer
MOV DI, OFFSET FILENAME ; di -> file name
INC CX ; .. assure nul moves too
REP MOVSB ; .. move in the file name
POP SI ; restore start pointer
POP DI ; .. and dir pointer
CMP SI, BX ; q. at start of parm?
JE DFLPATH90 ; a. yes .. return
INC BX ; bx -> next char
CMP SI, BX ; q. root only given?
JE DFLPATH40 ; a. yes .. continue
DEC SI ; si -> last \
DFLPATH40: MOV BYTE PTR [SI], 0 ; make dir ASCIIZ
DFLPATH50: MOV DX, DI ; dx -> directory
MOV AH, 3BH ; ah = CHDIR opcode
INT 21H ; .. change directory
JNC DFLPATH90 ; if ok .. continue
JMP SHORT DFLPATH85 ; dx -> baddir request
DFLPATH80: MOV DX, OFFSET FORMAT ; dx -> no file specified
CALL DIE
DFLPATH85: MOV DX, OFFSET FILENF ; dx -> invalid filename spec'd
CALL DIE
DFLPATH90: RET ; return to caller
DFLPATH ENDP
; ---------------------------------------------------------------------------
; EXPAND - Handle tab expansion
; Entry:
; si -> line read from file ended by a linefeed
; cx = length of line
; Exit:
; si -> reformatted line in output buffer
; cx = new line length
; ---------------------------------------------------------------------------
EXPAND PROC
PUSH AX ; save registers
PUSH BX
PUSH DX
PUSH DI
MOV DI, OFFSET OBUFF ; di -> start of output buffer
EXPAND10: LODSB ; al = character from input line
CMP AL, 09H ; q. tab character?
JNE EXPAND30 ; a. no .. continue processing
MOV AX, CURCOL ; ax = current column
DEC AX ; ax = column offset
XOR DX, DX ; dx:ax = current column
MOV BX, TABCOL ; bx = nbr of columns per tab
IDIV BX ; dx = space within tab stop
SUB BX, DX ; bx = spaces left in tab stop
MOV AL, 20H ; al = space char to padding string
EXPAND20: STOSB ; put a blank in output buffer
INC CURCOL ; bump current column nbr
DEC BX ; q. done yet?
JNZ EXPAND20 ; a. no .. keep looping
JMP SHORT EXPAND50 ; a. yes .. get next character
EXPAND30: CMP AL, 0DH ; q. carriage return?
JE EXPAND33 ; a. yes .. reset column
CMP AL, 0CH ; q. form feed?
JNE EXPAND35 ; a. no .. continue processing
EXPAND33: MOV CURCOL, 0 ; setup for start of new line
JMP SHORT EXPAND40 ; .. continue
EXPAND35: CMP AL, 08H ; q. backspace?
JNE EXPAND40 ; a. no .. continue
STOSB ; save the BS
DEC CURCOL ; .. move back a space
JNZ EXPAND50 ; .. get next character
MOV CURCOL, 1 ; init current column
JMP SHORT EXPAND50 ; .. continue
EXPAND40: STOSB ; move character to output line
CMP AL, 0AH ; q. line feed?
JE EXPAND50 ; a. yes .. don't count it
INC CURCOL ; bump current column nbr
EXPAND50: LOOP EXPAND10 ; ..loop till input exhausted
MOV SI, OFFSET OBUFF ; si -> start of output buffer
MOV CX, DI ; di -> just past last char of output
SUB CX, SI ; cx = nbr of characters in output
POP DI ; restore registers
POP DX
POP BX
POP AX
RET ; ..and return to caller
EXPAND ENDP
; ---------------------------------------------------------------------------
; LINEPRT - Handle printing a line
; Entry:
; dx -> start of line
; cx = nbr of characters to print
; Exit:
; dx -> start of next line
; cx = 0
; ---------------------------------------------------------------------------
LINEPRT PROC
JCXZ LINEPRT90 ; if nothing to print.. return
CMP NEWPAGE, 1 ; q. need a new page?
JNE LINEPRT10 ; a. no .. continue
CALL PAGEBREAK ; else .. do a pagebreak
LINEPRT10: CALL WRITE ; write the line
ADD DX, CX ; dx -> start of next line
XOR CX, CX ; cx = nbr of characters to print
LINEPRT90: RET
LINEPRT ENDP
; ---------------------------------------------------------------------------
; MKTITLE - Make the title line be ready to print
; Exit:
; dx -> title line
; cx = title line length
; ---------------------------------------------------------------------------
MKTITLE PROC
PUSH AX ; save registers
PUSH BX
PUSH DI
MOV AX, PAGENO ; ax = page number
MOV CX, 4 ; cx = length of output
MOV BL, ' ' ; bl = fill char (blank)
MOV DI, OFFSET TITLE_PGE ; di -> output area
CALL ITOA ; .. fill in page number
MOV DX, OFFSET TITLE_ ; dx -> title
MOV CX, TITLE_LEN ; cx = length of title
CALL WRITE ; print title line
POP DI ; restore registers
POP BX
POP AX
RET ; return to caller
MKTITLE ENDP
; ---------------------------------------------------------------------------
; PAGEBREAK - Handle page overflow condition
; ---------------------------------------------------------------------------
PAGEBREAK PROC
PUSH BX ; save registers
PUSH CX
PUSH DX
TEST PAGENO, 1 ; q. finishing up with left page?
JZ PAGEBRK20 ; a. no .. do other page
MOV DX, OFFSET STRING3 ; dx -> get to right page string
MOV CX, STRING3_LEN ; cx = length
CALL WRITE ; write out 1st part of string
MOV BX, 66 ; bx = loop count
MOV CX, 3 ; cx = nbr of chars to print
MOV DX, OFFSET STRING4 ; dx -> vertical bar string
PAGEBRK10: CALL WRITE ; write on line of vertical bars
DEC BX ; q. done yet?
JNZ PAGEBRK10 ; a. no .. keep looping
MOV DX, OFFSET STRING5 ; dx -> string to finish up
MOV CX, STRING5_LEN ; cx = length
JMP PAGEBRK30 ; ..and continue w/common code
PAGEBRK20: MOV DX, OFFSET STRING6 ; dx -> get to left page string
MOV CX, STRING6_LEN ; cx = length
PAGEBRK30: CALL WRITE ; print the init line
MOV LINECNT, 1 ; reset line counter
MOV NEWPAGE, 0 ; clear new page request flag
INC PAGENO ; ..and bump page number
CALL MKTITLE ; print title line
POP DX ; restore registers
POP CX ;
POP BX ;
RET ; ..and return to caller
PAGEBREAK ENDP
; ---------------------------------------------------------------------------
; BUMPLINE - Increment line counter and test for overflow
; ---------------------------------------------------------------------------
BUMPLINE PROC
INC LINECNT ; increment line counter
CMP LINECNT, 66 ; q. reached max lines/page?
JLE BUMPLINE90 ; a. no .. continue
MOV NEWPAGE, 1 ; else .. show we'll need a new one
BUMPLINE90: RET ; ..then return to caller
BUMPLINE ENDP
; ---------------------------------------------------------------------------
; PROLOGUE - Put out laserjet initialization string
; ---------------------------------------------------------------------------
PROLOGUE PROC
PUSH CX ; save registers
PUSH DX
MOV DX, OFFSET STRING1 ; dx -> initialization string
MOV CX, STRING1_LEN ; cx = length
CALL WRITE ; print the init line
MOV PAGENO, 1 ; setup page number
MOV CURCOL, 1 ; ..and current column number
MOV LINECNT, 1 ; ..and line counter
MOV HPSTATE, 1 ; ..show in left page
MOV USEDLEN, 0 ; ..clear column position
CALL MKTITLE ; print title line
POP DX ; restore registers
POP CX ;
RET ; ..and return to caller
PROLOGUE ENDP
; ---------------------------------------------------------------------------
; EPILOGUE - Put out laserjet finish up string
; ---------------------------------------------------------------------------
EPILOGUE PROC
PUSH CX ; save registers
PUSH DX
MOV DX, OFFSET STRING2 ; dx -> termination string
MOV CX, STRING2_LEN ; cx = length
CALL WRITE ; print the init line
MOV HPSTATE, 0 ; show back to initialization state
POP DX ; restore registers
POP CX ;
RET ; ..and return to caller
EPILOGUE ENDP
; ---------------------------------------------------------------------------
; WRITE - Send a string to the printer
; Entry:
; dx -> string to write
; cx = nbr of characters
; ---------------------------------------------------------------------------
WRITE PROC
PUSH AX ; save registers
PUSH BX
MOV AH, 40H ; ah = write to file/device function
MOV BX, PHANDLE ; bx = printer handle
INT 21H ; issue dos call
POP BX ; restore registers
POP AX ;
RET ; ..and return to caller
WRITE ENDP
; ---------------------------------------------------------------------------
; UPCASE - Convert command line arguments to uppercase
; ---------------------------------------------------------------------------
UPCASE PROC
PUSH SI ; save caller regs
PUSH DI
MOV SI, 81H ; si -> start of parm area
MOV DI, SI ; .. same for di
CLD ; .. assure ascending
UPCASE10: LODSB ; al = char
CMP AL, 0DH ; q. end of line?
JE UPCASE90 ; a. yes .. end of line!
CMP AL, 'a' ; q. is it below 'a'?
JB UPCASE20 ; a. yes .. continue
CMP AL, 'z' ; q. is it above 'z'?
JA UPCASE20 ; a. yes .. continue
SUB AL, 20H ; set to upper case
UPCASE20: STOSB ; save the byte
JMP UPCASE10 ; .. and continue
UPCASE90: POP DI ; restore caller regs
POP SI
RET ; .. and return to caller
UPCASE ENDP
; ---------------------------------------------------------------------------
; ATOI - Translate an ascii value to binary
; Entry:
; si -> ascii value
; Exit:
; al = binary value
; ---------------------------------------------------------------------------
ATOI PROC
XOR AX, AX ; ax = accumulator = 0
ATOI10: CMP BYTE PTR [SI], '0' ; q. below ascii 0?
JB ATOI90 ; a. yes.. exit
CMP BYTE PTR [SI], '9' ; q. above ascii 9?
JA ATOI90 ; a. yes.. exit
XOR AH, AH ; reset ah
MOV BL, 10 ; bl = multiply value
MUL BL ; .. multiply by 10
MOV BL, [SI] ; bl = value
AND BL, 0FH ; .. upper bits off
ADD AL, BL ; .. add to bl
INC SI ; si -> next char
JMP ATOI10 ; .. tranlate it
ATOI90: RET ; .. return to caller
ATOI ENDP
; ---------------------------------------------------------------------------
; ITOA - Integer to ASCII characters
; Entry:
; ax = value to convert
; bl = fill character
; cx = nbr of characters
; di -> start of alpha area
; ---------------------------------------------------------------------------
ITOA PROC
PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
PUSH DI
PUSHF
STD ; ..and set direction flag
ADD DI, CX ; di -> 1st char past work area
DEC DI ; di -> last char in work area
PUSH BX ; save fill character
MOV BX, 10 ; bx = divisor
ITOA10: OR AX, AX ; q. any value to convert?
JZ ITOA20 ; a. no .. exit loop
XOR DX, DX ; dx:ax = value to divide
IDIV BX ; ax = dividend, dx = remainder
OR DL, 30H ; dl = ASCII number
MOV [DI], DL ; store character in buffer
DEC DI ; di -> next output char location
DEC CX ; q. any more room in buffer?
JNZ ITOA10 ; a. yes .. continue loop
POP BX ; restore register
JMP ITOA90 ; ..and exit through common code
ITOA20: POP AX ; al = fill character
ITOA30: STOSB ; store fill character
DEC CX ; q. any more room in buffer?
JNZ ITOA30 ; a. yes .. continue loop
ITOA90: POPF ; restore flags
POP DI ; ..and registers
POP DX ;
POP CX ;
POP BX ;
POP AX ;
RET ; ..and return
ITOA ENDP
; ---------------------------------------------------------------------------
; Uninitialized data areas
; ---------------------------------------------------------------------------
UDATA EQU $ ; start of unitialized data
PAGENO EQU WORD PTR UDATA ; current page number
CURCOL EQU WORD PTR PAGENO+2 ; current column
LINECNT EQU WORD PTR CURCOL+2 ; line count
EDRV EQU BYTE PTR LINECNT+2 ; current disk
EDIR EQU BYTE PTR EDRV+1 ; current directory
FILENAME EQU BYTE PTR EDIR+65 ; filename specifed as ARG1
LASTBUFF EQU BYTE PTR FILENAME+13 ; last buffer indicator
FHANDLE EQU WORD PTR LASTBUFF+1 ; input file handle
BUFFER EQU BYTE PTR FHANDLE+2 ; file buffer
BUFLEN EQU 2048 ; length of buffer
USEDLEN EQU BYTE PTR BUFFER+BUFLEN ; used length in a logical line
OBUFF EQU BYTE PTR USEDLEN+1 ; output buffer - must be last!
CSEG ENDS ; end of code segment
END START
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/