MS-DOS/v2.0/source/PRINT.ASM

1645 lines
47 KiB
NASM
Raw Normal View History

1983-08-13 01:53:34 +01:00
;MS-DOS PRINT program for background printing of text files to the list
; device. INT 28H is a software interrupt generated by the DOS
; in its I/O wait loops. This spooler can be assembled for
; operation using only this interrupt which is portable from
; system to system. It may also be assembled to use a hardware
; timer interrupt in addition to the software INT 28H. The
; purpose of using hardware interrupts is to allow printing to
; continue during programs which do not enter the system and
; therefore causes the INT 28H to go away. A timer interrupt is
; chosen in preference to a "printer buffer empty" interrupt
; because PRINT in the timer form is generic. It can be given
; the name of any currently installed character device as the
; "printer", this makes it portable to devices which are
; installed by the user even in the hardware case. It could be
; modified to use a buffer empty interrupt (no code is given for
; this case), if this is done the PROMPT and BADMES messages and
; their associated code should be removed as PRINT will then be
; device specific.
;
; VERSION 1.00 07/03/82
FALSE EQU 0
TRUE EQU NOT FALSE
IBM EQU FALSE
IBMVER EQU IBM
MSVER EQU TRUE
IF MSVER
HARDINT EQU FALSE ;No hardware ints
AINT EQU FALSE ;No need to do interrupt acknowledge
ENDIF
IF IBM
HARDINT EQU TRUE
INTLOC EQU 1CH ;Hardware interrupt location (Timer)
AINT EQU TRUE ;Acknowledge interrupts
EOI EQU 20H ;End Of Interrupt "instruction"
AKPORT EQU 20H ;Interrupt Acknowledge port
ENDIF
;The following values have to do with the ERRCNT variable and the
; CNTMES message. The values define levels at wich it is assumed
; an off-line error exists. ERRCNT1 defines the value of ERRCNT above
; which the CNTMES message is printed by the transient. ERRCNT2
; defines the value of ERRCNT above which the resident will give up
; trying to print messages on the printer, it is much greater than
; ERRCNT1 because a much tighter loop is involved. The bounding event
; which determines the correct value is the time required to do a
; form feed.
IF IBM
ERRCNT1 EQU 1000
ERRCNT2 EQU 20000
ELSE
ERRCNT1 EQU 1000
ERRCNT2 EQU 20000
ENDIF
IF HARDINT
TIMESLICE EQU 8 ;The PRINT scheduling time slice. PRINT
; lets this many "ticks" go by before
; using a time slice to pump out characters.
; Setting this to 3 for instance means PRINT
; Will skip 3 slices, then take the fourth.
; Thus using up 1/4 of the CPU. Setting it
; to one gives PRINT 1/2 of the CPU.
; The above examples assume MAXTICK is
; 1. The actual PRINT CPU percentage is
; (MAXTICK/(1+TIMESLICE))*100
MAXTICK EQU 2 ;The PRINT in timeslice. PRINT will pump
; out characters for this many clock ticks
; and then exit. The selection of a value
; for this is dependent on the timer rate.
BUSYTICK EQU 1 ;If PRINT sits in a wait loop waiting for
; output device to come ready for this
; many ticks, it gives up its time slice.
; Setting it greater than or equal to
; MAXTICK causes it to be ignored.
;User gets TIMESLICE ticks and then PRINT takes MAXTICK ticks unless BUSYTICK
; ticks go by without getting a character out.
ENDIF
;WARNING DANGER WARNING:
; PRINT is a systems utility. It is clearly understood that it may have
; to be entirely re-written for future versions of MS-DOS. The following
; TWO vectors are version specific, they may not exist at all in future
; versions. If they do exist, they may function differently.
; ANY PROGRAM WHICH IMITATES PRINTS USE OF THESE VECTORS IS ALSO A SYSTEMS
; UTILITY AND IS THEREFORE NOT VERSION PORTABLE IN ANY WAY SHAPE OR FORM.
; YOU HAVE BEEN WARNED, "I DID IT THE SAME WAY PRINT DID" IS NOT AN REASON
; TO EXPECT A PROGRAM TO WORK ON FUTURE VERSIONS OF MS-DOS.
SOFTINT EQU 28H ;Software interrupt generated by DOS
COMINT EQU 2FH ;Communications interrupt used by PRINT
; This vector number is DOS reserved. It
; is not generally available to programs
; other than PRINT.
BLKSIZ EQU 512 ;Size of the PRINT I/O block in bytes
FCBSIZ EQU 40 ;Size of an FCB
INCLUDE DOST:DOSSYM.ASM
FCB EQU 5CH
PARMS EQU 80H
DG GROUP CODE,DATA
CODE SEGMENT
ASSUME CS:DG
ORG 100H
START:
JMP TRANSIENT
HEADER DB "Vers 1.00"
DB 128 DUP (?)
ISTACK LABEL WORD ;Stack starts here and grows down
;Resident data
IF HARDINT
INDOS DD ? ;DOS buisy flag
NEXTINT DD ? ;Chain for int
BUSY DB 0 ;Internal ME flag
SOFINT DB 0 ;Internal ME flag
TICKCNT DB 0 ;Tick counter
TICKSUB DB 0 ;Tick miss counter
SLICECNT DB TIMESLICE ;Time slice counter
ENDIF
CBUSY DB 0 ;ME on com interrupt
SPNEXT DD ? ;Chain location for INT 28
PCANMES DB 0 ;Cancel message flag
SSsave DW ? ;Stack save area for INT 24
SPsave DW ?
DMAADDR DD ? ;Place to save DMA address
HERRINT DD ? ;Place to save Hard error interrupt
LISTDEV DD ? ;Pointer to Device
COLPOS DB 0 ;Column position for TAB processing
NXTCHR DW OFFSET DG:BUFFER + BLKSIZ ;Buffer pointer
CURRFIL DW OFFSET DG:SPLFCB ;Current file being printed
LASTFCB DW ? ;Back pointer
LASTFCB2 DW ? ;Another back pointer
PABORT DB 0 ;Abort flag
;Resident messages
ERRMES DB 13,10,13,10,"**********",13,10,"$"
ERRMEST DB " error reading file",13,10
EMFILNAM DB " : . "
BELMES DB 13,0CH,7,"$"
CANMES DB 13,10,13,10
CANFILNAM DB " : . "
DB " Canceled by operator$"
ALLCAN DB 13,10,13,10,"All files canceled by operator$"
MESBAS DW OFFSET DG:ERR0
DW OFFSET DG:ERR1
DW OFFSET DG:ERR2
DW OFFSET DG:ERR3
DW OFFSET DG:ERR4
DW OFFSET DG:ERR5
DW OFFSET DG:ERR6
DW OFFSET DG:ERR7
DW OFFSET DG:ERR8
DW OFFSET DG:ERR9
DW OFFSET DG:ERR10
DW OFFSET DG:ERR11
DW OFFSET DG:ERR12
;INT 24 messages A La COMMAND
ERR0 DB "Write protect$"
ERR1 DB "Bad unit$"
ERR2 DB "Not ready$"
ERR3 DB "Bad command$"
ERR4 DB "Data$"
ERR5 DB "Bad call format$"
ERR6 DB "Seek$"
ERR7 DB "Non-DOS disk$"
ERR8 DB "Sector not found$"
ERR9 DB "No paper$"
ERR10 DB "Write fault$"
ERR11 DB "Read fault$"
ERR12 DB "Disk$"
FATMES DB "File allocation table bad drive "
BADDRVM DB "A.",13,10,"$"
;The DATA buffer
BUFFER DB BLKSIZ DUP(0)
DB ?
CODE ENDS
;Transient data
DATA SEGMENT BYTE
ORG 0
SWITCHAR DB ? ;User switch character
FULLFLAG DB 0 ;Flag for printing queue full message
MAKERES DB 0 ;Flag to indicate presence of resident
ARGSETUP DB 0 ;Flag to indicate a formatted FCB exists at 5C
DEFDRV DB 0 ;Default drive
CANFLG DB 0 ;Flag to indicate cancel
FILCNT DB 0 ;Number of files
SPLIST DD ? ;Pointer to FCBs in resident
CURFILE DD ? ;Pointer to current FCB
SRCHFCB DB 38 DUP (0) ;SEARCH-FIRST/NEXT FCB
ENDRES DW OFFSET DG:DEF_ENDRES ;Term-Res location
;Messages
NOFILS DB "PRINT queue is empty",13,10,"$"
CURMES DB 13,10," "
CURFNAM DB " : . is currently being printed",13,10,"$"
FILMES DB " "
FILFNAM DB " : . is in queue"
CRLF DB 13,10,"$"
OPMES DB "Cannot open "
OPFILNAM DB " : . ",13,10,"$"
FULLMES DB "PRINT queue is full",13,10,"$"
SRCHMES LABEL BYTE
SRCHFNAM DB " : . "," File not found",13,10,"$"
BADMES DB "List output is not assigned to a device",13,10,"$"
GOODMES DB "Resident part of PRINT installed",13,10,"$"
PROMPT DB "Name of list device [PRN]: $"
CNTMES DB "Errors on list device indicate that it",13,10
DB "may be off-line. Please check it.",13,10,13,10,"$"
BADSWT DB "Invalid parameter",13,10,"$"
BADVER DB "Incorrect DOS version",13,10,"$"
IF IBM
;Reserved names for parallel card
INT_17_HITLIST LABEL BYTE
DB 8,"PRN ",0
DB 8,"LPT1 ",0
DB 8,"LPT2 ",1
DB 8,"LPT3 ",2
DB 0
;Reserved names for Async adaptor
INT_14_HITLIST LABEL BYTE
DB 8,"AUX ",0
DB 8,"COM1 ",0
DB 8,"COM2 ",1
DB 0
ENDIF
COMBUF DB 14,0 ;Device name buffer
DB 14 DUP (?)
LISTFCB DB 0,"PRN " ;Device name FCB
DB 25 DUP (0)
PARSEBUF DB 80 DUP (?) ;Parsing space
DATA ENDS
CODE SEGMENT
ASSUME CS:DG,DS:DG,ES:DG,SS:DG
;Interrupt routines
ASSUME CS:DG,DS:NOTHING,ES:NOTHING,SS:NOTHING
IF HARDINT
HDSPINT: ;Hardware interrupt entry point
INC [TICKCNT] ;Tick
INC [TICKSUB] ;Tick
CMP [SLICECNT],0
JZ TIMENOW
DEC [SLICECNT] ;Count down
JMP SHORT CHAININT ;Not time yet
TIMENOW:
CMP [BUSY],0 ;See if interrupting ourself
JNZ CHAININT
PUSH DS
PUSH SI
LDS SI,[INDOS] ;Check for making DOS calls
CMP BYTE PTR [SI],0
POP SI
POP DS
JNZ CHAININT ;DOS is Buisy
INC [BUSY] ;Exclude furthur interrupts
MOV [TICKCNT],0 ;Reset tick counter
MOV [TICKSUB],0 ;Reset tick counter
STI ;Keep things rolling
IF AINT
MOV AL,EOI ;Acknowledge interrupt
OUT AKPORT,AL
ENDIF
CALL DOINT
CLI
MOV [SLICECNT],TIMESLICE ;Either soft or hard int resets time slice
MOV [BUSY],0 ;Done, let others in
CHAININT:
JMP [NEXTINT] ;Chain to next clock routine
ENDIF
SPINT: ;INT 28H entry point
IF HARDINT
CMP [BUSY],0
JNZ NXTSP
INC [BUSY] ;Exclude hardware interrupt
INC [SOFINT] ;Indicate a software int in progress
ENDIF
STI ;Hardware interrupts ok on INT 28H entry
CALL DOINT
IF HARDINT
CLI
MOV [SOFINT],0 ;Indicate INT done
MOV [SLICECNT],TIMESLICE ;Either soft or hard int resets time slice
MOV [BUSY],0
ENDIF
NXTSP: JMP [SPNEXT] ;Chain to next INT 28
DOINT:
PUSH SI
MOV SI,[CURRFIL]
INC SI
INC SI
CMP BYTE PTR CS:[SI],-1
POP SI
JNZ GOAHEAD
JMP SPRET ;Nothing to do
GOAHEAD:
PUSH AX ;Need a working register
MOV [SSsave],SS
MOV [SPsave],SP
MOV AX,CS
CLI
;Go to internal stack to prevent INT 24 overflowing system stack
MOV SS,AX
MOV SP,OFFSET DG:ISTACK
STI
PUSH ES
PUSH DS
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH CS
POP DS
ASSUME DS:DG
MOV BX,[NXTCHR]
CMP BX,OFFSET DG:BUFFER + BLKSIZ
JNZ PLOOP
JMP READBUFF ;Buffer empty
PLOOP:
IF HARDINT
MOV BX,[NXTCHR]
CMP BX,OFFSET DG:BUFFER + BLKSIZ
JZ DONEJMP ;Buffer has become empty
CMP [SOFINT],0
JNZ STATCHK
CMP [TICKCNT],MAXTICK ;Check our time slice
JAE DONEJMP
STATCHK:
ENDIF
CALL PSTAT
IF HARDINT
JZ DOCHAR ;Printer ready
CMP [SOFINT],0
ENDIF
JNZ DONEJMP ;If soft int give up
IF HARDINT
CMP [TICKSUB],BUSYTICK ;Check our busy timeout
JAE DONEJMP
JMP PLOOP
ENDIF
DOCHAR:
MOV AL,BYTE PTR [BX]
CMP AL,1AH ;^Z?
JZ FILEOFJ ;CPM EOF
CMP AL,0DH ;CR?
JNZ NOTCR
MOV [COLPOS],0
NOTCR:
CMP AL,9 ;TAB?
JNZ NOTABDO
MOV CL,[COLPOS]
OR CL,0F8H
NEG CL
XOR CH,CH
JCXZ TABDONE
TABLP:
MOV AL," "
INC [COLPOS]
PUSH CX
CALL POUT
POP CX
LOOP TABLP
JMP TABDONE
NOTABDO:
CMP AL,8 ;Back space?
JNZ NOTBACK
DEC [COLPOS]
NOTBACK:
CMP AL,20H ;Non Printing char?
JB NOCHAR
INC [COLPOS] ;Printing char
NOCHAR:
CALL POUT ;Print it
TABDONE:
INC [NXTCHR] ;Next char
IF HARDINT
MOV [TICKSUB],0 ;Got a character out, Reset counter
CMP [SOFINT],0 ;Soft int does one char at a time
JZ PLOOP
ENDIF
DONEJMP:
POP DI
POP SI
POP DX
POP CX
POP BX
POP DS
POP ES
ASSUME DS:NOTHING,ES:NOTHING
CLI
MOV SS,[SSsave] ;Restore Entry Stack
MOV SP,[SPsave]
STI
POP AX
SPRET:
RET
FILEOFJ: JMP FILEOF
READBUFF:
ASSUME DS:DG,ES:NOTHING
MOV AL,24H
MOV AH,GET_INTERRUPT_VECTOR
INT 21H
MOV WORD PTR [HERRINT+2],ES ;Save current vector
MOV WORD PTR [HERRINT],BX
MOV DX,OFFSET DG:DSKERR
MOV AL,24H
MOV AH,SET_INTERRUPT_VECTOR ;Install our own
INT 21H ;Spooler must catch its errors
MOV AH,GET_DMA
INT 21H
MOV WORD PTR [DMAADDR+2],ES ;Save DMA address
MOV WORD PTR [DMAADDR],BX
MOV DX,OFFSET DG:BUFFER
MOV AH,SET_DMA
INT 21H ;New DMA address
MOV [PABORT],0 ;No abort
MOV DX,[CURRFIL] ;Read
INC DX
INC DX ;Skip over pointer
MOV AH,FCB_SEQ_READ
INT 21H
PUSH AX
LDS DX,[DMAADDR]
ASSUME DS:NOTHING
MOV AH,SET_DMA
INT 21H ;Restore DMA
LDS DX,[HERRINT]
MOV AL,24H
MOV AH,SET_INTERRUPT_VECTOR
INT 21H ;Restore Error INT
POP AX
PUSH CS
POP DS
ASSUME DS:DG
CMP [PABORT],0
JNZ TONEXTFIL ;Barf on this file, got INT 24
CMP AL,01
JZ FILEOF ;Read EOF?
MOV BX,OFFSET DG:BUFFER ;Buffer full
MOV [NXTCHR],BX
JMP DONEJMP
FILEOF:
MOV AL,0CH ;Form feed
CALL LOUT
TONEXTFIL:
CALL NEXTFIL
JMP DONEJMP
;INT 24 handler
DSKERR:
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
STI
CMP [PABORT],0
JNZ IGNRET
PUSH BX
PUSH CX
PUSH DX
PUSH DI
PUSH SI
PUSH BP
PUSH ES
PUSH DS
PUSH CS
POP DS
PUSH CS
POP ES
ASSUME DS:DG,ES:DG
ADD [BADDRVM],AL ;Set correct drive letter
MOV SI,OFFSET DG:ERRMES
CALL LISTMES
TEST AH,080H
JNZ FATERR
AND DI,0FFH
CMP DI,12
JBE HAVCOD
MOV DI,12
HAVCOD:
SHL DI,1
MOV DI,WORD PTR [DI+MESBAS] ; Get pointer to error message
MOV SI,DI
CALL LISTMES ; Print error type
MOV DI,OFFSET DG:EMFILNAM
MOV SI,[CURRFIL]
ADD SI,2 ;Get to file name
LODSB
ADD AL,'@'
STOSB
INC DI
MOV CX,4
REP MOVSW
INC DI
MOVSW
MOVSB
MOV SI,OFFSET DG:ERRMEST
CALL LISTMES
SETABORT:
INC [PABORT] ;Indicate abort
POP DS
POP ES
POP BP
POP SI
POP DI
POP DX
POP CX
POP BX
IGNRET:
XOR AL,AL ;Ignore
IRET
FATERR:
MOV SI,OFFSET DG:FATMES
CALL LISTMES
JMP SHORT SETABORT
ADDFILJ: JMP ADDFIL
COMBUSY:
MOV AX,-1
IRET
;Communications interrupt
SPCOMINT:
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
CMP [CBUSY],0
JNZ COMBUSY
INC [CBUSY] ;Exclude
STI ;Turn ints back on
PUSH SI
PUSH DI
PUSH CX
PUSH DS
PUSH CS
POP DS
ASSUME DS:DG
MOV [PCANMES],0 ;Havn't printed cancel message
OR AH,AH
JZ ADDFILJ ;Add file
CMP AH,1
JZ CANFIL ;Cancel File(s)
XOR AL,AL
SETCOUNT:
PUSH AX ;Save AL return code
XOR AH,AH
MOV SI,OFFSET DG:SPLFCB
MOV CX,[NUMFCBS]
CNTFILS:
CMP BYTE PTR [SI+2],-1 ;Valid?
JZ LNEXT
INC AH
LNEXT:
ADD SI,FCBSIZ
LOOP CNTFILS
COMRET:
MOV BX,OFFSET DG:SPLFCB
MOV DX,[CURRFIL]
PUSH DS
POP ES
ASSUME ES:NOTHING
MOV CH,AH
POP AX ;Get AL return
MOV AH,CH
IF HARDINT
BWAIT3:
CMP [BUSY],0
JNZ BWAIT3
INC [BUSY]
ENDIF
CALL PSTAT ; Tweek error counter
IF HARDINT
MOV [BUSY],0
ENDIF
POP DS
ASSUME DS:NOTHING
POP CX
POP DI
POP SI
MOV [CBUSY],0
IRET
DELALLJ: JMP DELALL
CANFIL:
ASSUME DS:DG,ES:NOTHING
MOV CX,[NUMFCBS]
IF HARDINT
BWAIT:
CMP [BUSY],0
JNZ BWAIT
INC [BUSY]
ENDIF
MOV SI,[CURRFIL]
CMP DX,-1
JZ DELALLJ
MOV BX,[SI]
PUSH BX
LOOKEND: ;Set initial pointer values
CMP BX,SI
JZ GOTLAST
POP AX
PUSH BX
MOV BX,[BX]
JMP SHORT LOOKEND
GOTLAST:
POP BX
MOV [LASTFCB],BX
MOV [LASTFCB2],BX
POP ES
PUSH ES
MOV BX,SI
LOOKMATCH:
MOV DI,DX
ADD SI,2 ;Skip pointer
CMP BYTE PTR [SI],-1
JZ CANTERMJ ;No more
CMPSB
JNZ SKIPFIL ;DRIVE
PUSH CX
MOV CX,11
NXTCHAR:
MOV AL,ES:[DI]
INC DI
CALL UPCONV
MOV AH,AL
LODSB
CALL UPCONV
CMP AH,"?" ;Wild card?
JZ NXTCHRLP ;Yes
CMP AH,AL
JNZ SKIPFILC
NXTCHRLP:
LOOP NXTCHAR
MATCH:
POP CX
MOV AH,-1
XCHG AH,[BX+2] ;Zap it
CMP BX,[CURRFIL] ;Is current file?
JNZ REQUEUE ;No
MOV AL,1
XCHG AL,[PCANMES]
OR AL,AL
JNZ DIDCMES ;Only print cancel message once
PUSH ES
PUSH CS
POP ES
MOV DI,OFFSET DG:CANFILNAM
MOV SI,BX
ADD SI,3 ;Get to file name
MOV AL,AH
ADD AL,'@'
STOSB
INC DI
MOV CX,4
REP MOVSW
INC DI
MOVSW
MOVSB
POP ES
MOV SI,OFFSET DG:CANMES
CALL LISTMES
MOV SI,OFFSET DG:BELMES
CALL LISTMES
DIDCMES:
PUSH CX
CALL NEXTFIL
SKIPFILC:
POP CX
SKIPFIL:
MOV [LASTFCB2],BX
MOV BX,[BX]
NEXTFC:
MOV SI,BX
LOOP LOOKMATCH
CANTERMJ: JMP SHORT CANTERM
REQUEUE:
MOV AX,[BX]
CMP AX,[CURRFIL] ;Is last FCB?
JZ SKIPFIL ;Yes, is in right place
MOV SI,[LASTFCB2]
MOV [SI],AX ;Unlink FCB
MOV SI,[CURRFIL]
MOV [BX],SI
MOV SI,[LASTFCB]
MOV [SI],BX ;Link FCB at end
MOV [LASTFCB],BX ;New end
MOV BX,AX ;Process what it pointed to
JMP SHORT NEXTFC
DELALL:
CMP BYTE PTR CS:[SI+2],-1 ;Examine current file
DELALL2:
MOV BYTE PTR [SI+2],-1 ;Zap it
MOV SI,[SI]
LOOP DELALL2
JZ CANTERM1 ;No message if nothing was in progress
MOV SI,OFFSET DG:ALLCAN
CALL LISTMES
MOV SI,OFFSET DG:BELMES
CALL LISTMES
CANTERM1:
MOV [NXTCHR],OFFSET DG:BUFFER + BLKSIZ ;Buffer empty
CANTERM:
IF HARDINT
MOV [BUSY],0
ENDIF
XOR AX,AX
JMP SETCOUNT
UPCONV:
CMP AL,'a'
JB NOCONV
CMP AL,'z'
JA NOCONV
SUB AL,20H
NOCONV:
RET
ADDFIL:
ASSUME DS:DG,ES:NOTHING
MOV SI,[CURRFIL]
MOV CX,[NUMFCBS]
IF HARDINT
BWAIT2:
CMP [BUSY],0
JNZ BWAIT2
INC [BUSY]
ENDIF
LOOKSPOT:
CMP BYTE PTR [SI+2],-1
JZ GOTSPOT
MOV SI,[SI]
LOOP LOOKSPOT
IF HARDINT
MOV [BUSY],0
ENDIF
MOV AL,1
JMP SETCOUNT
GOTSPOT:
PUSH DS
POP ES
POP DS
PUSH DS
ASSUME DS:NOTHING
PUSH SI
MOV DI,SI
ADD DI,2
MOV SI,DX
MOV CX,19
REP MOVSW ;Copy in and set FCB
POP SI
PUSH ES
POP DS
ASSUME DS:DG
MOV WORD PTR [SI+2+fcb_EXTENT],0
MOV BYTE PTR [SI+2+fcb_NR],0
MOV WORD PTR [SI+2+fcb_RECSIZ],BLKSIZ
IF HARDINT
MOV [BUSY],0
ENDIF
XOR AL,AL
JMP SETCOUNT
NEXTFIL:
ASSUME DS:DG,ES:NOTHING
MOV SI,[CURRFIL]
MOV BYTE PTR [SI+2],-1 ;Done with current file
MOV SI,[SI]
MOV [CURRFIL],SI
MOV [NXTCHR],OFFSET DG:BUFFER + BLKSIZ ;Buffer empty
MOV [COLPOS],0 ;Start of line
RET
LISTMES:
ASSUME DS:DG,ES:NOTHING
LODSB
CMP AL,"$"
JZ LMESDONE
CALL LOUT
JMP LISTMES
LMESDONE:
RET
LOUT:
PUSH BX
LWAIT:
CALL PSTAT
JZ PREADY
CMP [ERRCNT],ERRCNT2
JA POPRET ;Don't get stuck
JMP SHORT LWAIT
PREADY:
CALL POUT
POPRET:
POP BX
RET
;Stuff for BIOS interface
IOBUSY EQU 0200H
IOERROR EQU 8000H
BYTEBUF DB ?
CALLAD DD ?
IOCALL DB 22
DB 0
IOREQ DB ?
IOSTAT DW 0
DB 8 DUP(?)
DB 0
DW OFFSET DG:BYTEBUF
INTSEG DW ?
IOCNT DW 1
DW 0
PSTAT:
ASSUME DS:DG
PUSH BX
INC [ERRCNT]
MOV BL,10
CALL DOCALL
TEST [IOSTAT],IOERROR
JZ NOSTATERR
OR [IOSTAT],IOBUSY ;If error, show buisy
NOSTATERR:
TEST [IOSTAT],IOBUSY
JNZ RET13P ;Shows buisy
MOV [ERRCNT],0
RET13P:
POP BX
RET
POUT:
ASSUME DS:DG
MOV [BYTEBUF],AL
MOV BL,8
DOCALL:
PUSH ES
MOV [IOREQ],BL
MOV BX,CS
MOV ES,BX
MOV BX,OFFSET DG:IOCALL
MOV [IOSTAT],0
MOV [IOCNT],1
PUSH DS
PUSH SI
PUSH AX
LDS SI,[LISTDEV]
ASSUME DS:NOTHING
MOV AX,[SI+6]
MOV WORD PTR [CALLAD],AX
CALL [CALLAD]
MOV AX,[SI+8]
MOV WORD PTR [CALLAD],AX
CALL [CALLAD]
POP AX
POP SI
POP DS
ASSUME DS:DG
POP ES
RET
IF IBM
REAL_INT_13 DD ?
INT_13_RETADDR DW OFFSET DG:INT_13_BACK
INT_13 PROC FAR
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
PUSHF
INC [BUSY] ;Exclude if dumb program call ROM
PUSH CS
PUSH [INT_13_RETADDR]
PUSH WORD PTR [REAL_INT_13+2]
PUSH WORD PTR [REAL_INT_13]
RET
INT_13 ENDP
INT_13_BACK PROC FAR
PUSHF
DEC [BUSY]
POPF
RET 2 ;Chuck saved flags
INT_13_BACK ENDP
ENDIF
IF IBM
REAL_INT_5 DD ?
REAL_INT_17 DD ?
INT_17_NUM DW 0
INT_17:
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
PUSH SI
MOV SI,[CURRFIL]
INC SI
INC SI
CMP BYTE PTR CS:[SI],-1
POP SI
JZ DO_INT_17 ;Nothing pending, so OK
CMP DX,[INT_17_NUM]
JNZ DO_INT_17 ;Not my unit
CMP [BUSY],0
JNZ DO_INT_17 ;You are me
STI
MOV AH,0A1H ;You are bad, get out of paper
IRET
DO_INT_17:
JMP [REAL_INT_17] ;Do a 17
REAL_INT_14 DD ?
INT_14_NUM DW 0
INT_14:
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
PUSH SI
MOV SI,[CURRFIL]
INC SI
INC SI
CMP BYTE PTR CS:[SI],-1
POP SI
JZ DO_INT_14 ;Nothing pending, so OK
CMP DX,[INT_14_NUM]
JNZ DO_INT_14 ;Not my unit
CMP [BUSY],0
JNZ DO_INT_14 ;You are me
STI
OR AH,AH
JZ SET14_AX
CMP AH,2
JBE SET14_AH
SET14_AX:
MOV AL,0
SET14_AH:
MOV AH,80H ;Time out
IRET
DO_INT_14:
JMP [REAL_INT_14] ;Do a 14
INT_5:
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
PUSH SI
MOV SI,[CURRFIL]
INC SI
INC SI
CMP BYTE PTR CS:[SI],-1
POP SI
JZ DO_INT_5 ;Nothing pending, so OK
CMP [INT_17_NUM],0
JNZ DO_INT_5 ;Only care about unit 0
IRET ;Pretend it worked
DO_INT_5:
JMP [REAL_INT_5] ;Do a 5
ENDIF
;The following data is order and position dependant
NUMFCBS DW 10
ERRCNT DW 0
SPLFCB DW OFFSET DG:FC1
DB (FCBSIZ - 2) DUP (-1)
FC1 DW OFFSET DG:FC2
DB (FCBSIZ - 2) DUP (-1)
FC2 DW OFFSET DG:FC3
DB (FCBSIZ - 2) DUP (-1)
FC3 DW OFFSET DG:FC4
DB (FCBSIZ - 2) DUP (-1)
FC4 DW OFFSET DG:FC5
DB (FCBSIZ - 2) DUP (-1)
FC5 DW OFFSET DG:FC6
DB (FCBSIZ - 2) DUP (-1)
FC6 DW OFFSET DG:FC7
DB (FCBSIZ - 2) DUP (-1)
FC7 DW OFFSET DG:FC8
DB (FCBSIZ - 2) DUP (-1)
FC8 DW OFFSET DG:FC9
DB (FCBSIZ - 2) DUP (-1)
FC9 DW OFFSET DG:SPLFCB
DB (FCBSIZ - 2) DUP (-1)
DEF_ENDRES LABEL BYTE
ASSUME CS:DG,DS:DG,ES:DG,SS:DG
BADSPOOL:
MOV DX,OFFSET DG:BADMES
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
INT 20H
SETUP:
;Called once to install resident
CLD
MOV [INTSEG],CS
MOV DX,OFFSET DG:PROMPT
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
MOV DX,OFFSET DG:COMBUF
MOV AH,STD_CON_STRING_INPUT
INT 21H ;Get device name
MOV DX,OFFSET DG:CRLF
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
MOV CL,[COMBUF+1]
OR CL,CL
JZ DEFSPOOL ;User didn't specify one
XOR CH,CH
MOV DI,OFFSET DG:LISTFCB + 1
MOV SI,OFFSET DG:COMBUF + 2
REP MOVSB
DEFSPOOL:
MOV DX,OFFSET DG:LISTFCB
MOV AH,FCB_OPEN
INT 21H
OR AL,AL
JNZ BADSPOOL ;Bad
TEST BYTE PTR [LISTFCB.fcb_DEVID],080H
JZ BADSPOOL ;Must be a device
LDS SI,DWORD PTR [LISTFCB.fcb_FIRCLUS]
ASSUME DS:NOTHING
MOV WORD PTR [CALLAD+2],DS ;Get I/O routines
MOV WORD PTR [LISTDEV+2],DS ;Get I/O routines
MOV WORD PTR [LISTDEV],SI
PUSH CS
POP DS
ASSUME DS:DG
MOV DX,OFFSET DG:SPINT
MOV AL,SOFTINT
MOV AH,GET_INTERRUPT_VECTOR
INT 21H ;Get soft vector
MOV WORD PTR [SPNEXT+2],ES
MOV WORD PTR [SPNEXT],BX
MOV AL,SOFTINT
MOV AH,SET_INTERRUPT_VECTOR
INT 21H ;Set soft vector
MOV DX,OFFSET DG:SPCOMINT
MOV AL,COMINT
MOV AH,SET_INTERRUPT_VECTOR ;Set communication vector
INT 21H
IF IBM
MOV AL,13H
MOV AH,GET_INTERRUPT_VECTOR
INT 21H
MOV WORD PTR [REAL_INT_13+2],ES
MOV WORD PTR [REAL_INT_13],BX
MOV DX,OFFSET DG:INT_13
MOV AL,13H
MOV AH,SET_INTERRUPT_VECTOR
INT 21H ;Set diskI/O interrupt
MOV AL,17H
MOV AH,GET_INTERRUPT_VECTOR
INT 21H
MOV WORD PTR [REAL_INT_17+2],ES
MOV WORD PTR [REAL_INT_17],BX
MOV AL,14H
MOV AH,GET_INTERRUPT_VECTOR
INT 21H
MOV WORD PTR [REAL_INT_14+2],ES
MOV WORD PTR [REAL_INT_14],BX
MOV AL,5H
MOV AH,GET_INTERRUPT_VECTOR
INT 21H
MOV WORD PTR [REAL_INT_5+2],ES
MOV WORD PTR [REAL_INT_5],BX
PUSH CS
POP ES
MOV BP,OFFSET DG:LISTFCB + 1
MOV SI,BP
MOV CX,8
CONLP: ;Make sure device name in upper case
LODSB
CMP AL,'a'
JB DOCONLP
CMP AL,'z'
JA DOCONLP
SUB BYTE PTR [SI-1],20H
DOCONLP:
LOOP CONLP
MOV DI,OFFSET DG:INT_17_HITLIST
CHKHIT:
MOV SI,BP
MOV CL,[DI]
INC DI
JCXZ NOTONHITLIST
REPE CMPSB
LAHF
ADD DI,CX ;Bump to next position without affecting flags
MOV BL,[DI] ;Get device number
INC DI
SAHF
JNZ CHKHIT
XOR BH,BH
MOV [INT_17_NUM],BX
MOV DX,OFFSET DG:INT_17
MOV AL,17H
MOV AH,SET_INTERRUPT_VECTOR
INT 21H ;Set printer interrupt
MOV DX,OFFSET DG:INT_5
MOV AL,5H
MOV AH,SET_INTERRUPT_VECTOR
INT 21H ;Set print screen interrupt
JMP SHORT ALLSET
NOTONHITLIST:
MOV DI,OFFSET DG:INT_14_HITLIST
CHKHIT2:
MOV SI,BP
MOV CL,[DI]
INC DI
JCXZ ALLSET
REPE CMPSB
LAHF
ADD DI,CX ;Bump to next position without affecting flags
MOV BL,[DI] ;Get device number
INC DI
SAHF
JNZ CHKHIT2
XOR BH,BH
MOV [INT_14_NUM],BX
MOV DX,OFFSET DG:INT_14
MOV AL,14H
MOV AH,SET_INTERRUPT_VECTOR
INT 21H ;Set RS232 port interrupt
ALLSET:
ENDIF
IF HARDINT
MOV AH,GET_INDOS_FLAG
INT 21H
MOV WORD PTR [INDOS+2],ES ;Get indos flag location
MOV WORD PTR [INDOS],BX
MOV AL,INTLOC
MOV AH,GET_INTERRUPT_VECTOR
INT 21H
MOV WORD PTR [NEXTINT+2],ES
MOV WORD PTR [NEXTINT],BX
MOV DX,OFFSET DG:HDSPINT
MOV AL,INTLOC
MOV AH,SET_INTERRUPT_VECTOR
INT 21H ;Set hardware interrupt
ENDIF
MOV [MAKERES],1 ;Indicate to do a terminate stay resident
MOV DX,OFFSET DG:GOODMES
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
RET
ASSUME CS:DG,DS:DG,ES:DG,SS:DG
TRANSIENT:
;User interface
CLD
;Code to print header
; MOV DX,OFFSET DG:HEADER
; MOV AH,STD_CON_STRING_OUTPUT
; INT 21H
DOSVER_LOW EQU 0136H ;1.54 in hex
DOSVER_HIGH EQU 0200H ;2.00 in hex
MOV AH,GET_VERSION
INT 21H
XCHG AH,AL ;Turn it around to AH.AL
CMP AX,DOSVER_LOW
JB GOTBADDOS
CMP AX,DOSVER_HIGH
JBE OKDOS
GOTBADDOS:
PUSH CS
POP DS
MOV DX,OFFSET DG:BADVER
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
INT 20H
OKDOS:
MOV AX,CHAR_OPER SHL 8
INT 21H
MOV [SWITCHAR],DL ;Get user switch character
MOV AH,GET_INTERRUPT_VECTOR
MOV AL,COMINT
INT 21H
ASSUME ES:NOTHING
MOV DI,BX
MOV SI,OFFSET DG:SPCOMINT
MOV CX,13
REPE CMPSB
JZ GOTRES ;Signature matched
PUSH CS
POP ES
CALL SETUP
GOTRES:
PUSH CS
POP ES
MOV AH,GET_DEFAULT_DRIVE
INT 21H
MOV [DEFDRV],AL
MOV SI,PARMS
LODSB
OR AL,AL
JNZ GOTPARMS
TRANEXIT:
CALL GETSPLIST
CMP [MAKERES],0
JNZ SETRES
INT 20H
SETRES:
MOV DX,[ENDRES]
INT 27H
ARGSDONE:
CMP [ARGSETUP],0
JZ TRANEXIT
CALL PROCESS
JMP SHORT TRANEXIT
GOTPARMS:
PARSE:
MOV DI,OFFSET DG:PARSEBUF
CALL CPARSE
JC ARGSDONE
CMP AX,4 ;Switch?
JNZ GOTNORMARG
MOV AL,[DI] ;Get the switch character
CMP AL,'C'
JZ SETCAN
CMP AL,'c'
JNZ CHKSPL
SETCAN:
MOV [CANFLG],1
JMP SHORT PARSE
CHKSPL:
CMP AL,'P'
JZ RESETCAN
CMP AL,'p'
JNZ CHKTERM
RESETCAN:
MOV [CANFLG],0
JMP SHORT PARSE
CHKTERM:
CMP AL,'T'
JZ SETTERM
CMP AL,'t'
JZ SETTERM
MOV DX,OFFSET DG:BADSWT
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
JMP SHORT PARSE
SETTERM:
CALL TERMPROCESS
JMP TRANEXIT ; Ignore everything after T switch
GOTNORMARG:
XOR AL,AL
XCHG AL,[ARGSETUP]
OR AL,AL
JZ PARSEARG
CALL NORMPROC ;Don't test ARGSETUP, it just got zeroed
PARSEARG:
PUSH SI
MOV SI,DI
MOV DI,FCB
MOV AX,(PARSE_FILE_DESCRIPTOR SHL 8) OR 1
INT 21H ;Parse the arg
CMP BYTE PTR [DI],0
JNZ DRVOK
MOV DL,[DEFDRV]
INC DL
MOV BYTE PTR [DI],DL ;Set the default drive
DRVOK:
POP SI
INC [ARGSETUP]
JMP SHORT PARSE
TERMPROCESS:
MOV DX,-1
PROCRET:
MOV AH,1
CALL DOSET
PROCRETNFUNC:
MOV [ARGSETUP],0
PUSH CS
POP ES
RET14: RET
PROCESS:
CMP [ARGSETUP],0
JZ RET14 ;Nothing to process
NORMPROC:
MOV AL,BYTE PTR DS:[FCB+1]
CMP AL," "
JZ SRCHBADJ
MOV DX,FCB
MOV AH,[CANFLG]
CMP AH,0
JNZ PROCRET
MOV DX,OFFSET DG:SRCHFCB
MOV AH,SET_DMA
INT 21H
MOV DX,FCB
MOV AH,DIR_SEARCH_FIRST
INT 21H
OR AL,AL
JNZ SRCHBADJ
SRCHLOOP:
MOV DX,OFFSET DG:SRCHFCB
MOV AH,FCB_OPEN
INT 21H
OR AL,AL
JZ OPENOK
CALL OPENERR
JMP SHORT NEXTSEARCH
SRCHBADJ: JMP SRCHBAD
OPENOK:
MOV DX,OFFSET DG:SRCHFCB
MOV AH,0
CALL DOSET
OR AL,AL
JZ NEXTSEARCH
XCHG AL,[FULLFLAG] ;Know AL non-zero
OR AL,AL
JNZ NEXTSEARCH ;Only print message once
MOV DX,OFFSET DG:FULLMES ;Queue full
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
NEXTSEARCH:
MOV DX,OFFSET DG:SRCHFCB
MOV AH,SET_DMA
INT 21H
MOV DX,FCB
MOV AH,DIR_SEARCH_NEXT
INT 21H
OR AL,AL
JNZ PROCRETNFUNC
JMP SRCHLOOP
DOSET:
INT COMINT
MOV [FILCNT],AH ;Suck up return info
MOV WORD PTR [SPLIST+2],ES
MOV WORD PTR [CURFILE+2],ES
MOV WORD PTR [SPLIST],BX
MOV WORD PTR [CURFILE],DX
RET
OPENERR:
PUSH SI
PUSH DI
MOV SI,OFFSET DG:OPFILNAM
PUSH DS
POP ES
MOV DI,OFFSET DG:SRCHFCB
CALL MVFNAM
MOV DX,OFFSET DG:OPMES
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
POP DI
POP SI
RET
SRCHBAD:
PUSH SI
PUSH DI
MOV SI,OFFSET DG:SRCHFNAM
PUSH DS
POP ES
MOV DI,FCB
CALL MVFNAM
MOV DX,OFFSET DG:SRCHMES
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
POP DI
POP SI
JMP PROCRETNFUNC
GETSPLIST:
MOV AH,0FFH
CALL DOSET
PUSH DS
LDS DI,[SPLIST]
MOV DI,[DI-2] ;Get the error count
POP DS
CMP DI,ERRCNT1
JB CNTOK
MOV DX,OFFSET DG:CNTMES
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
CNTOK:
MOV CL,[FILCNT]
OR CL,CL
JZ NOFILES
XOR CH,CH
LES DI,[CURFILE]
PUSH DI
INC DI
INC DI
MOV SI,OFFSET DG:CURFNAM
CALL MVFNAM
POP DI
MOV DX,OFFSET DG:CURMES
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
DEC CX
JCXZ RET12
FILOOP:
MOV DI,ES:[DI]
PUSH DI
INC DI
INC DI
MOV SI,OFFSET DG:FILFNAM
CALL MVFNAM
POP DI
MOV DX,OFFSET DG:FILMES
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
LOOP FILOOP
RET12: RET
NOFILES:
MOV DX,OFFSET DG:NOFILS
MOV AH,STD_CON_STRING_OUTPUT
INT 21H
RET
;Make a message with the file name
MVFNAM:
ASSUME DS:NOTHING,ES:NOTHING
PUSH SI
PUSH DI
PUSH CX
MOV AX,ES
PUSH DS
POP ES
MOV DS,AX
XCHG SI,DI
LODSB
ADD AL,"@"
CMP AL,"@"
JNZ STCHR
MOV AL,[DEFDRV]
ADD AL,"A"
STCHR:
STOSB
INC DI
MOV CX,4
REP MOVSW
INC DI
MOVSW
MOVSB
MOV AX,ES
PUSH DS
POP ES
MOV DS,AX
POP CX
POP DI
POP SI
RET
;-----------------------------------------------------------------------;
; ENTRY: ;
; DS:SI Points input buffer ;
; ES:DI Points to the token buffer ;
; ;
; EXIT: ;
; DS:SI Points to next char in the input buffer ;
; ES:DI Points to the token buffer ;
; CX Character count ;
; AX Condition Code ;
; =1 same as carry set ;
; =2 normal token ;
; =4 switch character, char in token buffer ;
; Carry Flag Set if a CR was found, Reset otherwise ;
; ;
; MODIFIES: ;
; CX, SI, AX and the Carry Flag ;
; ;
;-----------------------------------------------------------------------;
TAB equ 09h
CR equ 0dh
CPARSE:
ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING
pushf ;save flags
push di ;save the token buffer addrss
xor cx,cx ;no chars in token buffer
call kill_bl
cmp al,CR ;a CR?
jne sj2 ;no, skip
sj1:
mov ax,1 ;condition code
dec si ;adjust the pointer
pop di ;retrive token buffer address
popf ;restore flags
stc ;set the carry bit
ret
sj2:
mov dl,[SWITCHAR]
cmp al,dl ;is the char the switch char?
jne anum_char ;no, process...
call kill_bl
cmp al,CR ;a CR?
je sj1 ;yes, error exit
call move_char ;Put the switch char in the token buffer
mov ax,4 ;Flag switch
jmp short x_done2
anum_char:
call move_char ;just an alphanum string
lodsb
cmp al,' '
je x_done
cmp al,tab
je x_done
cmp al,CR
je x_done
cmp al,','
je x_done
cmp al,'='
je x_done
cmp al,dl ;Switch character
jne anum_char
x_done:
dec si ;adjust for next round
mov ax,2 ;normal token
x_done2:
push ax ;save condition code
mov al,0
stosb ;null at the end
pop ax
pop di ;restore token buffer pointer
popf
clc ;clear carry flag
ret
kill_bl proc near
lodsb
cmp al,' '
je kill_bl
cmp al,tab
je kill_bl
cmp al,',' ;a comma?
je kill_bl
cmp al,'='
je kill_bl
ret
kill_bl endp
move_char proc near
stosb ;store char in token buffer
inc cx ;increment char count
ret
move_char endp
CODE ENDS
END START