;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
��������������������������������������������������������������������������������������������������