--- /dev/null
+From: Tim Paterson
+To: Len Shustek
+Date: Mon, 16 Dec 2013 10:34:17 -0800
+Subject: RE: Source code to MS-DOS 1.0
+
+
+I have found and attached the source code for MS-DOS 1.25 as shipped by Seattle Computer Products. Version 1.25 was the first general release to OEM customers other than IBM so was used by all the first clone manufacturers.
+
+IBM's DOS 1.1 corresponds to MS-DOS 1.24. There is one minor difference between 1.24 and 1.25, as noted in the revision history at the top of MSDOS.ASM.
+
+Of the file attached, only STDDOS.ASM/MSDOS.ASM (DOS main code) and COMMAND.ASM (command processor) would have been used by an OEM other than Seattle Computer. The other files:
+
+IO.ASM - I/O system unique to SCP (equivalent to ibmbio.sys).
+ASM.ASM & HEX2BIN.ASM - Old 8086 assembler developed by SCP (used to assemble older version of DOS).
+TRANS.ASM - Z80 to 8086 assembly source code translator developed by SCP.
+
+I also have a 6\94 stack of printouts of assembly listings for some of these and probably other related programs.
+
+Tim Paterson
+Paterson Technology
+http://www.patersontech.com/
+
+
--- /dev/null
+; Seattle Computer Products 8086 Assembler version 2.44
+; by Tim Paterson
+; Runs on the 8086 under MS-DOS
+
+;* * * * * * REVISION HISTORY * * * * * *
+;
+; 12/29/80 2.01 General release with 86-DOS version 0.34
+; 02/22/81 2.10 Increased buffer size from 128 bytes to 1024 bytes
+; 03/18/81 2.11 General cleanup and more documentation
+; 03/24/81 2.20 Modify ESC handling for full 8087 operation
+; 04/01/81 2.21 Fix date in HEX and PRN files; modify buffer handling
+; 04/03/81 2.22 Fix 2.21 buffer handling
+; 04/13/81 2.23 Re-open source file for listing to allow assembling CON:
+; 04/28/81 2.24 Allow nested IFs
+; 07/30/81 2.25 Add Intel string mnemonics; clean up a little
+; 08/02/81 2.30 Re-write pass 2:
+; Always report errors to console
+; Exact byte lengths for HEX and PRN files
+; 11/08/81 2.40 Add 8087 mnemonics; print full error messages;
+; allow expressions with *, /, and ()
+; 07/04/82 2.41 Fix Intel's 8087 "reverse-bit" bug; don't copy date
+; 08/18/82 2.42 Increase stack from 80 to 256 (Damn! Overflowed again!)
+; 01/05/83 2.43 Correct over-zealous optimization in 2.42
+; 05/09/83 2.44 Add memory usage report
+;
+;* * * * * * * * * * * * * * * * * * * * *
+
+SYMWID: EQU 5 ;5 symbols per line in dump
+FCB: EQU 5CH
+BUFSIZ: EQU 1024 ;Source code buffer
+LSTBUFSIZ:EQU BUFSIZ ;List file buffer
+HEXBUFSIZ:EQU 70 ;Hex file buffer (26*2 + 5*2 + 3 + EXTRA)
+EOL: EQU 13 ;ASCII carriage return
+OBJECT: EQU 100H ;DEFAULT "PUT" ADDRESS
+
+;System call function codes
+PRINTMES: EQU 9
+OPEN: EQU 15
+CLOSE: EQU 16
+READ: EQU 20
+SETDMA: EQU 26
+MAKE: EQU 22
+BLKWRT: EQU 40
+
+;The following equates define some token values returned by GETSYM
+UNDEFID:EQU 0 ;Undefined identifier (including no nearby RET)
+CONST: EQU 1 ;Constant (including $)
+REG: EQU 2 ;8-bit register
+XREG: EQU 3 ;16-bit register (except segment registers)
+SREG: EQU 4 ;Segment register
+FREG: EQU 6 ;8087 floating point register
+
+;Bits to build 8087 opcode table entries
+ONEREG: EQU 40H ;Single ST register OK as operand
+NEEDOP: EQU 80H ;Must have an operand
+INTEGER:EQU 20H ;For integer operations
+REAL: EQU 28H ;For real operations
+EXTENDED EQU 10H ;For Long integers or Temporary real
+MEMORY: EQU 18H ;For general memory operations
+STACKOP:EQU 10H ;Two register arithmetic with pop
+ARITH: EQU 8 ;Non-pop arithmetic operations
+
+ ORG 100H
+ PUT 100H
+
+ JMPS BEGIN
+
+HEADER: DB 13,10,'Seattle Computer Products 8086 Assembler Version 2.44'
+ DB 13,10,'Copyright 1979-1983 by Seattle Computer Products, Inc.'
+ DB 13,10,13,10,'$'
+
+BEGIN:
+ MOV SP,STACK
+ MOV DX,HEADER
+ MOV AH,PRINTMES
+ INT 33
+ MOV AL,[FCB+17]
+ MOV [SYMFLG],AL ;Save symbol table request flag
+ MOV SI,FCB+9 ;Point to file extension
+ LODB ;Get source drive letter
+ CALL CHKDSK ;Valid drive?
+ OR AL,AL
+ JZ DEFAULT ;If no extension, use existing drive spec
+ MOV [FCB],AL
+DEFAULT:
+ LODB ;Get HEX file drive letter
+ CMP AL,'Z' ;Suppress HEX file?
+ JZ L0000
+ CALL CHKDSK
+L0000:
+ MOV [HEXFCB],AL
+ LODB ;Get PRN file drive letter
+ MOV AH,0 ;Signal no PRN file
+ CMP AL,'Z' ;Suppress PRN file?
+ JZ NOPRN
+ CMP AL,'Y' ;Print errors only on console?
+ JZ NOPRN
+ MOV AH,2
+ CMP AL,'X' ;PRN file to console?
+ JZ NOPRN
+ MOV AH,4
+ CMP AL,'P' ;PRN file to printer?
+ JZ NOPRN
+ CALL CHKDSK
+ MOV AH,80H
+NOPRN:
+ MOV [LSTFCB],AL
+ MOV [LSTDEV],AH ;Flag device for list ouput
+ MOV SI,EXTEND
+ MOV DI,FCB+9
+ MOVW
+ MOVB ;Set extension to ASM
+ MOVW ;Zero extent field
+ MOV DX,FCB
+ MOV AH,OPEN
+ INT 33
+ MOV BX,NOFILE
+ OR AL,AL
+ JZ $+5
+ JMP PRERR
+ MOV DX,HEXFCB
+ CALL MAKFIL
+ MOV DX,LSTFCB
+ CALL MAKFIL
+ XOR AX,AX
+ MOV [FCB+12],AX ;Zero CURRENT BLOCK field
+ MOV [FCB+32],AL ;Zero Next Record field
+ MOV [FCB+14],BUFSIZ ;Set record size
+ MOV [BUFPT],SRCBUF ;Initialize buffer pointer
+ MOV [CODE],START+1 ;POINTER TO NEXT BYTE OF INTERMEDIATE CODE
+ MOV [IY],START ;POINTER TO CURRENT RELOCATION BYTE
+ XOR AX,AX
+ MOV [PC],AX ;DEFAULT PROGRAM COUNTER
+ MOV [BASE],AX ;POINTER TO ROOT OF ID TREE=NIL
+ MOV [RETPT],AX ;Pointer to last RET record
+ MOV [IFFLG],AL ;NOT WITHIN IF/ENDIF
+ MOV [CHKLAB],AL ;LOOKUP ALL LABELS
+ DEC AX
+ MOV [LSTRET],AX ;Location of last RET
+ MOV AX,[6] ;HL=END OF MEMORY
+ MOV [HEAP],AX ;BACK END OF SYMBOL TABLE SPACE
+ MOV [BCOUNT],4 ;CODE BYTES PER RELOCATION BYTE
+
+;Assemble each line of code
+
+LOOP:
+ CALL NEXTCHR ;Get first character on line
+ CMP AL,1AH
+ JZ ENDJ
+ MOV AL,-1 ;Flag that no tokens have been read yet
+ MOV [SYM],AL
+ CALL ASMLIN ;Assemble the line
+ MOV AL,[SYM]
+ CMP AL,-1 ;Any tokens found on line?
+ JNZ L0002
+ CALL GETSYM ;If no tokens read yet, read first one
+L0002:
+ CMP AL,';'
+ JZ ENDLN
+ CMP AL,EOL
+ JZ ENDLN
+ MOV AL,14H ;Garbage at end of line error
+ JP ENDLIN
+ENDJ: JMP END
+
+ENDLN:
+ XOR AL,AL ;Flag no errors on line
+ENDLIN:
+;AL = error code for line. Stack depth unknown
+ MOV SP,STACK
+ CALL NEXLIN
+ JP LOOP
+
+NEXLIN:
+ MOV CH,0C0H ;Put end of line marker and error code (AL)
+ CALL PUTCD
+ CALL GEN1
+ MOV AL,[CHR]
+GETEOL:
+ CMP AL,10
+ JZ RET
+ CMP AL,1AH
+ JZ ENDJ
+ CALL NEXTCHR ;Scan over comments for linefeed
+ JP GETEOL
+
+ABORT:
+ MOV BX,NOMEM
+PRERR:
+ MOV DX,BX
+ MOV AH,PRINTMES
+ INT 33
+ INT 32
+
+MAKFIL:
+ MOV SI,DX
+ LODB ;Get drive select byte
+ CMP AL,20H ;If not valid, don't make file
+ JNC RET
+ MOV CX,4
+ MOV DI,SI
+ MOV SI,FCB+1
+ REP
+ MOVW ;Copy source file name
+ MOV AH,MAKE
+ INT 33
+ MOV [DI-9+14],1 ;Set record length to 1 byte
+ MOV BX,NOSPAC
+ OR AL,AL ;Success?
+ JNZ PRERR
+ RET
+
+CHKDSK:
+ SUB AL,' ' ;If not present, set zero flag
+ JZ RET
+ SUB AL,20H
+ JZ DSKERR ;Must be in range A-O
+ CMP AL,'P'-'@'
+ JC RET
+DSKERR:
+ MOV BX,BADDSK
+ JP PRERR
+
+ERROR:
+ MOV AL,CL
+ JMP ENDLIN
+
+NEXTCHR:
+ MOV SI,[BUFPT]
+ CMP SI,SRCBUF
+ JNZ GETCH
+;Buffer empty so refill it
+ PUSH DX
+ PUSH AX ;AH must be saved
+ MOV DX,SI
+ MOV AH,SETDMA
+ INT 33
+ MOV DX,FCB
+ MOV AH,READ
+ INT 33
+ XCHG AX,DX ;Put error code in DL
+ POP AX ;Restore AH
+ MOV AL,DL ;Error code back in AL
+ POP DX
+ CMP AL,1
+ MOV AL,1AH ;Possibly signal End of File
+ JZ NOMOD ;If nothing read
+GETCH:
+ LODB
+ CMP SI,SRCBUF+BUFSIZ
+ JNZ NOMOD
+ MOV SI,SRCBUF
+NOMOD:
+ MOV [BUFPT],SI
+ MOV [CHR],AL
+ RET
+
+
+MROPS:
+
+; Get two operands and check for certain types, according to flag byte
+; in CL. OP code in CH. Returns only if immediate operation.
+
+ PUSH CX ;Save type flags
+ CALL GETOP
+ PUSH DX ;Save first operand
+ CALL GETOP2
+ POP BX ;First op in BX, second op in DX
+ MOV AL,SREG ;Check for a segment register
+ CMP AL,BH
+ JZ SEGCHK
+ CMP AL,DH
+ JZ SEGCHK
+ MOV AL,CONST ;Check if the first operand is immediate
+ MOV CL,26
+ CMP AL,BH
+ JZ ERROR ;Error if so
+ POP CX ;Restore type flags
+ CMP AL,DH ;If second operand is immediate, then done
+ JZ RET
+ MOV AL,UNDEFID ;Check for memory reference
+ CMP AL,BH
+ JZ STORE ;Is destination memory?
+ CMP AL,DH
+ JZ LOAD ;Is source memory?
+ TEST CL,1 ;Check if register-to-register operation OK
+ MOV CL,27
+ JZ ERROR
+ MOV AL,DH
+ CMP AL,BH ;Registers must be of same length
+RR:
+ MOV CL,22
+ JNZ ERROR
+RR1:
+ AND AL,1 ;Get register length (1=16 bits)
+ OR AL,CH ;Or in to OP code
+ CALL PUT ;And write it
+ POP CX ;Dump return address
+ MOV AL,BL
+ ADD AL,AL ;Rotate register number into middle position
+ ADD AL,AL
+ ADD AL,AL
+ OR AL,0C0H ;Set register-to-register mode
+ OR AL,DL ;Combine with other register number
+ JMP PUT
+
+SEGCHK:
+;Come here if at least one operand is a segment register
+ POP CX ;Restore flags
+ TEST CL,8 ;Check if segment register OK
+ MOV CL,22
+ JZ ERR1
+ MOV CX,8E03H ;Segment register move OP code
+ MOV AL,UNDEFID
+ CMP AL,DH ;Check if source is memory
+ JZ LOAD
+ CMP AL,BH ;Check if destination is memory
+ JZ STORE
+ MOV AL,XREG
+ SUB AL,DH ;Check if source is 16-bit register
+ JZ RR ;If so, AL must be zero
+ MOV CH,8CH ;Change direction
+ XCHG DX,BX ;Flip which operand is first and second
+ MOV AL,XREG
+ SUB AL,DH ;Let RR perform finish the test
+ JP RR
+
+STORE:
+ TEST CL,004H ;Check if storing is OK
+ JNZ STERR
+ XCHG DX,BX ;If so, flip operands
+ AND CH,0FDH ; and zero direction bit
+LOAD:
+ MOV DH,25
+ CMP AL,BH ;Check if memory-to-memory
+ JZ MRERR
+ MOV AL,BH
+ CMP AL,REG ;Check if 8-bit operation
+ JNZ XRG
+ MOV DH,22
+ TEST CL,1 ;See if 8-bit operation is OK
+ JZ MRERR
+XRG:
+ MOV AL,DL
+ SUB AL,6 ;Check for R/M mode 6 and register 0
+ OR AL,BL ; meaning direct load/store of accumulator
+ JNZ NOTAC
+ TEST CL,8 ;See if direct load/store of accumulator
+ JZ NOTAC ; means anything in this case
+; Process direct load/store of accumulator
+ MOV AL,CH
+ AND AL,2 ;Preserve direction bit only
+ XOR AL,2 ; but flip it
+ OR AL,0A0H ;Combine with OP code
+ MOV CH,AL
+ MOV AL,BH ;Check byte/word operation
+ AND AL,1
+ OR AL,CH
+ POP CX ;Dump return address
+ JMP PUTADD ;Write the address
+
+NOTAC:
+ MOV AL,BH
+ AND AL,1 ;Get byte/word bit
+ AND AL,CL ;But don't use it in word-only operations
+ OR AL,CH ;Combine with OP code
+ CALL PUT
+ MOV AL,BL
+ ADD AL,AL ;Rotate to middle position
+ ADD AL,AL
+ ADD AL,AL
+ OR AL,DL ;Combine register field
+ POP CX ;Dump return address
+ JMP PUTADD ;Write the address
+
+STERR:
+ MOV DH,29
+MRERR:
+ MOV CL,DH
+
+ERR1: JMP ERROR
+
+GETOP2:
+;Get the second operand: look for a comma and drop into GETOP
+ MOV AL,[SYM]
+ CMP AL,','
+ MOV CL,21
+ JNZ ERR1
+
+
+GETOP:
+
+; Get one operand. Operand may be a memory reference in brackets, a register,
+; or a constant. If a flag (such as "B" for byte operation) is encountered,
+; it is noted and processing continues to find the operand.
+;
+; On exit, AL (=DH) has the type of operand. Other information depends
+; on the actual operand:
+;
+; AL=DH=0 Memory Reference. DL has the address mode properly prepared in
+; the 8086 R/M format (middle bits zero). The constant part of the address
+; is in ADDR. If an undefined label needs to be added to this, a pointer to
+; its information fields is in ALABEL, otherwise ALABEL is zero.
+;
+; AL=DH=1 Value. The constant part is in DATA. If an undefined label needs
+; to be added to this, a pointer to its information fields is in DLABEL,
+; otherwise DLABEL is zero. "$" and "RET" are in this class.
+;
+; AL=DH=2 8-bit Register. DL has the register number.
+;
+; AL=DH=3 16-bit Register. DL has the register number.
+;
+; AL=DH=4 Segment Register. DL has the register number.
+
+ CALL GETSYM
+GETOP1:
+;Enter here if we don't need a GETSYM first
+ CMP AL,'[' ;Memory reference?
+ JZ MEM
+ CMP AL,5 ;Flag ("B", "W", etc.)?
+ JZ FLG
+ CMP AL,REG ;8-Bit register?
+ JZ NREG
+ CMP AL,XREG ;16-Bit register?
+ JZ NREG
+ CMP AL,SREG ;Segment register?
+ JZ NREG
+VAL: ;Must be immediate
+ XOR AL,AL ;No addressing modes allowed
+VAL1:
+ CALL GETVAL
+ MOV AX,[CON] ;Defined part
+ MOV [DATA],AX
+ MOV AX,[UNDEF] ;Undefined part
+ MOV [DLABEL],AX
+ MOV DL,CH
+ MOV DH,CONST
+ MOV AL,DH
+ RET
+NREG:
+ PUSH DX
+ CALL GETSYM
+ POP DX
+ MOV AL,DH
+ RET
+MEM:
+ CALL GETSYM
+ MOV AL,1
+ CALL GETVAL
+ MOV AL,[SYM]
+ CMP AL,']'
+ MOV CL,24
+ JNZ ERR1
+ CALL GETSYM
+ MOV BX,[CON]
+ MOV [ADDR],BX
+ MOV BX,[UNDEF]
+ MOV [ALABEL],BX
+ MOV DL,CH
+ MOV DH,UNDEFID
+ MOV AL,DH
+ RET
+FLG:
+ CMP DL,[MAXFLG] ;Invalid flag for this operation?
+ MOV CL,27H
+ JG ERR1
+ CALL GETSYM
+ CMP AL,','
+ JZ GETOP
+ JP GETOP1
+
+
+GETVAL:
+
+; Expression analyzer. On entry, if AL=0 then do not allow base or index
+; registers. If AL=1, we are analyzing a memory reference, so allow base
+; and index registers, and compute addressing mode when done. The constant
+; part of the expression will be found in CON. If an undefined label is to
+; be added to this, a pointer to its information fields will be found in
+; UNDEF.
+
+ MOV AH,AL ;Flag is kept in AH
+ MOV [UNDEF],0
+ MOV AL,[SYM]
+ CALL EXPRESSION
+ MOV [CON],DX
+ MOV AL,AH
+ MOV CH,0 ;Initial mode
+ TEST AL,10H ;Test INDEX bit
+ RCL AL ;BASE bit (zero flag not affected)
+ JZ NOIND ;Jump if not indexed, with BASE bit in carry
+ CMC
+ RCL CH ;Rotate in BASE bit
+ RCL AL ;BP bit
+ RCL CH
+ RCL AL ;DI bit
+ RCL CH ;The low 3 bits now have indexing mode
+MODE:
+ OR CH,080H ;If undefined label, force 16-bit displacement
+ TEST [UNDEF],-1
+ JNZ RET
+ MOV BX,[CON]
+ MOV AL,BL
+ CBW ;Extend sign
+ CMP AX,BX ;Is it a signed 8-bit number?
+ JNZ RET ;If not, use 16-bit displacement
+ AND CH,07FH ;Reset 16-bit displacement
+ OR CH,040H ;Set 8-bit displacement
+ OR BX,BX
+ JNZ RET ;Use it if not zero displacement
+ AND CH,7 ;Specify no displacement
+ CMP CH,6 ;Check for BP+0 addressing mode
+ JNZ RET
+ OR CH,040H ;If BP+0, use 8-bit displacement
+ RET
+
+NOIND:
+ MOV CH,6 ;Try direct address mode
+ JNC RET ;If no base register, that's right
+ RCL AL ;Check BP bit
+ JC MODE
+ INC CH ;If not, must be BX
+ JP MODE
+
+EXPRESSION:
+;Analyze arbitrary expression. Flag byte in AH.
+;On exit, AL has type byte: 0=register or undefined label
+ MOV CH,-1 ;Initial type
+ MOV DI,DX
+ XOR DX,DX ;Initial value
+ CMP AL,'+'
+ JZ PLSMNS
+ CMP AL,'-'
+ JZ PLSMNS
+ MOV CL,'+'
+ PUSH DX
+ PUSH CX
+ MOV DX,DI
+ JP OPERATE
+PLSMNS:
+ MOV CL,AL
+ PUSH DX
+ PUSH CX
+ OR AH,4 ;Flag that a sign was found
+ CALL GETSYM
+OPERATE:
+ CALL TERM
+ POP CX ;Recover operator
+ POP BX ;Recover current value
+ XCHG DX,BX
+ AND CH,AL
+ OR AL,AL ;Is it register or undefined label?
+ JZ NOCON ;If so, then no constant part
+ CMP CL,"-" ;Subtract it?
+ JNZ ADD
+ NEG BX
+ADD:
+ ADD DX,BX
+NEXTERM:
+ MOV AL,[SYM]
+ CMP AL,'+'
+ JZ PLSMNS
+ CMP AL,'-'
+ JZ PLSMNS
+ MOV AL,CH
+ RET
+NOCON:
+ CMP CL,"-"
+ JNZ NEXTERM
+BADOP:
+ MOV CL,5
+ JMP ERROR
+
+TERM:
+ CALL FACTOR
+MULOP:
+ PUSH DX ;Save value
+ PUSH AX ;Save type
+ CALL GETSYM
+ POP CX
+ CMP AL,"*"
+ JZ GETFACT
+ CMP AL,"/"
+ JNZ ENDTERM
+GETFACT:
+ OR CL,CL ;Can we operate on this type?
+ JZ BADOP
+ PUSH AX ;Save operator
+ CALL GETSYM ;Get past operator
+ CALL FACTOR
+ OR AL,AL
+ JZ BADOP
+ POP CX ;Recover operator
+ POP BP ;And current value
+ XCHG AX,BP ;Save AH in BP
+ CMP CL,"/" ;Do we divide?
+ JNZ DOMUL
+ OR DX,DX ;Dividing by zero?
+ MOV CL,29H
+ JZ ERR2
+ MOV BX,DX
+ XOR DX,DX ;Make 32-bit dividend
+ DIV AX,BX
+ JMPS NEXFACT
+DOMUL:
+ MUL AX,DX
+NEXFACT:
+ MOV DX,AX ;Result in DX
+ XCHG AX,BP ;Restore flags to AH
+ MOV AL,-1 ;Indicate a number
+ JMPS MULOP
+ENDTERM:
+ POP DX
+ MOV AL,CL
+ RET
+
+FACTOR:
+ MOV AL,[SYM]
+ CMP AL,CONST
+ JZ RET
+ CMP AL,UNDEFID
+ JZ UVAL
+ CMP AL,"("
+ JZ PAREN
+ CMP AL,'"'
+ JZ STRING
+ CMP AL,"'"
+ JZ STRING
+ CMP AL,XREG ;Only 16-bit register may index
+ MOV CL,20
+ JNZ ERR2
+ TEST AH,1 ;Check to see if indexing is OK
+ MOV CL,1
+ JZ ERR2
+ MOV AL,DL
+ MOV CL,3
+ SUB AL,3 ;Check for BX
+ JZ BXJ
+ SUB AL,2 ;Check for BP
+ JZ BPJ
+ DEC AL ;Check for SI
+ MOV CL,4
+ JZ SIJ
+ DEC AL ;Check for DI
+ JZ DIJ
+ MOV CL,2 ;Invalid base/index register
+ERR2: JMP ERROR
+
+DIJ:
+ OR AH,20H ;Flag seeing index register DI
+SIJ:
+ TEST AH,10H ;Check if already seen index register
+ JNZ ERR2
+ OR AH,10H ;Flag seeing index register
+ RET
+
+BPJ:
+ OR AH,40H ;Flag seeing base register BP
+BXJ:
+ TEST AH,80H ;Check if already seen base register
+ JNZ ERR2
+ OR AH,80H ;Flag seeing base register
+ RET
+
+PAREN:
+ CALL GETSYM ;Eat the "("
+ CALL EXPRESSION
+ CMP B,[SYM],")" ;Better have closing paren
+ MOV CL,20
+ JNZ ERR30
+ RET
+
+UVAL:
+ MOV CL,6
+ TEST AH,8 ;Check if undefined label has been seen
+ JNZ ERR30
+ OR AH,8 ;Flag seeing undefined label
+ MOV [UNDEF],BX
+ RET
+
+ERR30: JMP ERROR
+
+STRING:
+ MOV CH,AL
+ MOV AL,[CHR]
+ CMP AL,CH
+ MOV CL,35
+ MOV DL,AL
+ MOV DH,0
+ JNZ L0003
+ CALL ZERLEN
+L0003:
+ CALL GETCHR
+ MOV CL,37
+ TEST AH,2
+ JZ ERR30
+ TEST AH,4
+ MOV CL,38
+ JNZ ERR30
+STRGDAT:
+ MOV AL,DL
+ CMP AL,EOL
+ MOV CL,39
+ JZ ERR30
+ CALL PUT
+ MOV AL,[DATSIZ]
+ OR AL,AL
+ JNZ BYTSIZ
+ MOV AL,DH
+ CALL PUT
+BYTSIZ:
+ MOV AL,[CHR]
+ MOV DL,AL
+ CALL GETCHR
+ JP STRGDAT
+
+ZERLEN:
+ CALL NEXTCHR
+ CMP AL,CH
+ JNZ ERR30
+ RET
+
+GETCHR:
+ CALL NEXTCHR
+ CMP AL,CH
+ JNZ RET
+ CALL NEXTCHR
+ CMP AL,CH
+ JZ RET
+ POP BX ;Kill return address to STRGDAT loop
+ MOV AL,-1 ;Flag type as constant
+ RET
+
+
+GETSYM:
+
+; The lexical scanner. Used only in the operand field. Returns with the token
+; in SYM and AL, sometimes with additional info in BX or DX.
+;
+; AL=SYM=0 Undefined label. BX has pointer to information fields.
+;
+; AL=SYM=1 Constant (or defined label). DX has value.
+;
+; AL=SYM=2,3,4 8-bit register, 16-bit register, or segment register,
+; respectively. DL has register number.
+;
+; AL=SYM=5 A mode flag (such as "B" for byte operation). Type of flag in DL
+; and also stored in FLAG: -1=no flags, 0=B, 1=W, 2=S, 3=L, 4=T.
+;
+; AL=SYM=6 8087 floating point register, ST(n) or ST. DL has register number.
+;
+; All other values are the ASCII code of the character. Note that this may
+; never be a letter or number.
+
+ PUSH AX ;Save AH
+ CALL GETSY
+ POP AX
+ MOV AL,[SYM]
+ RET
+
+SCANB:
+ MOV AL,[CHR]
+SCANT:
+ CMP AL,' '
+ JZ NEXB
+ CMP AL,9
+ JNZ RET
+NEXB:
+ CALL NEXTCHR
+ JP SCANT
+
+DOLLAR:
+ MOV DX,[OLDPC]
+ MOV AL,CONST
+ MOV [SYM],AL
+NEXTCHJ:
+ JMP NEXTCHR
+
+GETSY:
+ CALL SCANB
+ CMP AL,'$'
+ JZ DOLLAR
+ MOV [SYM],AL
+ OR AL,20H
+ CMP AL,'z'+1
+ JNC NEXTCHJ
+ CMP AL,'a'
+ JC $+5
+ JMP LETTER
+ CMP AL,'9'+1
+ JNC NEXTCHJ
+ CMP AL,'0'
+ JC NEXTCHJ
+ MOV BX,SYM
+ MOV B,[BX],CONST
+ CALL READID
+ DEC BX
+ MOV AL,[BX]
+ MOV CL,7
+ MOV BX,0
+ CMP AL,'h'
+ JNZ $+5
+ JMP HEX
+ INC CL
+ MOV [IX],ID
+DEC:
+ MOV SI,[IX]
+ MOV AL,[SI]
+ INC [IX]
+ CMP AL,'9'+1
+ JC $+5
+ JMP ERROR
+ SUB AL,'0'
+ MOV DX,BX
+ SHL BX
+ SHL BX
+ ADD BX,DX
+ SHL BX
+ MOV DL,AL
+ MOV DH,0
+ ADD BX,DX
+ DEC CH
+ JNZ DEC
+ XCHG DX,BX
+ RET
+
+HEX:
+ MOV DX,ID
+ DEC CH
+HEX1:
+ MOV SI,DX
+ LODB
+ INC DX
+ SUB AL,'0'
+ CMP AL,10
+ JC GOTIT
+ CMP AL,'g'-'0'
+ JNC ERR4
+ SUB AL,'a'-10-'0'
+GOTIT:
+ SHL BX
+ SHL BX
+ SHL BX
+ SHL BX
+ ADD BL,AL
+ DEC CH
+ JNZ HEX1
+ XCHG DX,BX
+ RET
+
+ERR4: JMP ERROR
+
+GETLET:
+ CALL SCANB
+ CMP AL,EOL
+ STC
+ JZ RET
+ CMP AL,';'
+ STC
+ JZ RET
+ MOV CL,10
+ OR AL,20H
+ CMP AL,'a'
+ JC ERR4
+ CMP AL,'z'+1
+ JNC ERR4
+READID:
+ MOV BX,ID
+ MOV CH,0
+MOREID:
+ MOV [BX],AL
+ INC CH
+ INC BX
+ CALL NEXTCHR
+ CMP AL,'0'
+ JC NOMORE
+ OR AL,20H
+ CMP AL,'z'+1
+ JNC NOMORE
+ CMP AL,'9'+1
+ JC MOREID
+ CMP AL,'a'
+ JNC MOREID
+NOMORE:
+ MOV CL,AL
+ MOV AL,CH
+ MOV [LENID],AL
+ OR AL,AL
+ MOV AL,CL
+ RET
+
+LETTER:
+ CALL READID
+ MOV AL,CH
+ DEC AL
+ JNZ NOFLG
+ MOV AL,[ID]
+ MOV CX,5
+ MOV DI,FLGTAB
+ UP
+ REPNE
+ SCAB ;See if one of B,W,S,L,T
+ JZ SAVFLG ;Go save flag
+ XOR AL,AL
+ MOV CH,[LENID]
+NOFLG:
+ DEC AL
+ PUSH BX
+ JNZ L0004
+ CALL REGCHK
+L0004:
+ POP BX
+ MOV AL,DH
+ JZ SYMSAV
+ CALL LOOKRET
+SYMSAV:
+ MOV [SYM],AL
+ RET
+
+SAVFLG:
+ MOV DL,CL ;Need flag type in DL
+ XCHG [FLAG],CL
+ CMP CL,-1
+ MOV CL,32
+ MOV AL,5
+ JZ SYMSAV
+ERRJ3: JMP ERROR
+
+FLGTAB: DB "tlswb"
+
+FPREG:
+;Have detected "ST" for 8087 floating point stack register
+ MOV DL,0 ;Default is ST(0)
+ CALL SCANB ;Get next character
+ CMP AL,"(" ;Specifying register number?
+ JNZ HAVREG
+;Get register number
+ CALL NEXTCHR ;Skip over the "("
+ CALL GETOP ;A little recursion never hurt anybody
+ CMP AL,CONST ;Better have found a constant
+ MOV CL,20 ;Operand error if not
+ JNZ ERRJ3
+ CMP [DLABEL],0 ;Constant must be defined
+ MOV CL,30
+ JNZ ERRJ3
+ MOV DX,[DATA] ;Get constant
+ CMP DX,7 ;Constant must be in range 0-7
+ MOV CL,31
+ JA ERRJ3
+ MOV AL,[SYM]
+ CMP AL,")"
+ MOV CL,24
+ JNZ ERRJ3
+HAVREG:
+ MOV DH,FREG
+ XOR AL,AL ;Zero set means register found
+ RET
+
+REGCHK:
+ MOV BX,ID
+ CMP [BX],"s"+7400H ;"st"
+ JZ FPREG
+ MOV CL,[BX]
+ INC BX
+ MOV AL,[BX]
+ MOV BX,REGTAB
+ MOV DH,XREG
+ MOV DL,0
+ CMP AL,'x'
+ JZ SCANREG
+ MOV DH,REG
+ CMP AL,'l'
+ JZ SCANREG
+ MOV DL,4
+ CMP AL,'h'
+ JZ SCANREG
+ MOV DH,SREG
+ MOV DL,0
+ MOV BX,SEGTAB
+ CMP AL,'s'
+ JZ SCANREG
+ MOV DH,XREG
+ CMP AL,'p'
+ JZ PREG
+ CMP AL,'i'
+ JNZ RET
+ MOV DL,6
+ MOV AL,CL
+ CMP AL,'s'
+ JZ RET
+ INC DL
+ CMP AL,'d'
+ RET
+PREG:
+ MOV DL,4
+ MOV AL,CL
+ CMP AL,'s'
+ JZ RET
+ INC DL
+ CMP AL,'b'
+ RET
+SCANREG:
+ MOV AL,CL
+ MOV CX,4
+ UP
+ MOV DI,BX
+ REPNZ
+ SCAB
+ MOV BX,DI
+ JNZ RET
+ MOV AL,CL
+ ADD AL,DL
+ MOV DL,AL
+ XOR AL,AL
+ RET
+
+REGTAB: DB 'bdca'
+
+SEGTAB: DB 'dsce'
+
+LOOK:
+ MOV CH,[BX]
+ INC BX
+ MOV DX,ID
+ CALL CPSLP
+ JZ RET
+ XOR AL,80H
+ ROL AL ;Make end-of-symbol bit least significant
+ MOV CL,AL
+ DEC BX
+ MOV AL,[BX]
+ XOR AL,80H
+ ROL AL
+ CMP AL,CL
+ JNC SMALL
+ INC CH
+ INC CH
+SMALL:
+ MOV DL,CH
+ MOV DH,0
+ ADD BX,DX
+ MOV DX,[BX]
+ INC BX
+ MOV AL,DL
+ OR AL,DH
+ STC
+ JZ RET
+ XCHG DX,BX
+ JP LOOK
+
+LOOKRET:
+ MOV AL,CH
+ CMP AL,3 ;RET has 3 letters
+ JNZ LOOKUP
+ DEC BX
+ OR B,[BX],080H
+ MOV DX,RETSTR+2
+CHKRET:
+ MOV SI,DX
+ LODB
+ CMP AL,[BX]
+ JNZ LOOKIT
+ DEC BX
+ DEC DX
+ DEC CH
+ JNZ CHKRET
+ MOV DX,[LSTRET]
+ MOV AL,DL
+ AND AL,DH
+ INC AL
+ JZ ALLRET
+ MOV BX,[PC]
+ SUB BX,DX
+ MOV AL,BL
+ CBW
+ CMP AX,BX ;Signed 8-bit number?
+ MOV AL,1
+ JZ RET
+ALLRET:
+ MOV BX,[RETPT]
+ MOV AL,BH
+ OR AL,BL
+ MOV AL,0
+ JNZ RET
+ MOV BX,[HEAP]
+ DEC BX
+ DEC BX
+ DEC BX
+ MOV [HEAP],BX
+ XOR AL,AL
+ MOV [BX],AL
+ MOV [RETPT],BX
+ RET
+
+LOOKUP:
+ DEC BX
+ OR B,[BX],080H
+LOOKIT:
+ MOV BX,[BASE]
+ MOV AL,BH
+ OR AL,BL
+ JZ EMPTY
+ CALL LOOK
+ JC ENTER
+ MOV DX,4
+ ADD BX,DX
+ MOV AL,[BX]
+ OR AL,AL
+ JZ RET
+ INC BX
+ MOV DX,[BX]
+ INC BX
+ RET
+
+ENTER:
+ PUSH BX ;Save pointer to link field
+ CALL CREATE ;Add the node
+ POP SI
+ MOV [SI-1],DX ;Link new node
+ RET ;Zero was set by CREATE
+
+EMPTY:
+ CALL CREATE
+ MOV [BASE],DX
+ RET
+
+
+CREATE:
+
+; Add a new node to the identifier tree. The identifier is at ID with
+; bit 7 of the last character set to one. The length of the identifier is
+; in LENID, which is ID-1.
+;
+; Node format:
+; 1. Length of identifier (1 byte)
+; 2. Identifier (1-80 bytes)
+; 3. Left link (2-byte pointer to alphabetically smaller identifiers)
+; 4. Right link (0 if none larger)
+; 5. Data field:
+; a. Defined flag (0=undefined, 1=defined)
+; b. Value (2 bytes)
+;
+; This routine returns with AL=zero and zero flag set (which indicates
+; on return from LOOKUP that it has not yet been defined), DX points
+; to start of new node, and BX points to data field of new node.
+
+ MOV AL,[LENID]
+ ADD AL,8 ;Storage needed for the node
+ MOV BX,[HEAP]
+ MOV DL,AL
+ MOV DH,0
+ SUB BX,DX ;Heap grows downward
+ MOV [HEAP],BX
+ XCHG DX,BX
+ MOV BX,[CODE] ;Check to make sure there's enough
+ CMP BX,DX
+ JB $+5
+ JMP ABORT
+ PUSH DX
+ MOV BX,LENID
+ MOV CL,[BX]
+ INC CL
+ MOV CH,0
+ UP
+ MOV SI,BX
+ MOV DI,DX
+ REP
+ MOVB ;Move identifier and length into node
+ MOV DX,DI
+ MOV BX,SI
+ MOV CH,4
+ XCHG DX,BX
+NILIFY:
+ MOV [BX],CL ;Zero left and right links
+ INC BX
+ DEC CH
+ JNZ NILIFY
+ XOR AL,AL ;Set zero flag
+ MOV [BX],AL ;Zero defined flag
+ POP DX ;Restore pointer to node
+ RET
+
+CPSLP:
+ MOV SI,DX
+ LODB
+ CMP AL,[BX]
+ LAHF
+ INC DX
+ INC BX
+ SAHF
+ JNZ RET
+ DEC CH
+ JNZ CPSLP
+ RET
+
+GETLAB:
+ MOV BX,0
+ MOV [LABPT],BX
+ MOV B,[FLAG],-1
+ MOV DH,0
+ MOV AL,[CHR]
+ CMP AL,' '+1
+ JC NOT1
+ OR DH,001H
+NOT1:
+ CALL GETLET
+ JC RET
+ CMP AL,':'
+ JNZ LABCHK
+ CALL NEXTCHR
+ JP LABEL
+LABCHK:
+ OR AL,AL
+ TEST DH,001H
+ JZ RET
+LABEL:
+ MOV AL,[CHKLAB]
+ OR AL,AL
+ JZ $+5
+ JMP GETLET
+ CALL LOOKUP
+ MOV CL,11
+ JNZ ERR5
+ MOV DX,[PC]
+ MOV B,[BX],1
+ INC BX
+ MOV [BX],DX
+ MOV [LABPT],BX
+ JMP GETLET
+
+ERR5: JMP ERROR
+
+ASMLIN:
+ MOV B,[MAXFLG],1 ;Allow only B and W flags normally
+ MOV BX,[PC]
+ MOV [OLDPC],BX
+ CALL GETLAB
+ JNC $+5
+ JMP ENDLN
+ MOV BX,LENID
+ MOV AL,[BX]
+ MOV CL,12
+ SUB AL,2
+ MOV CH,AL
+ JC ERR5
+ INC BX
+ CMP B,[BX],"f" ;See if an 8087 mnemonic
+ JZ NDPOP
+ CMP AL,5
+ JNC ERR5
+ MOV AL,[BX]
+ SUB AL,'a'
+ MOV CL,AL
+ ADD AL,AL
+ ADD AL,AL
+ ADD AL,CL
+ ADD AL,CH
+ ADD AL,AL
+ MOV BX,OPTAB
+ MOV DL,AL
+ MOV DH,0
+ ADD BX,DX
+ MOV BX,[BX]
+ INC CH
+ MOV CL,CH
+ MOV AH,[BX]
+ INC BX
+ OR AH,AH
+ JZ OPERR
+FINDOP:
+ MOV CH,CL
+ MOV DX,ID+1
+ XCHG AX,BP ;Save count of opcodes in BP
+ CALL CPSLP
+ JZ HAVOP
+ XCHG AX,BP
+ MOV DH,0
+ MOV DL,CH
+ INC DX
+ INC DX
+ ADD BX,DX
+ DEC AH
+ JNZ FINDOP
+OPERR:
+ MOV CL,12
+ JMP ERROR
+
+HAVOP:
+ MOV AL,[BX+2] ;Get opcode
+ JMP [BX]
+
+NDPOP: ;First letter is "F" so must be 8087 opcode ("Numeric Data Processor")
+ MOV B,[MAXFLG],4 ;Allow all type flags
+ INC BX
+ CMP B,[BX],"n" ;"No-wait" form?
+ MOV AH,0
+ JNZ SAVNFLG
+ MOV AH,1
+ DEC AL
+ INC BX ;Skip over the "N"
+SAVNFLG:
+ MOV [NOWAIT],AH ;0 for wait, 1 for no wait
+ CMP AL,1
+ JB OPERR ;Not enough char left for valid opcode?
+ CMP AL,5
+ JA OPERR ;Too many?
+ CBW
+ XCHG AX,DX ;Save length in DX
+ MOV SI,DX
+ OR B,[SI+BX],80H ;Set high bit of last character
+ MOV AL,[BX] ;Get first char of opcode
+ INC BX
+ SUB AL,"a"
+ JB TRY2XM1 ;Go see if opcode starts with "2"
+ CMP AL,"z"-"a"
+ JA OPERR
+ CBW
+ SHL AX ;Double to index into address table
+ XCHG AX,SI ;Put in index register
+ MOV DI,[SI+NDPTAB] ;Get start of opcode table for this letter
+LOOKNDP:
+ MOV AH,[DI] ;Number of opcodes starting with this letter
+ OR AH,AH
+ JZ OPERR ;Any start with this letter?
+FNDNDP:
+ INC DI
+ MOV SI,BX ;Pointer to start of opcode
+ MOV CX,DX ;Get length of opcode
+ REPE
+ CMPB ;Compare opcode to table entry
+ JZ HAVNDP
+ DEC DI ;Back up in case that was last letter
+ MOV AL,80H ;Look for char with high bit set
+ENDOP:
+ SCASB
+ JA ENDOP
+ INC DI ;Skip over info about opcode
+ DEC AH
+ JNZ FNDNDP
+OPERRJ: JP OPERR
+
+TRY2XM1:
+ CMP AL,"2"-"a"
+ JNZ OPERR
+ MOV DI,XM1
+ JP LOOKNDP
+
+SPECIALOP:
+ AND AL,7 ;Mask to special op number
+ JZ FWAIT ;If zero, go handle FWAIT
+;Handle FNOP
+ CMP B,[NOWAIT],0 ;Was "N" present (If not opcode was "FOP")
+ JZ OPERR
+ MOV AL,9BH ;Need Wait opcode after all
+ CALL PUT
+ MOV AL,0D9H
+ CALL PUT
+ MOV AL,0D0H
+ JMP PUT
+
+FWAIT:
+ CMP B,[NOWAIT],0 ;"FNWAIT" not legal
+ JNZ OPERRJ
+ RET ;Nothing to do - "WAIT" already sent
+
+HAVNDP:
+ MOV SI,DI
+ CMP B,[NOWAIT],0
+ JNZ NWAIT
+ MOV AL,9BH ;Wait opcode
+ CALL PUT
+NWAIT:
+ LODW ;Get opcode info
+ TEST AL,0F8H ;Any operand bits set?
+ JZ NOOPS ;If no operands, output code
+ TEST AL,78H ;Special case?
+ JZ SPECIALOP
+ PUSH AX
+ CALL GETSYM ;See if any operands
+ POP CX
+ CMP AL,";"
+ JZ NOOPCHK
+ CMP AL,EOL
+ JZ NOOPCHK
+ CMP AL,FREG ;Is it 8087 register?
+ JNZ MEMOP
+ XCHG AX,CX
+ TEST AL,ONEREG ;One register OK as operand?
+ JNZ PUTREG ;Yes - save it
+ TEST AL,20H ;Memory-only operation?
+ MOV CL,20
+ JNZ ERRJ4
+ TEST AL,18H ;Two-register operation?
+ JPE ERRJ4 ;Must be exactly one bit set
+ PUSH DX ;Save register number
+ PUSH AX ;Save opcode
+ CALL GETSYM
+ CMP AL,","
+ MOV CL,15H
+ JNZ ERRJ4
+ CALL GETSYM
+ MOV CL,20
+ CMP AL,FREG
+ JNZ ERRJ4
+ POP AX
+ POP BX
+ XOR AL,2 ;Flip "POP" bit
+ AND AL,0FBH ;Reset direction bit to ST(0)
+ OR BL,BL ;Is first register ST(0)?
+ JZ ST0DEST
+ XCHG DX,BX
+ OR BL,BL ;One of these must be ST(0)
+ JNZ ERRJ4
+ XOR AL,4 ;Flip direction
+ JMPS PUTREG
+ST0DEST:
+ TEST AL,2 ;Is POP bit set?
+ JNZ ERRJ4 ;Don't allow destination ST(0) then pop
+PUTREG:
+ AND AH,0F8H ;Zero out register field
+ OR AH,DL
+ OR AH,0C0H
+ PUSH AX
+ CALL GETSYM ;Get to next symbol
+ POP AX
+ JMPS NOOPS
+
+NOOPCHK:
+ XCHG AX,CX
+ TEST AL,80H ;Is no operands OK?
+ MOV CL,20
+ JNZ ERRJ4
+NOOPS:
+;First test for FDIV or FSUB and reverse "R" bit if "D" bit is set
+ PUSH AX
+ AND AX,0E005H
+ CMP AX,0E004H
+ POP AX
+ JNZ NOREV
+ XOR AH,8 ;Reverse "R" bit
+NOREV:
+ AND AL,7
+ OR AL,0D8H ;ESC hook
+ CALL PUT
+ MOV AL,AH
+ JMP PUT
+
+BADFLAG:
+ MOV CL,20H
+ERRJ4: JMP ERROR
+
+MEMOP:
+ PUSH CX ;Save opcode
+ CALL GETOP1 ;Get memory operand
+ CMP AL,UNDEFID ;Is it?
+ MOV CL,20
+ JNZ ERRJ4
+ POP AX
+ TEST AL,20H ;Does it have memory format field?
+ JNZ GETFORMAT
+ TEST AL,8 ;Check if any memory operand legal
+ JZ ERRJ4
+ TEST AL,10H ;Check for 2-op arithmetic
+ JNZ PUTMEM ;If not, just use as plain memory op
+GETFORMAT:
+ AND AL,0F9H ;Zero memory format bits
+ MOV CL,[FLAG]
+ DEC CL ;Must now be in range 0-3
+ JL BADFLAG
+ MOV CH,AL ;Save opcode byte
+ SHR AL ;Put format bits in bits 2 & 3
+ AND AL,0CH
+ OR AL,CL ;Combine format bits with flag
+ MOV BX,FORMATTAB
+ XLAT
+ OR AL,AL ;Valid combination?
+ JS BADFLAG
+ OR AH,AL ;Possibly set new bits in second byte
+ OR AL,CH ;Set memory format bits
+PUTMEM:
+ AND AL,7
+ OR AL,0D8H
+ CALL PUT
+ MOV AL,AH
+ AND AL,38H
+ OR AL,DL ;Combine addressing mode
+ JMP PUTADD
+
+FORMATTAB:
+;There are 16 entries in this table. The 4-bit index is built like this:
+; Bit 3 0 for normal memory ops, 1 if extended is OK
+; Bit 2 0 for integer, 1 for real
+; Bit 0 & 1 Flag: 00=W, 01=S, 10=L, 11=T
+;
+;The entries in the table are used as two 3-bit fields. Bits 0-2 are ORed
+;into the first byte of the opcode for the Memory Format field. Bits 3-6
+;are ORed into the second byte to modify the opcode for extended operands.
+;If bit 7 is set, then that combination is illegal.
+
+ DB 6,2,80H,80H ;Normal integers
+ DB 80H,0,4,80H ;Normal reals
+ DB 6,2,2EH,80H ;Extended integers
+ DB 80H,0,4,2BH ;Extended reals
+
+GRP1:
+ MOV CX,8A09H
+ CALL MROPS
+ MOV CX,0C6H
+ MOV AL,BH
+ CMP AL,UNDEFID
+ JNZ L0006
+ CALL STIMM
+L0006:
+ AND AL,1
+ JZ BYTIMM
+ MOV AL,0B8H
+ OR AL,BL
+ CALL PUT
+ JMP PUTWOR
+
+BYTIMM:
+ MOV AL,0B0H
+ OR AL,BL
+ CALL PUT
+PUTBJ: JMP PUTBYT
+
+IMMED:
+ MOV AL,BH
+ CMP AL,UNDEFID
+ JZ STIMM
+ MOV AL,BL
+ OR AL,AL
+ JZ RET
+ MOV AL,BH
+ CALL IMM
+ OR AL,0C0H
+ CALL PUT
+FINIMM:
+ MOV AL,CL
+ POP CX
+ TEST AL,1
+ JZ PUTBJ
+ CMP AL,83H
+ JZ PUTBJ
+ JMP PUTWOR
+
+STIMM:
+ MOV AL,[FLAG]
+ CALL IMM
+ CALL PUTADD
+ JP FINIMM
+
+IMM:
+ AND AL,1
+ OR AL,CL
+ MOV CL,AL
+ CALL PUT
+ MOV AL,CH
+ AND AL,38H
+ OR AL,BL
+ RET
+
+PUT:
+;Save byte in AL as pure code, with intermediate code bits 00. AL and
+;DI destroyed, no other registers affected.
+ PUSH BX
+ PUSH CX
+ MOV CH,0 ;Flag as pure code
+ CALL GEN
+ POP CX
+ POP BX
+ RET
+
+GEN:
+;Save byte of code in AL, given intermediate code bits in bits 7&8 of CH.
+ CALL PUTINC ;Save it and bump code pointer
+GEN1:
+ MOV AL,[RELOC]
+ RCL CH
+ RCL AL
+ RCL CH
+ RCL AL
+ MOV [RELOC],AL
+ MOV BX,BCOUNT
+ DEC B,[BX]
+ JNZ RET
+ MOV B,[BX],4
+ MOV BX,RELOC
+ MOV AL,[BX]
+ MOV B,[BX],0
+ MOV DI,[IY]
+ MOV [DI],AL
+ MOV BX,[CODE]
+ MOV [IY],BX
+ INC BX
+ MOV [CODE],BX
+ RET
+
+PUTINC:
+ INC [PC]
+PUTCD:
+ MOV DI,[CODE]
+ STOB
+ MOV [CODE],DI
+ RET
+
+PUTWOR:
+;Save the word value described by [DLABEL] and [DATA] as code. If defined,
+;two bytes of pure code will be produced. Otherwise, appropriate intermediate
+;code will be generated.
+ PUSH CX
+ MOV CH,80H
+ PUSH DX
+ PUSH BX
+ JP PUTBW
+
+PUTBYT:
+;Same as PUTWOR, above, but for byte value.
+ PUSH CX
+ MOV CH,40H
+ PUSH DX
+ PUSH BX
+ MOV BX,[DLABEL]
+ MOV AL,BH
+ OR AL,BL
+ JNZ PUTBW
+ MOV BX,[DATA]
+ OR AL,BH
+ JZ PUTBW
+ INC BH
+ JZ PUTBW
+ MOV CL,31
+ JMP ERROR
+PUTBW:
+ MOV DX,[DLABEL]
+ MOV BX,[DATA]
+PUTCHK:
+ OR DX,DX
+ JZ NOUNDEF
+ MOV AL,DL
+ CALL PUTCD
+ MOV AL,DH
+ CALL PUTCD
+ MOV AL,BL
+ CALL PUTINC
+ MOV AL,BH
+ TEST CH,080H
+ JZ SMPUT
+ CALL GEN
+ JP PRET
+SMPUT:
+ CALL PUTCD
+ CALL GEN1
+PRET:
+ POP BX
+ POP DX
+ POP CX
+ RET
+
+NOUNDEF:
+ MOV AL,BL
+ MOV CL,BH
+ PUSH CX
+ MOV CH,0
+ CALL GEN
+ POP CX
+ MOV AL,CL
+ TEST CH,080H
+ MOV CH,0
+ JZ PRET
+ CALL GEN
+ JP PRET
+
+PUTADD:
+;Save complete addressing mode. Addressing mode is in AL; if this is a register
+;operation (>=C0), then the one byte will be saved as pure code. Otherwise,
+;the details of the addressing mode will be investigated and the optional one-
+;or two-byte displacement will be added, as described by [ADDR] and [ALABEL].
+ PUSH CX
+ PUSH DX
+ PUSH BX
+ MOV CH,0
+ MOV CL,AL
+ CALL GEN ;Save the addressing mode as pure code
+ MOV AL,CL
+ MOV CH,80H
+ AND AL,0C7H
+ CMP AL,6
+ JZ TWOBT ;Direct address?
+ AND AL,0C0H
+ JZ PRET ;Indirect through reg, no displacement?
+ CMP AL,0C0H
+ JZ PRET ;Register to register operation?
+ MOV CH,AL ;Save whether one- or two-byte displacement
+TWOBT:
+ MOV BX,[ADDR]
+ MOV DX,[ALABEL]
+ JP PUTCHK
+
+GRP2:
+ CALL GETOP
+ MOV CX,0FF30H
+ CMP AL,UNDEFID
+ JZ PMEM
+ MOV CH,50H
+ CMP AL,XREG
+ JZ PXREG
+ MOV CH,6
+ CMP AL,SREG
+ JNZ $+5
+ JMP PACKREG
+ MOV CL,20
+ JMP ERROR
+
+PMEM:
+ MOV AL,CH
+ CALL PUT
+ MOV AL,CL
+ OR AL,DL
+ JMP PUTADD
+
+PXREG:
+ MOV AL,CH
+ OR AL,DL
+ JMP PUT
+
+GRP3:
+ CALL GETOP
+ PUSH DX
+ CALL GETOP2
+ POP BX
+ MOV CX,8614H
+ MOV AL,SREG
+ CMP AL,BH
+ JZ ERR6
+ CMP AL,DH
+ JZ ERR6
+ MOV AL,CONST
+ CMP AL,BH
+ JZ ERR6
+ CMP AL,DH
+ JZ ERR6
+ MOV AL,UNDEFID
+ CMP AL,BH
+ JZ EXMEM
+ CMP AL,DH
+ JZ EXMEM1
+ MOV AL,BH
+ CMP AL,DH
+ MOV CL,22
+ JNZ ERR6
+ CMP AL,XREG
+ JZ L0008
+ CALL RR1
+L0008: ;RR1 never returns
+ MOV AL,BL
+ OR AL,AL
+ JZ EXACC
+ XCHG DX,BX
+ MOV AL,BL
+ OR AL,AL
+ MOV AL,BH
+ JZ EXACC
+ CALL RR1
+EXACC:
+ MOV AL,90H
+ OR AL,DL
+ JMP PUT
+
+EXMEM:
+ XCHG DX,BX
+EXMEM1:
+ CMP AL,BH
+ JZ ERR6
+ MOV CL,1 ;Flag word as OK
+ CALL NOTAC ;NOTAC never returns
+ERR6: JMP ERROR
+
+GRP4:
+ PUSH AX
+ CALL GETOP
+ POP CX
+ XCHG CL,CH
+ CMP AL,CONST
+ JZ FIXED
+ SUB AL,XREG
+ DEC DL
+ DEC DL
+ OR AL,DL
+ MOV CL,20
+ JNZ ERR6
+ MOV AL,CH
+ OR AL,8
+ JMP PUT
+FIXED:
+ MOV AL,CH
+ CALL PUT
+ JMP PUTBYT
+
+GRP5:
+ PUSH AX
+ CALL GETOP
+ MOV CL,20
+ CMP AL,CONST
+ JNZ ERR6
+ MOV BX,[DLABEL]
+ MOV AL,BH
+ OR AL,BL
+ MOV CL,30
+ JNZ ERR6
+ MOV BX,[DATA]
+ POP AX
+ OR AL,AL
+ JZ ORG
+ DEC AL
+ JZ DSJ
+ DEC AL
+ JZ EQU
+ DEC AL
+ JZ $+5
+ JMP IF
+PUTOP:
+ MOV AL,-3
+ JP NEWLOC
+ALIGN:
+ MOV AL,[PC]
+ AND AL,1
+ JZ RET
+ MOV BX,1
+DSJ:
+ XCHG DX,BX
+ MOV BX,[PC]
+ ADD BX,DX
+ MOV [PC],BX
+ XCHG DX,BX
+ MOV AL,-4
+ JP NEWLOC
+EQU:
+ XCHG DX,BX
+ MOV BX,[LABPT]
+ MOV AL,BH
+ OR AL,BL
+ MOV CL,34
+ JZ ERR7
+ MOV [BX],DL
+ INC BX
+ MOV [BX],DH
+ RET
+ORG:
+ MOV [PC],BX
+ MOV AL,-2
+NEWLOC:
+ CALL PUTCD
+ MOV AL,BL
+ CALL PUTCD
+ MOV AL,BH
+ CALL PUTCD
+ MOV CH,0C0H
+ JMP GEN1
+GRP6:
+ MOV CH,AL
+ MOV CL,4
+ CALL MROPS
+ MOV CL,23
+ERR7: JMP ERROR
+GRP7:
+ MOV CH,AL
+ MOV CL,1
+ CALL MROPS
+ MOV CL,80H
+ MOV DX,[DLABEL]
+ MOV AL,DH
+ OR AL,DL
+ JNZ ACCJ
+ XCHG DX,BX
+ MOV BX,[DATA]
+ MOV AL,BL
+ CBW
+ CMP AX,BX
+ XCHG DX,BX
+ JNZ ACCJ
+ OR CL,002H
+ACCJ: JMP ACCIMM
+GRP8:
+ MOV CL,AL
+ MOV CH,0FEH
+ JP ONEOP
+GRP9:
+ MOV CL,AL
+ MOV CH,0F6H
+ONEOP:
+ PUSH CX
+ CALL GETOP
+ONE:
+ MOV CL,26
+ CMP AL,CONST
+ JZ ERR7
+ CMP AL,SREG
+ MOV CL,22
+ JZ ERR7
+ POP CX
+ CMP AL,UNDEFID
+ JZ MOP
+ AND AL,1
+ JZ ROP
+ TEST CL,001H
+ JZ ROP
+ MOV AL,CL
+ AND AL,0F8H
+ OR AL,DL
+ JMP PUT
+MOP:
+ MOV AL,[FLAG]
+ AND AL,1
+ OR AL,CH
+ CALL PUT
+ MOV AL,CL
+ AND AL,38H
+ OR AL,DL
+ JMP PUTADD
+ROP:
+ OR AL,CH
+ CALL PUT
+ MOV AL,CL
+ AND AL,38H
+ OR AL,0C0H
+ OR AL,DL
+ JMP PUT
+GRP10:
+ MOV CL,AL
+ MOV CH,0F6H
+ PUSH CX
+ CALL GETOP
+ MOV CL,20
+ MOV AL,DL
+ OR AL,AL
+ JNZ ERRJ1
+ MOV AL,DH
+ CMP AL,XREG
+ JZ G10
+ CMP AL,REG
+ERRJ1: JNZ ERR8
+G10:
+ PUSH AX
+ CALL GETOP
+ POP AX
+ AND AL,1
+ MOV [FLAG],AL
+ MOV AL,DH
+ONEJ: JP ONE
+GRP11:
+ CALL PUT
+ MOV AL,0AH
+ JMP PUT
+GRP12:
+ MOV CL,AL
+ MOV CH,0D0H
+ PUSH CX
+ CALL GETOP
+ MOV AL,[SYM]
+ CMP AL,','
+ MOV AL,DH
+ JNZ ONEJ
+ PUSH DX
+ CALL GETOP
+ SUB AL,REG
+ MOV CL,20
+ DEC DL
+ OR AL,DL
+ JNZ ERR8
+ POP DX
+ MOV AL,DH
+ POP CX
+ OR CH,002H
+ PUSH CX
+ JMP ONE
+GRP13:
+ MOV CH,AL
+ MOV CL,1
+ CALL MROPS
+ MOV CL,80H
+ACCIMM:
+ CALL IMMED
+ OR CH,004H
+ AND CH,0FDH
+AIMM:
+ MOV AL,BH
+ AND AL,1
+ LAHF
+ PUSH AX
+ OR AL,CH
+ CALL PUT
+ POP AX
+ SAHF
+ JNZ $+5
+ JMP PUTBYT
+ JMP PUTWOR
+
+ERR8: JMP ERROR
+
+GRP14:
+;JMP and CALL mnemonics
+ LAHF
+ XCHG AH,AL
+ PUSH AX
+ XCHG AH,AL
+ MOV B,[MAXFLG],3 ;Allow "L" flag
+ CALL GETOP
+ CMP AL,CONST
+ JZ DIRECT
+ MOV CL,20
+ CMP AL,REG
+ JZ ERR8
+ CMP AL,SREG
+ JZ ERR8
+ CMP AL,XREG
+ JNZ NOTRG
+ OR DL,0C0H
+NOTRG:
+;Indirect jump. DL has addressing mode.
+ MOV AL,0FFH
+ CALL PUT
+ POP AX
+ XCHG AH,AL
+ SAHF
+ AND AL,38H
+ OR AL,DL
+ MOV CH,[FLAG]
+ CMP CH,3 ;Flag "L" present?
+ JZ PUTADDJ ;If so, do inter-segment
+ MOV CL,27H
+ CMP CH,-1 ;Better not be a flag
+ JNZ ERR8
+ AND AL,0F7H ;Convert to intra-segment
+PUTADDJ:
+ JMP PUTADD
+DIRECT:
+ MOV AL,[SYM]
+ CMP AL,','
+ JZ LONGJ
+ POP AX
+ XCHG AH,AL
+ SAHF
+ DEC AL
+ CMP AL,0E9H
+ JZ GOTOP
+ MOV AL,0E8H
+GOTOP:
+ CALL PUT
+ MOV DX,[PC]
+ INC DX
+ INC DX
+ SUB [DATA],DX
+ JMP PUTWOR
+LONGJ:
+ POP AX
+ XCHG AH,AL
+ SAHF
+ CALL PUT
+ CALL PUTWOR
+ CALL GETOP
+ MOV CL,20
+ CMP AL,CONST
+ JNZ ERR8
+ JMP PUTWOR
+
+GRP16:
+;RET mnemonic
+ LAHF
+ XCHG AH,AL
+ PUSH AX
+ XCHG AH,AL
+ CALL GETSYM
+ CMP AL,5
+ JZ LONGR
+ CMP AL,EOL
+ JZ NODEC
+ CMP AL,';'
+ JZ NODEC
+GETSP:
+ CALL GETOP1
+ POP CX
+ CMP AL,CONST
+ MOV CL,20
+ JNZ ERR9
+ MOV AL,CH
+ AND AL,0FEH
+ CALL PUT
+ JMP PUTWOR
+LONGR:
+ CMP DL,3 ;Is flag "L"?
+ MOV CL,27H
+ JNZ ERR10 ;If not, bad flag
+ POP AX
+ XCHG AH,AL
+ SAHF
+ OR AL,8
+ LAHF
+ XCHG AH,AL
+ PUSH AX
+ XCHG AH,AL
+NOTLON:
+ CALL GETSYM
+ CMP AL,EOL
+ JZ DORET
+ CMP AL,';'
+ JZ DORET
+ CMP AL,','
+ JNZ L0011
+ CALL GETSYM
+L0011:
+ JP GETSP
+NODEC:
+;Return is intra-segment (short) without add to SP.
+;Record position for RET symbol.
+ MOV BX,[PC]
+ MOV [LSTRET],BX
+ XCHG DX,BX
+ MOV BX,[RETPT]
+ MOV AL,BH
+ OR AL,BL
+ JZ DORET
+ MOV B,[BX],1
+ INC BX
+ MOV [BX],DX
+ MOV BX,0
+ MOV [RETPT],BX
+DORET:
+ POP AX
+ XCHG AH,AL
+ SAHF
+ JMP PUT
+
+GRP17:
+ CALL PUT
+ CALL GETOP
+ CMP AL,CONST
+ MOV CL,20
+ERR9: JNZ ERR10
+ MOV BX,[DATA]
+ MOV DX,[PC]
+ INC DX
+ SUB BX,DX
+ MOV [DATA],BX
+ CALL PUTBYT
+ MOV BX,[DLABEL]
+ MOV AL,BH
+ OR AL,BL
+ JNZ RET
+ MOV BX,[DATA]
+ MOV AL,BL
+ CBW
+ CMP AX,BX ;Signed 8-bit number?
+ JZ RET
+ MOV CL,31
+ERR10: JMP ERROR
+ RET
+GRP18:
+ CALL GETOP
+ CMP AL,CONST
+ MOV CL,20
+ JNZ ERR10
+ MOV BX,[DLABEL]
+ MOV AL,BH
+ OR AL,BL
+ JNZ GENINT
+ MOV BX,[DATA]
+ MOV DX,3
+ SBB BX,DX
+ JNZ GENINT
+ MOV AL,0CCH
+ JMP PUT
+GENINT:
+ MOV AL,0CDH
+ CALL PUT
+ JMP PUTBYT
+
+GRP19: ;ESC opcode
+ CALL GETOP
+ MOV CL,20
+ CMP AL,CONST
+ JNZ ERRJ ;First operand must be immediate
+ MOV CL,1EH
+ TEST [DLABEL],-1 ;See if all labels have been defined
+ JNZ ERRJ
+ MOV AX,[DATA]
+ CMP AX,64 ;Must only be 6 bits
+ MOV CL,1FH
+ JNB ERRJ
+ MOV BL,AL ;Save for second byte
+ SHR AL
+ SHR AL
+ SHR AL
+ OR AL,0D8H ;ESC opcode
+ CALL PUT
+ PUSH BX
+ CALL GETOP2
+ POP BX
+ AND BL,7 ;Low 3 bits of first operand
+ SHL BL
+ SHL BL
+ SHL BL
+ CMP AL,UNDEFID ;Check for memory operand
+ JZ ESCMEM
+ CMP AL,CONST ;Check for another immediate
+ JZ ESCIMM
+ MOV CL,20
+ERRJ: JMP ERROR
+
+ESCMEM:
+ OR BL,DL ;Combine mode with first operand
+ MOV AL,BL
+ JMP PUTADD
+
+ESCIMM:
+ MOV CL,1EH
+ TEST [DLABEL],-1 ;See if second operand is fully defined
+ JNZ ERRJ
+ MOV AX,[DATA]
+ MOV CL,1FH
+ CMP AX,8 ;Must only be 3 bit value
+ JNB ERRJ
+ OR AL,BL ;Combine first and second operands
+ OR AL,0C0H ;Force "register" mode
+ JMP PUT
+
+GRP20:
+ MOV CH,AL
+ MOV CL,1
+ CALL MROPS
+ MOV CL,0F6H
+ CALL IMMED
+ MOV CH,0A8H
+ JMP AIMM
+GRP21:
+ CALL GETOP
+ CMP AL,SREG
+ MOV CL,28
+ JNZ ERRJ
+ MOV CH,26H
+PACKREG:
+ MOV AL,DL
+ ADD AL,AL
+ ADD AL,AL
+ ADD AL,AL
+ OR AL,CH
+ JMP PUT
+GRP22:
+ CALL GETOP
+ MOV CX,8F00H
+ CMP AL,UNDEFID
+ JNZ $+5
+ JMP PMEM
+ MOV CH,58H
+ CMP AL,XREG
+ JNZ $+5
+ JMP PXREG
+ MOV CH,7
+ CMP AL,SREG
+ JZ PACKREG
+ MOV CL,20
+ERR11: JMP ERROR
+GRP23:
+ MOV [DATSIZ],AL
+GETDAT:
+ CALL GETSYM
+ MOV AL,2
+ CALL VAL1
+ MOV AL,[SYM]
+ CMP AL,','
+ MOV AL,[DATSIZ]
+ JNZ ENDDAT
+ CALL SAVDAT
+ JP GETDAT
+ENDDAT:
+ CMP AL,2
+ JNZ SAVDAT
+ MOV BX,[DATA]
+ LAHF
+ OR BL,080H
+ SAHF
+ MOV [DATA],BX
+SAVDAT:
+ OR AL,AL
+ JZ $+5
+ JMP PUTBYT
+ JMP PUTWOR
+IF:
+ OR BX,BX
+ JZ SKIPCD
+ INC B,[IFFLG]
+ RET
+
+SKIPCD:
+ INC B,[CHKLAB]
+SKIPLP:
+ XOR AL,AL
+ CALL NEXLIN
+ CALL NEXTCHR
+ CMP AL,1AH
+ JZ END
+ CALL GETLAB
+ JC SKIPLP
+ MOV DI,LENID
+ MOV SI,IFEND
+ MOV CH,0
+ MOV CL,[DI]
+ INC CL
+ REPE
+ CMPB
+ JZ ENDCOND
+ MOV DI,LENID
+ MOV SI,IFNEST
+ MOV CL,[DI]
+ INC CL
+ REPE
+ CMPB
+ JNZ SKIPLP
+ INC B,[CHKLAB]
+ JP SKIPLP
+
+ENDCOND:
+ DEC B,[CHKLAB]
+ JNZ SKIPLP
+ RET
+
+ENDIF:
+ MOV AL,[IFFLG]
+ MOV CL,36
+ DEC AL
+ JS ERRJMP
+ MOV [IFFLG],AL
+ RET
+
+ERRJMP: JMP ERROR
+
+;*********************************************************************
+;
+; PASS 2
+;
+;*********************************************************************
+
+END:
+ MOV DL,4
+WREND:
+ MOV CH,0FFH
+ MOV AL,CH
+ CALL GEN
+ DEC DL
+ JNZ WREND
+ MOV [BUFPT],SRCBUF
+ MOV B,[HEXCNT],-5 ;FLAG HEX BUFFER AS EMPTY
+ MOV [LSTPNT],LSTBUF
+ MOV [HEXPNT],HEXBUF
+ XOR AX,AX
+ MOV [ERRCNT],AX
+ MOV [PC],AX
+ MOV [LINE],AX ;Current line number
+ MOV [HEXADD],OBJECT
+ MOV DX,FCB
+ MOV AH,OPEN
+ INT 33 ;Re-open source file
+ XOR AX,AX
+ MOV [FCB+12],AX ;Set CURRENT BLOCK to zero
+ MOV [FCB+20H],AL ;Set NEXT RECORD field to zero
+ MOV [FCB+14],BUFSIZ
+ MOV [COUNT],AL
+ MOV CH,1
+ MOV SI,START
+FIXLINE:
+ MOV DI,START ;Store code over used up intermediate code
+ XOR AL,AL
+ MOV [SPC],AL ;No "special" yet (ORG, PUT, DS)
+ MOV [ERR],AL ;No second pass errors yet
+NEXBT:
+ SHL CL ;Shift out last bit of previous code
+ DEC CH ;Still have codes left?
+ JNZ TESTTYP
+ LODB ;Get next flag byte
+ MOV CL,AL
+ MOV CH,4
+TESTTYP:
+ SHL CL ;Set flags based on two bits
+ JO FIXUP
+ LODB
+ JC EMARK
+OBJBT:
+ STOB
+ JP NEXBT
+
+FIXUP:
+;Either a word or byte fixup is needed from a forward reference
+ LODW ;Get pointer to symbol
+ XCHG AX,BX
+ LODW ;Get constant part
+ ADD AX,[BX+1] ;Add symbol value to constant part
+ CMP B,[BX],0 ;See if symbol got defined
+ JNZ HAVDEF
+ MOV B,[ERR],100 ;Undefined - flag error
+ XOR AX,AX
+HAVDEF:
+ OR CL,CL ;See if word or byte fixup
+ JS DEFBYT
+ STOW
+ JP NEXBT
+
+DEFBYT:
+ MOV DX,AX
+ CBW ;Extend sign
+ CMP AX,DX ;See if in range +127 to -128
+ JZ OBJBT ;If so, it's always OK
+ NOT AH ;Check for range +255 to -256
+ CMP AH,DH
+ JNZ RNGERR ;Must always be in this range
+;Check for short jump. If so, we're out of range; otherwise we're OK
+ CMP DI,START+1 ;Only one other byte on line?
+ JNZ OBJBT ;Can't be short jump if not
+ MOV AL,[START] ;Get the first byte of this line
+ CMP AL,0EBH ;Direct short jump?
+ JZ RNGERR
+ AND AL,0FCH
+ CMP AL,0E0H ;LOOP or JCXZ instruction?
+ JZ RNGERR
+ AND AL,0F0H
+ CMP AL,70H ;Conditional jump?
+ MOV AL,DL ;Get code byte in AL
+ JNZ OBJBT ;If not, we're OK
+RNGERR:
+ MOV B,[ERR],101 ;Value out of range
+ JP OBJBT
+
+FINIJ: JMP FINI
+
+EMARK:
+ CMP AL,-1 ;End of file?
+ JZ FINIJ
+ CMP AL,-10 ;Special item?
+ JA SPEND
+ PUSH CX
+ PUSH SI
+ PUSH AX ;Save error code
+ MOV AH,[LSTDEV]
+ AND AH,0FEH ;Reset error indicator
+ OR AL,[ERR] ;See if any errors on this line
+ JZ NOERR
+ OR AH,1 ;Send line to console if error occured
+NOERR:
+ MOV [LSTDEV],AH
+ MOV CX,DI
+ CALL STRTLIN ;Print address of line
+ MOV SI,START
+ SUB CX,SI ;Get count of bytes of code
+ JZ SHOLIN
+CODLP:
+ LODB
+ CALL SAVCD ;Ouput code to HEX and PRN files
+ LOOP CODLP
+SHOLIN:
+ MOV AL,0
+ XCHG AL,[COUNT]
+ MOV CX,7 ;Allow 7 bytes of code per line
+ SUB CL,AL
+ MOV AL,' '
+ JZ NOFIL
+BLNK: ;Put in 3 blanks for each byte not present
+ CALL LIST
+ CALL LIST
+ CALL LIST
+ LOOP BLNK
+NOFIL:
+ CALL OUTLIN
+ POP AX ;Restore error code
+ CALL REPERR
+ MOV AL,[ERR]
+ CALL REPERR
+ POP SI
+ POP CX
+ MOV AL,[SPC] ;Any special funtion?
+ OR AL,AL
+ JNZ SPCFUN
+ JMP FIXLINE
+
+SPEND:
+ MOV [SPC],AL ;Record special function
+ LODW ;Get it's data
+ MOV [DATA],AX
+ JMP NEXBT
+
+SPCFUN:
+ MOV DX,[DATA]
+ CMP AL,-2
+ JZ DORG
+ CMP AL,-3
+ JZ DPUT
+DDS:
+;Handle DS pseudo-op
+ ADD [PC],DX
+ ADD [HEXADD],DX
+ JMP FIXLINE
+
+DORG:
+;Handle ORG pseudo-op
+ MOV [PC],DX
+ JMP FIXLINE
+
+DPUT:
+;Handle PUT pseudo-op
+ MOV [HEXADD],DX
+ JMP FIXLINE
+
+OUTLIN:
+;Copy the source line to the ouput device. Line will be preceded by
+;assembler-generated line number. This routine may be called several times
+;on one line (once for each line of object code bytes), so it sets a flag
+;so the line will only be output on the first call.
+ MOV AL,-1
+ XCHG AL,[LINFLG]
+ OR AL,AL
+ JNZ CRLF ;Output line only if first time
+ MOV AX,[LINE]
+ INC AX
+ MOV [LINE],AX
+ MOV BH,0 ;No leading zero suppression
+ CALL OUT10
+ MOV AL," "
+ CALL LIST
+ MOV AL,[LSTFCB]
+ CMP AL,'Z'
+ JZ CRLF ;Don't call NEXTCHR if listing suppressed
+ PUSH SI ;Save the only register destroyed by NEXTCHR
+OUTLN:
+ CALL NEXTCHR
+ CALL LIST
+ CMP AL,10 ;Output until linefeed found
+ JNZ OUTLN
+ POP SI
+ RET
+
+PRTCNT:
+ MOV AX,[ERRCNT]
+ MOV BX,ERCNTM
+PRNT10:
+ PUSH AX
+ CALL PRINT
+ POP AX
+ MOV BH,"0"-" " ;Enable leading zero suppression
+ CALL OUT10
+CRLF:
+ MOV AL,13
+ CALL LIST
+ MOV AL,10
+ JP LIST
+
+OUT10:
+ XOR DX,DX
+ MOV DI,10000
+ DIV AX,DI
+ OR AL,AL ;>10,000?
+ JNZ LEAD
+ SUB AL,"0"-" " ;Convert leading zero to blank
+LEAD:
+ ADD AL,"0"
+ CALL LIST
+ XCHG AX,DX
+ MOV BL,100
+ DIV AL,BL
+ MOV BL,AH
+ CALL HIDIG ;Convert to decimal and print 1000s digit
+ CALL DIGIT ;Print 100s digit
+ MOV AL,BL
+ CALL HIDIG ;Convert to decimal and print 10s digit
+ MOV BH,0 ;Ensure leading zero suppression is off
+ JP DIGIT
+
+HIDIG:
+ AAM ;Convert binary to unpacked BCD
+ OR AX,3030H ;Add "0" bias
+DIGIT:
+ XCHG AL,AH
+ CMP AL,"0"
+ JZ SUPZ
+ MOV BH,0 ;Turn off zero suppression if not zero
+SUPZ:
+ SUB AL,BH ;Convert leading zeros to blanks
+ JP LIST
+
+STRTLIN:
+ MOV B,[LINFLG],0
+ MOV BX,[PC]
+ MOV AL,BH
+ CALL PHEX
+ MOV AL,BL
+PHEXB:
+ CALL PHEX
+ MOV AL,' '
+LIST:
+ PUSH AX
+ PUSH DX
+ AND AL,7FH
+ MOV DL,AL
+ TEST B,[LSTDEV],3 ;See if output goes to console
+ JZ PRNCHK
+ MOV AH,2
+ INT 33 ;Output to console
+PRNCHK:
+ TEST B,[LSTDEV],4 ;See if output goes to printer
+ JZ FILCHK
+ MOV AH,5
+ INT 33 ;Output to printer
+FILCHK:
+ MOV AL,DL
+ POP DX
+ TEST B,[LSTDEV],80H ;See if output goes to a file
+ JZ LISTRET
+ CALL WRTBUF
+LISTRET:
+ POP AX
+ RET
+
+WRTBUF:
+ PUSH DI
+ MOV DI,[LSTPNT]
+ STOB
+ CMP DI,LSTBUF+LSTBUFSIZ
+ JNZ SAVPT
+ PUSH AX
+ PUSH CX
+ PUSH DX
+ CALL FLUSHBUF
+ POP DX
+ POP CX
+ POP AX
+SAVPT:
+ MOV [LSTPNT],DI
+ POP DI
+ RET
+
+PHEX:
+ PUSH AX
+ CALL UHALF
+ CALL LIST
+ POP AX
+ CALL LHALF
+ JP LIST
+
+FINI:
+ OR B,[LSTDEV],1
+ CALL PRTCNT
+ MOV BX,SYMSIZE
+ MOV AX,[6]
+ SUB AX,[HEAP] ;Size of symbol table
+ CALL PRNT10
+ MOV BX,FRESIZE
+ MOV AX,[HEAP]
+ SUB AX,[CODE] ;Free space remaining
+ CALL PRNT10
+ AND B,[LSTDEV],0FEH
+ MOV AL,[HEXFCB]
+ CMP AL,'Z'
+ JZ SYMDMP
+ MOV AL,[HEXCNT]
+ CMP AL,-5
+ JZ L0012
+ CALL ENHEXL
+L0012:
+ MOV AL,':'
+ CALL PUTCHR
+ MOV CH,10
+HEXEND:
+ PUSH CX
+ MOV AL,'0'
+ CALL PUTCHR
+ POP CX
+ DEC CH
+ JNZ HEXEND
+ MOV AL,13
+ CALL PUTCHR
+ MOV AL,10
+ CALL PUTCHR
+ MOV AL,1AH
+ CALL PUTCHR
+ CALL WRTHEX ;Flush HEX file buffer
+ MOV DX,HEXFCB
+ MOV AH,CLOSE
+ INT 33
+SYMDMP:
+ MOV AL,[SYMFLG]
+ CMP AL,'S'
+ JNZ ENDSYM
+ MOV AL,[LSTDEV]
+ OR AL,AL ;Any output device for symbol table dump?
+ JNZ DOSYMTAB
+ OR AL,1 ;If not, send it to console
+ MOV [LSTDEV],AL
+DOSYMTAB:
+ MOV BX,SYMMES
+ CALL PRINT
+ MOV DX,[BASE]
+ MOV AL,DH
+ OR AL,DL
+ JZ ENDSYM
+ MOV B,[SYMLIN],SYMWID ;No symbols on this line yet
+ MOV BX,[HEAP]
+ MOV SP,BX ;Need maximum stack for recursive tree walk
+ CALL NODE
+ENDSYM:
+ TEST B,[LSTDEV],80H ;Print listing to file?
+ JZ EXIT
+ MOV AL,1AH
+ CALL WRTBUF ;Write end-of-file mark
+ MOV DI,[LSTPNT]
+ CALL FLUSHBUF
+ MOV AH,CLOSE
+ INT 33
+EXIT: JMP 0
+
+NODE:
+ XCHG DX,BX
+ PUSH BX
+ MOV DL,[BX]
+ MOV DH,0
+ INC BX
+ ADD BX,DX
+ MOV DX,[BX]
+ OR DX,DX
+ JZ L0014
+ CALL NODE
+L0014:
+ POP BX
+ MOV AL,[BX]
+ INC BX
+ MOV CH,AL
+ ADD AL,24
+ SHR AL
+ SHR AL
+ SHR AL
+ MOV CL,AL
+ INC CL ;Invert last bit
+ AND CL,1 ;Number of extra tabs needed (0 or 1)
+ SHR AL ;Number of positions wide this symbol needs
+ SUB [SYMLIN],AL
+ JNC WRTSYM ;Will it fit?
+ SUB AL,SYMWID
+ NEG AL
+ MOV [SYMLIN],AL
+ CALL CRLF ;Start new line if not
+WRTSYM:
+ MOV AL,[BX]
+ INC BX
+ CALL LIST
+ DEC CH
+ JNZ WRTSYM
+ INC CL
+TABVAL:
+ MOV AL,9
+ CALL LIST
+ LOOP TABVAL
+ INC BX
+ INC BX
+ PUSH BX
+ MOV AL,[BX+4]
+ CALL PHEX
+ MOV AL,[BX+3]
+ CALL PHEX
+ CMP B,[SYMLIN],0 ;Will any more fit on line?
+ JZ NEXSYMLIN
+ MOV AL,9
+ CALL LIST
+ JP RIGHTSON
+NEXSYMLIN:
+ CALL CRLF
+ MOV B,[SYMLIN],SYMWID
+RIGHTSON:
+ POP BX
+ MOV DX,[BX]
+ OR DX,DX
+ JNZ NODE
+ RET
+
+SAVCD:
+ MOV [PREV],AL
+ PUSH BX
+ PUSH CX
+ PUSH AX
+ PUSH DX
+ CALL CODBYT
+ POP DX
+ MOV BX,COUNT
+ INC B,[BX]
+ MOV AL,[BX]
+ CMP AL,8
+ JNZ NOEXT
+ MOV B,[BX],1
+ CALL OUTLIN
+ MOV AL,' '
+ MOV CH,5
+TAB:
+ CALL LIST
+ DEC CH
+ JNZ TAB
+NOEXT:
+ POP AX
+ CALL PHEXB
+ POP CX
+ INC [PC]
+ INC [HEXADD]
+ POP BX
+ RET
+
+REPERR:
+ OR AL,AL ;Did an error occur?
+ JZ RET
+ INC [ERRCNT]
+ PUSH AX
+ MOV BX,ERRMES ;Print "ERROR"
+ CALL PRINT
+ POP AX
+;We have error number in AL. See if there's an error message for it
+ MOV DI,ERRTAB
+ MOV BL,80H
+ERRLOOK:
+ SCASB ;Do we have the error message
+ JBE HAVMES ;Quit looking if we have it or passed it
+ XCHG AX,BX ;Put 80H in AL to look for end of this message
+NEXTMES:
+ SCASB ;Look for high bit set in message
+ JA NEXTMES ; which means we've reached the end
+ XCHG AX,BX ;Restore error number to AL
+ JMPS ERRLOOK ;Keep looking
+
+HAVMES:
+ MOV BX,DI ;Put address of message in BX
+ JZ PRNERR ;Do we have a message for this error?
+ CALL PHEX ;If not, just print error number
+ JMP CRLF
+
+PRNERR:
+ CALL PRINT
+ JMP CRLF
+
+PRINT:
+ MOV AL,[BX]
+ CALL LIST
+ OR AL,AL
+ JS RET
+ INC BX
+ JP PRINT
+
+OUTA:
+ MOV DL,AL
+OUT:
+ AND DL,7FH
+ MOV CL,2
+SYSTEM:
+ CALL 5
+ RET
+
+CODBYT:
+ CMP B,[HEXFCB],"Z"
+ JZ RET
+ PUSH AX
+ MOV DX,[LASTAD]
+ MOV BX,[HEXADD]
+ MOV [LASTAD],BX
+ INC DX
+ MOV AL,[HEXCNT]
+ CMP AL,-5
+ JZ NEWLIN
+ CMP BX,DX
+ JZ AFHEX
+ CALL ENHEXL
+NEWLIN:
+ MOV AL,':'
+ CALL PUTCHR
+ MOV AL,-4
+ MOV [HEXCNT],AL
+ XOR AL,AL
+ MOV [CHKSUM],AL
+ MOV BX,[HEXPNT]
+ MOV [HEXLEN],BX
+ CALL HEXBYT
+ MOV AL,[HEXADD+1]
+ CALL HEXBYT
+ MOV AL,[HEXADD]
+ CALL HEXBYT
+ XOR AL,AL
+ CALL HEXBYT
+AFHEX:
+ POP AX
+HEXBYT:
+ MOV CH,AL
+ MOV BX,CHKSUM
+ ADD AL,[BX]
+ MOV [BX],AL
+ MOV AL,CH
+ CALL UHALF
+ CALL PUTCHR
+ MOV AL,CH
+ CALL LHALF
+ CALL PUTCHR
+ MOV BX,HEXCNT
+ INC B,[BX]
+ MOV AL,[BX]
+ CMP AL,26
+ JNZ RET
+ENHEXL:
+ MOV DI,[HEXLEN]
+ MOV CH,AL
+ CALL UHALF
+ STOB
+ MOV AL,CH
+ CALL LHALF
+ STOB
+ MOV AL,-6
+ MOV [HEXCNT],AL
+ MOV AL,[CHKSUM]
+ ADD AL,CH
+ NEG AL
+ CALL HEXBYT
+ MOV AL,13
+ CALL PUTCHR
+ MOV AL,10
+ CALL PUTCHR
+WRTHEX:
+;Write out the line
+ MOV DX,HEXBUF
+ MOV [HEXPNT],DX
+ MOV AH,SETDMA
+ INT 33
+ SUB DI,DX ;Length of buffer
+ MOV CX,DI
+ MOV DX,HEXFCB
+ MOV AH,BLKWRT
+ INT 33
+ OR AL,AL
+ JNZ DSKFUL
+ RET
+
+PUTCHR:
+ MOV DI,[HEXPNT]
+ STOB
+ MOV [HEXPNT],DI
+ RET
+
+FLUSHBUF:
+ MOV CX,DI
+ MOV DX,LSTBUF
+ MOV DI,DX
+ SUB CX,DX
+ JZ RET ;Buffer empty?
+ MOV AH,SETDMA
+ INT 33
+ MOV DX,LSTFCB
+ MOV AH,BLKWRT
+ INT 33
+ OR AL,AL
+ JZ RET
+DSKFUL:
+ MOV BX,WRTERR
+ JMP PRERR
+
+UHALF:
+ RCR AL
+ RCR AL
+ RCR AL
+ RCR AL
+LHALF:
+ AND AL,0FH
+ OR AL,30H
+ CMP AL,'9'+1
+ JC RET
+ ADD AL,7
+ RET
+
+NONE: DB 0
+
+; 8086 MNEMONIC TABLE
+
+; This table is actually a sequence of subtables, each starting with a label.
+; The label signifies which mnemonics the subtable applies to--A3, for example,
+; means all 3-letter mnemonics beginning with A.
+
+A3:
+ DB 7
+ DB 'dd'
+ DW GRP7
+ DB 2
+ DB 'nd'
+ DW GRP13
+ DB 22H
+ DB 'dc'
+ DW GRP7
+ DB 12H
+ DB 'aa'
+ DW PUT
+ DB 37H
+ DB 'as'
+ DW PUT
+ DB 3FH
+ DB 'am'
+ DW GRP11
+ DB 0D4H
+ DB 'ad'
+ DW GRP11
+ DB 0D5H
+A5:
+ DB 1
+ DB 'lign'
+ DW ALIGN
+ DB 0
+C3:
+ DB 7
+ DB 'mp'
+ DW GRP7
+ DB 3AH
+ DB 'lc'
+ DW PUT
+ DB 0F8H
+ DB 'ld'
+ DW PUT
+ DB 0FCH
+ DB 'li'
+ DW PUT
+ DB 0FAH
+ DB 'mc'
+ DW PUT
+ DB 0F5H
+ DB 'bw'
+ DW PUT
+ DB 98H
+ DB 'wd'
+ DW PUT
+ DB 99H
+C4:
+ DB 3
+ DB 'all'
+ DW GRP14
+ DB 9AH
+ DB 'mpb'
+ DW PUT
+ DB 0A6H
+ DB 'mpw'
+ DW PUT
+ DB 0A7H
+C5:
+ DB 2
+ DB 'mpsb'
+ DW PUT
+ DB 0A6H
+ DB 'mpsw'
+ DW PUT
+ DB 0A7H
+D2:
+ DB 5
+ DB 'b'
+ DW GRP23
+ DB 1
+ DB 'w'
+ DW GRP23
+ DB 0
+ DB 'm'
+ DW GRP23
+ DB 2
+ DB 's'
+ DW GRP5
+ DB 1
+ DB 'i'
+ DW PUT
+ DB 0FAH
+D3:
+ DB 4
+ DB 'ec'
+ DW GRP8
+ DB 49H
+ DB 'iv'
+ DW GRP10
+ DB 30H
+ DB 'aa'
+ DW PUT
+ DB 27H
+ DB 'as'
+ DW PUT
+ DB 2FH
+D4:
+ DB 1
+ DB 'own'
+ DW PUT
+ DB 0FDH
+E2:
+ DB 1
+ DB 'i'
+ DW PUT
+ DB 0FBH
+E3:
+ DB 3
+ DB 'qu'
+ DW GRP5
+ DB 2
+ DB 'sc'
+ DW GRP19
+ DB 0D8H
+ DB 'nd'
+ DW END
+ DB 0
+E5:
+ DB 1
+ DB 'ndif'
+ DW ENDIF
+ DB 0
+H3:
+ DB 1
+ DB 'lt'
+ DW PUT
+ DB 0F4H
+H4:
+ DB 1
+ DB 'alt'
+ DW PUT
+ DB 0F4H
+I2:
+ DB 2
+ DB 'n'
+ DW GRP4
+ DB 0E4H
+ DB 'f'
+ DW GRP5
+ DB 4
+I3:
+ DB 4
+ DB 'nc'
+ DW GRP8
+ DB 41H
+ DB 'nb'
+ DW GRP4
+ DB 0E4H
+ DB 'nw'
+ DW GRP4
+ DB 0E5H
+ DB 'nt'
+ DW GRP18
+ DB 0CCH
+I4:
+ DB 4
+ DB 'mul'
+ DW GRP10
+ DB 28H
+ DB 'div'
+ DW GRP10
+ DB 38H
+ DB 'ret'
+ DW PUT
+ DB 0CFH
+ DB 'nto'
+ DW PUT
+ DB 0CEH
+J2:
+ DB 10
+ DB 'p'
+ DW GRP17
+ DB 0EBH
+ DB 'z'
+ DW GRP17
+ DB 74H
+ DB 'e'
+ DW GRP17
+ DB 74H
+ DB 'l'
+ DW GRP17
+ DB 7CH
+ DB 'b'
+ DW GRP17
+ DB 72H
+ DB 'a'
+ DW GRP17
+ DB 77H
+ DB 'g'
+ DW GRP17
+ DB 7FH
+ DB 'o'
+ DW GRP17
+ DB 70H
+ DB 's'
+ DW GRP17
+ DB 78H
+ DB 'c'
+ DW GRP17
+ DB 72H
+J3:
+ DB 17
+ DB 'mp'
+ DW GRP14
+ DB 0EAH
+ DB 'nz'
+ DW GRP17
+ DB 75H
+ DB 'ne'
+ DW GRP17
+ DB 75H
+ DB 'nl'
+ DW GRP17
+ DB 7DH
+ DB 'ge'
+ DW GRP17
+ DB 7DH
+ DB 'nb'
+ DW GRP17
+ DB 73H
+ DB 'ae'
+ DW GRP17
+ DB 73H
+ DB 'nc'
+ DW GRP17
+ DB 73H
+ DB 'ng'
+ DW GRP17
+ DB 7EH
+ DB 'le'
+ DW GRP17
+ DB 7EH
+ DB 'na'
+ DW GRP17
+ DB 76H
+ DB 'be'
+ DW GRP17
+ DB 76H
+ DB 'pe'
+ DW GRP17
+ DB 7AH
+ DB 'np'
+ DW GRP17
+ DB 7BH
+ DB 'po'
+ DW GRP17
+ DB 7BH
+ DB 'no'
+ DW GRP17
+ DB 71H
+ DB 'ns'
+ DW GRP17
+ DB 79H
+J4:
+ DB 6
+ DB 'mps'
+ DW GRP17
+ DB 0EBH
+ DB 'cxz'
+ DW GRP17
+ DB 0E3H
+ DB 'nge'
+ DW GRP17
+ DB 7CH
+ DB 'nae'
+ DW GRP17
+ DB 72H
+ DB 'nbe'
+ DW GRP17
+ DB 77H
+ DB 'nle'
+ DW GRP17
+ DB 7FH
+L3:
+ DB 3
+ DB 'ea'
+ DW GRP6
+ DB 8DH
+ DB 'ds'
+ DW GRP6
+ DB 0C5H
+ DB 'es'
+ DW GRP6
+ DB 0C4H
+L4:
+ DB 5
+ DB 'oop'
+ DW GRP17
+ DB 0E2H
+ DB 'odb'
+ DW PUT
+ DB 0ACH
+ DB 'odw'
+ DW PUT
+ DB 0ADH
+ DB 'ahf'
+ DW PUT
+ DB 9FH
+ DB 'ock'
+ DW PUT
+ DB 0F0H
+L5:
+ DB 4
+ DB 'oope'
+ DW GRP17
+ DB 0E1H
+ DB 'oopz'
+ DW GRP17
+ DB 0E1H
+ DB 'odsb'
+ DW PUT
+ DB 0ACH
+ DB 'odsw'
+ DW PUT
+ DB 0ADH
+L6:
+ DB 2
+ DB 'oopne'
+ DW GRP17
+ DB 0E0H
+ DB 'oopnz'
+ DW GRP17
+ DB 0E0H
+M3:
+ DB 2
+ DB 'ov'
+ DW GRP1
+ DB 88H
+ DB 'ul'
+ DW GRP10
+ DB 20H
+M4:
+ DB 2
+ DB 'ovb'
+ DW PUT
+ DB 0A4H
+ DB 'ovw'
+ DW PUT
+ DB 0A5H
+M5:
+ DB 2
+ DB 'ovsb'
+ DW PUT
+ DB 0A4H
+ DB 'ovsw'
+ DW PUT
+ DB 0A5H
+N3:
+ DB 3
+ DB 'ot'
+ DW GRP9
+ DB 10H
+ DB 'eg'
+ DW GRP9
+ DB 18H
+ DB 'op'
+ DW PUT
+ DB 90H
+O2:
+ DB 1
+ DB 'r'
+ DW GRP13
+ DB 0AH
+O3:
+ DB 2
+ DB 'ut'
+ DW GRP4
+ DB 0E6H
+ DB 'rg'
+ DW GRP5
+ DB 0
+O4:
+ DB 2
+ DB 'utb'
+ DW GRP4
+ DB 0E6H
+ DB 'utw'
+ DW GRP4
+ DB 0E7H
+P3:
+ DB 2
+ DB 'op'
+ DW GRP22
+ DB 8FH
+ DB 'ut'
+ DW GRP5
+ DB 3
+P4:
+ DB 2
+ DB 'ush'
+ DW GRP2
+ DB 0FFH
+ DB 'opf'
+ DW PUT
+ DB 9DH
+P5:
+ DB 1
+ DB 'ushf'
+ DW PUT
+ DB 9CH
+R3:
+ DB 6
+ DB 'et'
+ DW GRP16
+ DB 0C3H
+ DB 'ep'
+ DW PUT
+ DB 0F3H
+ DB 'ol'
+ DW GRP12
+ DB 0
+ DB 'or'
+ DW GRP12
+ DB 8
+ DB 'cl'
+ DW GRP12
+ DB 10H
+ DB 'cr'
+ DW GRP12
+ DB 18H
+R4:
+ DB 2
+ DB 'epz'
+ DW PUT
+ DB 0F3H
+ DB 'epe'
+ DW PUT
+ DB 0F3H
+R5:
+ DB 2
+ DB 'epnz'
+ DW PUT
+ DB 0F2H
+ DB 'epne'
+ DW PUT
+ DB 0F2H
+S3:
+ DB 11
+ DB 'ub'
+ DW GRP7
+ DB 2AH
+ DB 'bb'
+ DW GRP7
+ DB 1AH
+ DB 'bc'
+ DW GRP7
+ DB 1AH
+ DB 'tc'
+ DW PUT
+ DB 0F9H
+ DB 'td'
+ DW PUT
+ DB 0FDH
+ DB 'ti'
+ DW PUT
+ DB 0FBH
+ DB 'hl'
+ DW GRP12
+ DB 20H
+ DB 'hr'
+ DW GRP12
+ DB 28H
+ DB 'al'
+ DW GRP12
+ DB 20H
+ DB 'ar'
+ DW GRP12
+ DB 38H
+ DB 'eg'
+ DW GRP21
+ DB 26H
+S4:
+ DB 5
+ DB 'cab'
+ DW PUT
+ DB 0AEH
+ DB 'caw'
+ DW PUT
+ DB 0AFH
+ DB 'tob'
+ DW PUT
+ DB 0AAH
+ DB 'tow'
+ DW PUT
+ DB 0ABH
+ DB 'ahf'
+ DW PUT
+ DB 9EH
+S5:
+ DB 4
+ DB 'casb'
+ DW PUT
+ DB 0AEH
+ DB 'casw'
+ DW PUT
+ DB 0AFH
+ DB 'tosb'
+ DW PUT
+ DB 0AAH
+ DB 'tosw'
+ DW PUT
+ DB 0ABH
+T4:
+ DB 1
+ DB 'est'
+ DW GRP20
+ DB 84H
+U2:
+ DB 1
+ DB 'p'
+ DW PUT
+ DB 0FCH
+W4:
+ DB 1
+ DB 'ait'
+ DW PUT
+ DB 9BH
+X3:
+ DB 1
+ DB 'or'
+ DW GRP13
+ DB 32H
+X4:
+ DB 2
+ DB 'chg'
+ DW GRP3
+ DB 86H
+ DB 'lat'
+ DW PUT
+ DB 0D7H
+
+
+; 8087 MNEMONIC TABLE
+; Similar to 8086 table above, except NOT distinguished by opcode length
+
+XM1: ;F2XM1
+ DB 1 ;One opcode
+ DM "xm1"
+ DB 1,0F0H
+
+NDPA:
+ DB 3
+ DM "dd"
+ DB 6+ARITH,0C1H
+ DM "ddp"
+ DB NEEDOP+STACKOP,0
+ DM "bs"
+ DB 1,0E1H
+
+NDPB:
+ DB 2
+ DM "ld"
+ DB 7+NEEDOP+MEMORY,20H
+ DM "stp"
+ DB 7+NEEDOP+MEMORY,30H
+
+NDPC:
+ DB 5
+ DM "om"
+ DB 0+ONEREG+REAL,0D1H
+ DM "omp"
+ DB 0+ONEREG+REAL,0D9H
+ DM "hs"
+ DB 1,0E0H
+ DM "ompp"
+ DB 6,0D9H
+ DM "lex"
+ DB 3,0E2H
+
+NDPD:
+ DB 6
+ DM "iv"
+ DB 6+ARITH,0F1H
+ DM "ivp"
+ DB NEEDOP+STACKOP,30H
+ DM "ivr"
+ DB 6+ARITH,0F9H
+ DM "ivrp"
+ DB NEEDOP+STACKOP,38H
+ DM "ecstp"
+ DB 1,0F6H
+ DM "isi"
+ DB 3,0E1H
+
+NDPE:
+ DB 1
+ DM "ni"
+ DB 3,0E0H
+
+NDPF:
+ DB 1
+ DM "ree"
+ DB 5+NEEDOP+ONEREG,0
+
+NDPI:
+ DB 13
+ DM "add"
+ DB 2+NEEDOP+INTEGER,0
+ DM "ld"
+ DB 3+NEEDOP+INTEGER+EXTENDED,0
+ DM "sub"
+ DB 2+NEEDOP+INTEGER,20H
+ DM "stp"
+ DB 3+NEEDOP+INTEGER+EXTENDED,18H
+ DM "st"
+ DB 3+NEEDOP+INTEGER,10H
+ DM "mul"
+ DB 2+NEEDOP+INTEGER,8
+ DM "div"
+ DB 2+NEEDOP+INTEGER,30H
+ DM "subr"
+ DB 2+NEEDOP+INTEGER,28H
+ DM "divr"
+ DB 2+NEEDOP+INTEGER,38H
+ DM "com"
+ DB 2+NEEDOP+INTEGER,10H
+ DM "comp"
+ DB 2+NEEDOP+INTEGER,18H
+ DM "ncstp"
+ DB 1,0F7H
+ DM "nit"
+ DB 3,0E3H
+
+NDPL:
+ DB 10
+ DM "d"
+ DB 1+NEEDOP+ONEREG+REAL+EXTENDED,0
+ DM "dz"
+ DB 1,0EEH
+ DM "d1"
+ DB 1,0E8H
+ DM "dpi"
+ DB 1,0EBH
+ DM "dl2t"
+ DB 1,0E9H
+ DM "dl2e"
+ DB 1,0EAH
+ DM "dlg2"
+ DB 1,0ECH
+ DM "dln2"
+ DB 1,0EDH
+ DM "dcw"
+ DB 1+NEEDOP+MEMORY,28H
+ DM "denv"
+ DB 1+NEEDOP+MEMORY,20H
+
+NDPM:
+ DB 2
+ DM "ul"
+ DB 6+ARITH,0C9H
+ DM "ulp"
+ DB NEEDOP+STACKOP,8
+
+NDPO:
+ DB 1
+ DM "p"
+ DB NEEDOP+1,0 ;Flag special handling
+
+NDPN:
+ DB 1
+ DM "op"
+ DB 1,0D0H
+
+NDPP:
+ DB 3
+ DM "rem"
+ DB 1,0F8H
+ DM "tan"
+ DB 1,0F2H
+ DM "atan"
+ DB 1,0F3H
+
+NDPR:
+ DB 2
+ DM "ndint"
+ DB 1,0FCH
+ DM "stor"
+ DB 5+NEEDOP+MEMORY,20H
+
+NDPS:
+ DB 12
+ DM "t"
+ DB 5+NEEDOP+ONEREG+REAL,0D0H
+ DM "tp"
+ DB 7+NEEDOP+ONEREG+REAL+EXTENDED,0D8H
+ DM "ub"
+ DB 6+ARITH,0E1H
+ DM "ubp"
+ DB NEEDOP+STACKOP,0E0H
+ DM "ubr"
+ DB 6+ARITH,0E9H
+ DM "ubrp"
+ DB NEEDOP+STACKOP,0E8H
+ DM "qrt"
+ DB 1,0FAH
+ DM "cale"
+ DB 1,0FDH
+ DM "ave"
+ DB 5+NEEDOP+MEMORY,30H
+ DM "tcw"
+ DB 1+NEEDOP+MEMORY,38H
+ DM "tenv"
+ DB 1+NEEDOP+MEMORY,30H
+ DM "tsw"
+ DB 5+NEEDOP+MEMORY,38H
+
+NDPT:
+ DB 1
+ DM "st"
+ DB 1,0E4H
+
+NDPW:
+ DB 1
+ DM "ait"
+ DB NEEDOP,0 ;Flag special handling
+
+NDPX:
+ DB 3
+ DM "ch"
+ DB 1+ONEREG,0C9H
+ DM "am"
+ DB 1,0E5H
+ DM "tract"
+ DB 1,0F4H
+
+NDPY:
+ DB 2
+ DM "l2x"
+ DB 1,0F1H
+ DM "l2xp1"
+ DB 1,0F9H
+
+
+OPTAB:
+; Table of pointers to mnemonics. For each letter of the alphabet (the
+; starting letter of the mnemonic), there are 5 entries. Each entry
+; corresponds to a mnemonic whose length is 2, 3, 4, 5, and 6 characters
+; long, respectively. If there are no mnemonics for a given combination
+; of first letter and length (such as A-2), then the corresponding entry
+; points to NONE. Otherwise, it points to a place in the mnemonic table
+; for that type.
+
+; This table only needs to be modified if a mnemonic is added to a group
+; previously marked NONE. Change the NONE to a label made up of the first
+; letter of the mnemonic and its length, then add a new subsection to
+; the mnemonic table in alphabetical order.
+
+ DW NONE
+ DW A3
+ DW NONE
+ DW A5
+ DW NONE
+ DW NONE ;B
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;C
+ DW C3
+ DW C4
+ DW C5
+ DW NONE
+ DW D2 ;D
+ DW D3
+ DW D4
+ DW NONE
+ DW NONE
+ DW E2 ;E
+ DW E3
+ DW NONE
+ DW E5
+ DW NONE
+ DW NONE ;F
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;G
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;H
+ DW H3
+ DW H4
+ DW NONE
+ DW NONE
+ DW I2 ;I
+ DW I3
+ DW I4
+ DW NONE
+ DW NONE
+ DW J2 ;J
+ DW J3
+ DW J4
+ DW NONE
+ DW NONE
+ DW NONE ;K
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;L
+ DW L3
+ DW L4
+ DW L5
+ DW L6
+ DW NONE ;M
+ DW M3
+ DW M4
+ DW M5
+ DW NONE
+ DW NONE ;N
+ DW N3
+ DW NONE
+ DW NONE
+ DW NONE
+ DW O2 ;O
+ DW O3
+ DW O4
+ DW NONE
+ DW NONE
+ DW NONE ;P
+ DW P3
+ DW P4
+ DW P5
+ DW NONE
+ DW NONE ;Q
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;R
+ DW R3
+ DW R4
+ DW R5
+ DW NONE
+ DW NONE ;S
+ DW S3
+ DW S4
+ DW S5
+ DW NONE
+ DW NONE ;T
+ DW NONE
+ DW T4
+ DW NONE
+ DW NONE
+ DW U2 ;U
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;V
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;W
+ DW NONE
+ DW W4
+ DW NONE
+ DW NONE
+ DW NONE ;X
+ DW X3
+ DW X4
+ DW NONE
+ DW NONE
+ DW NONE ;Y
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE ;Z
+ DW NONE
+ DW NONE
+ DW NONE
+ DW NONE
+
+NDPTAB:
+;Lookup table for 8087 mnemonics. There is one entry for each letter of the
+;alphabet
+ DW NDPA
+ DW NDPB
+ DW NDPC
+ DW NDPD
+ DW NDPE
+ DW NDPF
+ DW NONE ;G
+ DW NONE ;H
+ DW NDPI
+ DW NONE ;J
+ DW NONE ;K
+ DW NDPL
+ DW NDPM
+ DW NDPN
+ DW NDPO
+ DW NDPP
+ DW NONE ;Q
+ DW NDPR
+ DW NDPS
+ DW NDPT
+ DW NONE ;U
+ DW NONE ;V
+ DW NDPW
+ DW NDPX
+ DW NDPY
+ DW NONE ;Z
+
+;Error message table
+
+ERRTAB:
+ DM 1,"Register not allowed in immediate value"
+ DM 2,"Index or base register must be BP, BX, SI, or DI"
+ DM 3,"Only one base register (BX, BP) allowed"
+ DM 4,"Only one index register (SI or DI) allowed"
+ DM 5,"Only addition allowed on register or undefined label"
+ DM 6,"Only one undefined label per expression allowed"
+ DM 7,"Illegal digit in hexadecimal number"
+ DM 8,"Illegal digit in decimal number"
+ DM 10,"Illegal character in label or opcode"
+ DM 11,"Label defined twice"
+ DM 12,"Opcode not recognized"
+ DM 20,"Invalid operand"
+ DM 21,'"," and second operand expected'
+ DM 22,"Register mismatch"
+ DM 23,"Immediate operand not allowed"
+ DM 24,'"]" expected'
+ DM 25,"Two memory operands not allowed"
+ DM 26,"Destination must not be immediate value"
+ DM 27,"Both operands must not be registers"
+ DM 28,"Operand must be segment register"
+ DM 29,"First operand must be register"
+ DM 30,"Undefined label not allowed"
+ DM 31,"Value out of range"
+ DM 32,"Missing or illegal operand size flag"
+ DM 33,"Must have label on same line"
+ DM 35,"Zero-length string illegal"
+ DM 36,"ENDIF without IF"
+ DM 37,"One-character strings only"
+ DM 38,"Illegal expression"
+ DM 39,"End of string not found"
+ DM 100,"Undefined label"
+ DM 101,"Value out of range (forward)"
+ DB 255
+
+ERRMES: DM '***** ERROR: '
+NOSPAC: DB 13,10,'File creation error',13,10,"$"
+NOMEM: DB 13,10,'Insufficient memory',13,10,'$'
+NOFILE: DB 13,10,'File not found',13,10,'$'
+WRTERR: DB 13,10,'Disk full',13,10,'$'
+BADDSK: DB 13,10,'Bad disk specifier',13,10,'$'
+ERCNTM: DM 13,10,13,10,'Error Count ='
+SYMSIZE DM 13,10,'Symbol Table size = '
+FRESIZE DM 'Free space = '
+SYMMES: DM 13,10,'Symbol Table',13,10,13,10
+EXTEND: DB 'ASM',0,0
+IFEND: DB 5,'endif'
+IFNEST: DB 2,'if'
+RETSTR: DM 'ret'
+HEXFCB: DB 0,' HEX',0,0,0,0
+ DS 16
+ DB 0,0,0,0,0
+LSTFCB: DB 0,' PRN',0,0,0,0
+ DS 16
+ DB 0,0,0,0,0
+PC: DS 2
+OLDPC: DS 2
+LABPT: DS 2
+FLAG: DS 1
+MAXFLG: DS 1
+ADDR: DS 2
+ALABEL: DS 2
+DATA: DS 2
+DLABEL: DS 2
+CON: DS 2
+UNDEF: DS 2
+LENID: DS 1
+ID: DS 80
+CHR: DS 1
+SYM: DS 1
+BASE: DS 2
+HEAP: DS 2
+SYMFLG: DS 1
+SYMLIN: DS 1
+CODE: DS 2
+DATSIZ: DS 1
+RELOC: DS 1
+BCOUNT: DS 1
+COUNT: DS 1
+ERR: DS 1
+LINE: DS 2
+HEXLEN: DS 2
+HEXADD: DS 2
+LASTAD: DS 2
+HEXCNT: DS 1
+CHKSUM: DS 1
+LINFLG: DS 1
+PREV: DS 1
+IFFLG: DS 1
+CHKLAB: DS 1
+ERRCNT: DS 2
+LSTRET: DS 2
+RETPT: DS 2
+LSTDEV: DS 2
+SPC: DS 1
+NOWAIT: DS 1
+IX: DS 2
+IY: DS 2
+HEXPNT: DS 2
+LSTPNT: DS 2
+HEXBUF: DS HEXBUFSIZ
+LSTBUF: DS LSTBUFSIZ
+BUFPT: DS 2
+SRCBUF: DS BUFSIZ
+ DS 100H
+ ALIGN
+STACK: EQU $
+START: EQU $
+\1a
\ No newline at end of file
--- /dev/null
+; COMMAND version 1.17
+;
+; This version of COMMAND is divided into three distinct parts. First
+; is the resident portion, which includes handlers for interrupts
+; 22H (terminate), 23H (Cntrl-C), 24H (fatal error), and 27H (stay
+; resident); it also has code to test and, if necessary, reload the
+; transient portion. Following the resident is the init code, which is
+; overwritten after use. Then comes the transient portion, which
+; includes all command processing (whether internal or external).
+; The transient portion loads at the end of physical memory, and it may
+; be overlayed by programs that need as much memory as possible. When
+; the resident portion of command regains control from a user program,
+; a checksum is performed on the transient portion to see if it must be
+; reloaded. Thus programs which do not need maximum memory will save
+; the time required to reload COMMAND when they terminate.
+
+;Use the following booleans to set assembly flags
+FALSE EQU 0
+TRUE EQU NOT FALSE
+
+IBMVER EQU FALSE ;Switch to build IBM version of Command
+MSVER EQU TRUE ;Switch to build MS-DOS version of Command
+
+HIGHMEM EQU TRUE ;Run resident part above transient (high memory)
+
+LINPERPAG EQU 23
+NORMPERLIN EQU 1
+WIDEPERLIN EQU 5
+
+ IF IBMVER
+SYM EQU ">"
+COMDRV EQU 1
+ ENDIF
+
+ IF MSVER
+SYM EQU ":"
+COMDRV EQU 0
+ ENDIF
+
+FCB EQU 5CH
+DSKRESET EQU 13
+SETBASE EQU 38
+SRCHFRST EQU 17
+SRCHNXT EQU 18
+RENAM EQU 23
+INCHAR EQU 1
+GETFAT EQU 27
+OPEN EQU 15
+CLOSE EQU 16
+MAKE EQU 22
+DELETE EQU 19
+RDBLK EQU 39
+WRBLK EQU 40
+SETDMA EQU 26
+SELDRV EQU 14
+GETDRV EQU 25
+PRINTBUF EQU 9
+OUTCH EQU 2
+INBUF EQU 10
+GETDATE EQU 2AH
+SETDATE EQU 2BH
+GETTIME EQU 2CH
+SETTIME EQU 2DH
+RR EQU 33
+RECLEN EQU 14
+FILLEN EQU 16
+OFFDATE EQU 20
+
+
+;The following are all of the segments used in the load order
+
+CODERES SEGMENT
+CODERES ENDS
+
+DATARES SEGMENT BYTE
+DATARES ENDS
+
+INIT SEGMENT BYTE
+INIT ENDS
+
+TAIL SEGMENT PARA
+TAIL ENDS
+
+TRANCODE SEGMENT PARA
+TRANCODE ENDS
+
+TRANDATA SEGMENT BYTE
+TRANDATA ENDS
+
+TRANSPACE SEGMENT BYTE
+TRANSPACE ENDS
+
+RESGROUP GROUP CODERES,DATARES,INIT,TAIL
+TRANGROUP GROUP TRANCODE,TRANDATA,TRANSPACE
+
+;Data for resident portion
+
+DATARES SEGMENT BYTE
+ ORG 0
+ZERO = $
+MESBAS DW OFFSET RESGROUP:ERR0
+ DW OFFSET RESGROUP:ERR2
+ DW OFFSET RESGROUP:ERR4
+ DW OFFSET RESGROUP:ERR6
+ DW OFFSET RESGROUP:ERR8
+ DW OFFSET RESGROUP:ERR10
+ DW OFFSET RESGROUP:ERR12
+ERR0 DB "Write protect$"
+ERR2 DB "Not ready$"
+ERR4 DB "Data$"
+ERR6 DB "Seek$"
+ERR8 DB "Sector not found$"
+ERR10 DB "Write fault$"
+ERR12 DB "Disk$"
+READ DB "read$"
+WRITE DB "writ$"
+ERRMES DB " error "
+IOTYP DB "writing"
+DRVNUM DB " drive "
+DRVLET DB "A"
+NEWLIN DB 13,10,"$"
+REQUEST DB "Abort, Retry, Ignore? $"
+BADFAT DB 13,10,"File allocation table bad,$"
+COMBAD DB 13,10,"Invalid COMMAND.COM"
+NEEDCOM DB 13,10,"Insert DOS disk in "
+ IF IBMVER
+ DB "drive A"
+ ELSE
+ DB "default drive"
+ ENDIF
+PROMPT DB 13,10,"and strike any key when ready",13,10,"$"
+NEEDBAT DB 13,10,"Insert disk with batch file$"
+ENDBATMES DB 13,10,"Terminate batch job (Y/N)? $"
+LOADING DB 0
+BATFCB DB 1,"AUTOEXECBAT"
+ DB 21 DUP(?)
+ DW 0
+ DW 0 ;Initialize RR field to zero
+PARMTAB DW 10 DUP(-1) ;No parameters initially
+BATCH DB 1 ;Assume batch mode initially
+COMFCB DB COMDRV,"COMMAND COM"
+ DB 25 DUP(?)
+TRANS DW OFFSET TRANGROUP:COMMAND
+TRNSEG DW ?
+BATBYT DB ?
+MEMSIZ DW ?
+SUM DW ?
+INITADD DB 4 DUP(?)
+RESDATASIZE EQU $-ZERO
+DATARES ENDS
+
+;Data for transient portion
+
+TRANDATA SEGMENT BYTE
+ ORG 0
+ZERO EQU $
+BADNAM DB "Bad command or file name",13,10,"$"
+MISNAM DB "Missing file name$"
+RENERR DB "Duplicate file name or "
+NOTFND DB "File not found$"
+EXEBAD DB "Error in EXE file$"
+NOSPACE DB "Insufficient disk space",13,10,"$"
+FULDIR DB "File creation error",13,10,"$"
+OVERWR DB "File cannot be copied onto itself",13,10,"$"
+LOSTERR DB "Content of destination lost before copy",13,10,"$"
+COPIED DB " File(s) copied$"
+DIRMES DB " File(s)$"
+TOOBIG DB "Program too big to fit in memory$"
+BADDRV DB "Invalid drive specification$"
+PAUSMES DB "Strike a key when ready . . . $"
+BADSWT DB "Illegal switch",13,10,"$"
+WEEKTAB DB "SunMonTueWedThuFriSat"
+BADDAT DB 13,10,"Invalid date$"
+CURDAT DB "Current date is $"
+NEWDAT DB 13,10,"Enter new date: $"
+BADTIM DB 13,10,"Invalid time$"
+CURTIM DB "Current time is $"
+NEWTIM DB 13,10,"Enter new time: $"
+SUREMES DB "Are you sure (Y/N)? $"
+
+COMTAB DB 4,"DIR",1
+ DW OFFSET TRANGROUP:CATALOG
+ DB 7,"RENAME",1
+ DW OFFSET TRANGROUP:RENAME
+ DB 4,"REN",1
+ DW OFFSET TRANGROUP:RENAME
+ DB 6,"ERASE",1
+ DW OFFSET TRANGROUP:ERASE
+ DB 4,"DEL",1
+ DW OFFSET TRANGROUP:ERASE
+ DB 5,"TYPE",1
+ DW OFFSET TRANGROUP:TYPEFIL
+ DB 4,"REM",1
+ DW OFFSET TRANGROUP:COMMAND
+ DB 5,"COPY",1
+ DW OFFSET TRANGROUP:COPY
+ DB 6,"PAUSE",1
+ DW OFFSET TRANGROUP:PAUSE
+ DB 5,"DATE",0
+ DW OFFSET TRANGROUP:DATE
+ DB 5,"TIME",0
+ DW OFFSET TRANGROUP:TIME
+ DB 0 ;Terminate command table
+
+COMBUF DB 128,1,13
+
+TRANDATASIZE EQU $-ZERO
+TRANDATA ENDS
+
+;Uninitialized transient data
+TRANSPACE SEGMENT BYTE
+ ORG 0
+ZERO = $
+ DB 128 DUP(?)
+TPA DW 1 DUP(?)
+RESSEG DW 1 DUP(?)
+CHKDRV DB 1 DUP(?)
+FILTYP DB 1 DUP(?)
+CURDRV DB 1 DUP(?)
+PARM1 DB 1 DUP(?)
+PARM2 DB 1 DUP(?)
+COMSW DW 1 DUP(?)
+ARG1S DW 1 DUP(?)
+ARG2S DW 1 DUP(?)
+FLAGER DB 1 DUP(?)
+CFLAG DB 1 DUP(?)
+SPECDRV DB 1 DUP(?)
+BYTCNT DW 1 DUP(?)
+NXTADD DW 1 DUP(?)
+LINCNT DB 1 DUP(?)
+LINLEN DB 1 DUP(?)
+FILECNT DW 1 DUP(?)
+EXEFCB LABEL WORD
+IDLEN DB 1 DUP(?)
+ID DB 8 DUP(?)
+COM DB 3 DUP(?)
+DEST DB 37 DUP(?)
+DESTNAME DB 11 DUP(?)
+DIRBUF DB 37 DUP(?)
+BITS DW 1 DUP(?)
+FULLSCR DW 1 DUP(?)
+EXEEND DW 1 DUP(?)
+;Header variables for EXE file load
+;These are overlapped with COPY variables, below
+RUNVAR LABEL WORD
+RELPT DW 1 DUP(?)
+RELSEG DW 1 DUP(?)
+PSIZE LABEL WORD
+PAGES DW 1 DUP(?)
+RELCNT DW 1 DUP(?)
+HEADSIZ DW 1 DUP(?)
+ DW 1 DUP(?)
+LOADLOW DW 1 DUP(?)
+INITSS DW 1 DUP(?)
+INITSP DW 1 DUP(?)
+ DW 1 DUP(?)
+INITIP DW 1 DUP(?)
+INITCS DW 1 DUP(?)
+RELTAB DW 1 DUP(?)
+RUNVARSIZ EQU $-RUNVAR
+
+ DB 80H DUP(?)
+STACK LABEL WORD
+
+PRETRLEN EQU $-ZERO ;Used later to compute TRNLEN
+
+ ORG RUNVAR-ZERO ;Overlaps EXE variables
+
+SRCPT DW 1 DUP(?)
+INEXACT DB 1 DUP(?)
+APPEND DB 1 DUP(?)
+NOWRITE DB 1 DUP(?)
+ASCII DB 1 DUP(?)
+PLUS DB 1 DUP(?)
+SOURCE DB 11 DUP(?)
+TRANSPACESIZE EQU $-ZERO
+TRANSPACE ENDS
+
+
+;START OF RESIDENT PORTION
+
+CODERES SEGMENT
+ASSUME CS:RESGROUP,DS:RESGROUP,ES:RESGROUP,SS:RESGROUP
+ ORG 0
+ZERO = $
+PARMBUF LABEL WORD
+
+ ORG 100H
+
+RSTACK LABEL WORD
+
+PROGSTART:
+ JMP CONPROC
+
+LTPA DW 0 ;WILL STORE TPA SEGMENT HERE
+MYSEG DW 0 ;Put our own segment here
+
+CONTC:
+ MOV AX,CS
+ MOV DS,AX
+ MOV SS,AX
+ MOV SP,OFFSET RESGROUP:RSTACK
+ STI
+ CALL SETVECT
+ MOV AH,DSKRESET
+ INT 33 ;Reset disks in case files were open
+ TEST [BATCH],-1
+ JZ LODCOM
+ASKEND:
+ MOV DX,OFFSET RESGROUP:ENDBATMES
+ MOV AH,PRINTBUF
+ INT 33
+ MOV AX,0C00H+INCHAR
+ INT 33
+ AND AL,5FH
+ CMP AL,"N"
+ JZ LODCOM
+ CMP AL,"Y"
+ JNZ ASKEND
+ MOV [BATCH],0
+LODCOM:
+ MOV AX,CS
+ MOV SS,AX
+ MOV SP,OFFSET RESGROUP:RSTACK
+ MOV DS,AX
+ CALL SETVECT
+ CALL CHKSUM
+ CMP DX,[SUM]
+ JZ HAVCOM
+ MOV [LOADING],1
+ CALL LOADCOM
+CHKSAME:
+ CALL CHKSUM
+ CMP DX,[SUM]
+ JZ HAVCOM
+ CALL WRONGCOM
+ JMP SHORT CHKSAME
+HAVCOM:
+ MOV [LOADING],0
+ MOV SI,OFFSET RESGROUP:LTPA
+ MOV DI,OFFSET TRANGROUP:TPA
+ MOV ES,[TRNSEG]
+ CLD
+ MOVSW ;Move TPA segment to transient storage
+ MOVSW ;Move resident segment too
+ MOV AX,[MEMSIZ]
+ MOV WORD PTR ES:[2],AX
+ JMP DWORD PTR [TRANS]
+
+RESIDENT:
+ ADD DX,15
+ MOV CL,4
+ SHR DX,CL ;Number of paragraphs of new addition
+ ADD CS:[LTPA],DX
+ XOR AX,AX
+ MOV DS,AX
+ JMP DWORD PTR DS:[80H] ;Pretend user executed INT 20H
+
+DSKERR:
+ ;******************************************************
+ ; THIS IS THE DEFAULT DISK ERROR HANDLING CODE
+ ; AVAILABLE TO ALL USERS IF THEY DO NOT TRY TO
+ ; INTERCEPT INTERRUPT 24H.
+ ;******************************************************
+ STI
+ PUSH DS
+ PUSH CS
+ POP DS ;Set up local data segment
+ PUSH DX
+ CALL CRLF
+ POP DX
+ ADD AL,"A" ;Compute drive letter
+ MOV [DRVLET],AL
+ TEST AH,80H ;Check if hard disk error
+ JNZ FATERR
+ MOV SI,OFFSET RESGROUP:READ
+ TEST AH,1
+ JZ SAVMES
+ MOV SI,OFFSET RESGROUP:WRITE
+SAVMES:
+ LODSW
+ MOV WORD PTR [IOTYP],AX
+ LODSW
+ MOV WORD PTR [IOTYP+2],AX
+ AND DI,0FFH
+ CMP DI,12
+ JBE HAVCOD
+ MOV DI,12
+HAVCOD:
+ MOV DI,WORD PTR [DI+MESBAS] ;Get pointer to error message
+ XCHG DI,DX ;May need DX later
+ MOV AH,PRINTBUF
+ INT 33 ;Print error type
+ MOV DX,OFFSET RESGROUP:ERRMES
+ INT 33
+ CMP [LOADING],0
+ JNZ GETCOMDSK
+ASK:
+ MOV DX,OFFSET RESGROUP:REQUEST
+ MOV AH,PRINTBUF
+ INT 33
+ MOV AX,0C00H+INCHAR
+ INT 33 ;Get response
+ CALL CRLF
+ OR AL,20H ;Convert to lower case
+ MOV AH,0 ;Return code for ignore
+ CMP AL,"i" ;Ignore?
+ JZ EXIT
+ INC AH
+ CMP AL,"r" ;Retry?
+ JZ EXIT
+ INC AH
+ CMP AL,"a" ;Abort?
+ JNZ ASK
+EXIT:
+ MOV AL,AH
+ MOV DX,DI
+ POP DS
+ IRET
+
+FATERR:
+ MOV DX,OFFSET RESGROUP:BADFAT
+ MOV AH,PRINTBUF
+ INT 33
+ MOV DX,OFFSET RESGROUP:DRVNUM
+ INT 33
+ MOV AL,2 ;Abort
+ POP DS
+ IRET
+
+GETCOMDSK:
+ MOV DX,OFFSET RESGROUP:NEEDCOM
+ MOV AH,PRINTBUF
+ INT 33
+ MOV AX,0C07H ;Get char without testing or echo
+ INT 33
+ JMP LODCOM
+
+CRLF:
+ MOV DX,OFFSET RESGROUP:NEWLIN
+ PUSH AX
+ MOV AH,PRINTBUF
+ INT 33
+ POP AX
+RET10: RET
+
+LOADCOM:
+ PUSH DS
+ MOV DS,[TRNSEG]
+ MOV DX,100H
+ MOV AH,SETDMA
+ INT 33
+ POP DS
+ MOV DX,OFFSET RESGROUP:COMFCB
+ MOV AH,OPEN
+ INT 33 ;Open COMMAND.COM
+ OR AL,AL
+ JZ READCOM
+ MOV DX,OFFSET RESGROUP:NEEDCOM
+PROMPTCOM:
+ MOV AH,PRINTBUF
+ INT 33
+ MOV AX,0C07H ;Get char without testing or echo
+ INT 33
+ JMP SHORT LOADCOM
+READCOM:
+ MOV WORD PTR[COMFCB+RR],OFFSET RESGROUP:TRANSTART
+ XOR AX,AX
+ MOV WORD PTR[COMFCB+RR+2],AX
+ MOV [COMFCB],AL ;Use default drive
+ INC AX
+ MOV WORD PTR[COMFCB+RECLEN],AX
+ MOV CX,COMLEN
+ MOV DX,OFFSET RESGROUP:COMFCB
+ MOV AH,RDBLK
+ INT 33
+ OR AL,AL
+ JZ RET10
+WRONGCOM:
+ MOV DX,OFFSET RESGROUP:COMBAD
+ JMP SHORT PROMPTCOM
+
+CHKSUM:
+ CLD
+ PUSH DS
+ MOV DS,[TRNSEG]
+ MOV SI,100H
+ MOV CX,COMLEN
+ SHR CX,1
+ XOR DX,DX
+CHK:
+ LODSW
+ ADD DX,AX
+ LOOP CHK
+ POP DS
+ RET
+
+SETVECT:
+ MOV DX,OFFSET RESGROUP:LODCOM
+ MOV AX,2522H ;Set Terminate address
+ INT 21H
+ MOV DX,OFFSET RESGROUP:CONTC
+ MOV AX,2523H ;Set Ctrl-C address
+ INT 21H
+ MOV DX,OFFSET RESGROUP:DSKERR
+ MOV AX,2524H ;Set Hard Disk Error address
+ INT 33
+ MOV DX,OFFSET RESGROUP:RESIDENT
+ MOV AX,2527H ;Set Terminate and Stay Resident address
+ INT 33
+ RET
+RESCODESIZE EQU $-ZERO
+CODERES ENDS
+
+;*******************************************************************
+;START OF INIT PORTION
+;This code is overlayed the first time the TPA is used.
+
+INIT SEGMENT BYTE
+
+ ORG 0
+ZERO = $
+CONPROC:
+ MOV SP,OFFSET RESGROUP:RSTACK
+
+ IF HIGHMEM
+ MOV AX,WORD PTR DS:[2]
+ SUB AX,((RESCODESIZE+RESDATASIZE)+15)/16 ;Subtract size of resident
+ MOV WORD PTR DS:[2],AX
+ MOV ES,AX
+ MOV SI,100H
+ MOV DI,SI
+ MOV CX,((RESCODESIZE+RESDATASIZE)-100H+1)/2 ;Length of resident in words
+ REP MOVSW ;Move to end of memory
+ MOV DS,AX
+ MOV [LTPA],CS
+ ENDIF
+
+ IF NOT HIGHMEM
+ MOV AX,CS
+ ADD AX,((RESCODESIZE+RESDATASIZE)+15)/16 ;Compute segment of TPA
+ MOV [LTPA],AX
+ MOV AX,WORD PTR DS:[2]
+ ENDIF
+
+ MOV [MYSEG],DS
+ MOV [MEMSIZ],AX
+ SUB AX,TRNLEN ;Subtract size of transient
+ MOV [TRNSEG],AX
+ CALL SETVECT
+ CALL LOADCOM
+ CALL CHKSUM
+ MOV [SUM],DX
+
+ IF MSVER
+ IF HIGHMEM
+ PUSH DS
+ PUSH CS
+ POP DS
+ ENDIF
+ MOV DX,OFFSET RESGROUP:HEADER
+ MOV AH,PRINTBUF
+ INT 33
+ IF HIGHMEM
+ POP DS
+ ENDIF
+ ENDIF
+
+ MOV DX,OFFSET RESGROUP:BATFCB
+ MOV AH,OPEN
+ INT 33 ;See if AUTOEXEC.BAT exists
+ MOV WORD PTR[BATFCB+RECLEN],1 ;Set record length to 1
+ OR AL,AL ;Zero means file found
+ JZ DRV0
+ MOV [BATCH],0 ;Not found--turn off batch job
+ MOV AX,OFFSET TRANGROUP:DATINIT
+ MOV WORD PTR[INITADD],AX
+ MOV AX,[TRNSEG]
+ MOV WORD PTR[INITADD+2],AX
+ CALL DWORD PTR DS:[INITADD]
+
+ IF IBMVER
+ MOV DX,OFFSET RESGROUP:HEADER
+ MOV AH,PRINTBUF
+ INT 33
+ ENDIF
+
+DRV0:
+ JMP HAVCOM
+
+
+ IF MSVER
+HEADER DB 13,10,"Command v. 1.17"
+ IF HIGHMEM
+ DB "H"
+ ENDIF
+ DB 13,10,"$"
+ ENDIF
+
+ IF IBMVER
+HEADER DB 13,10,13,10,"The IBM Personal Computer DOS",13,10
+ DB "Version 1.10 (C)Copyright IBM Corp 1981, 1982",13,10,"$"
+ DB "Licensed Material - Program Property of IBM"
+ ENDIF
+
+INITSIZE EQU $-ZERO
+INIT ENDS
+
+;This TAIL segment is used to produce a PARA aligned label in the resident
+; group which is the location where the transient segments will be loaded
+; initialy.
+
+TAIL SEGMENT PARA
+ ORG 0
+TRANSTART LABEL WORD
+TAIL ENDS
+
+;********************************************************************
+;START OF TRANSIENT PORTION
+;This code is loaded at the end of memory and may be overwritten by
+;memory-intensive user programs.
+
+TRANCODE SEGMENT PARA
+ASSUME CS:TRANGROUP,DS:TRANGROUP,ES:TRANGROUP,SS:TRANGROUP
+
+WSWITCH EQU 1 ;Wide display during DIR
+PSWITCH EQU 2 ;Pause (or Page) mode during DIR
+VSWITCH EQU 4 ;Verify during COPY
+ASWITCH EQU 8 ;ASCII mode during COPY
+BSWITCH EQU 10H ;Binary mode during COPY
+
+ ORG 0
+ZERO = $
+
+ ORG 100H ;Allow for 100H parameter area
+
+SETDRV:
+ MOV AH,SELDRV
+ INT 21H
+COMMAND:
+ CLD
+ MOV AX,CS
+ MOV SS,AX
+ MOV SP,OFFSET TRANGROUP:STACK
+ MOV ES,AX
+ MOV DS,AX
+ STI
+ MOV AX,46*100H
+ MOV DL,0
+ INT 33 ;Turn off verify after write
+ MOV AX,CS ;Get segment we're in
+ SUB AX,[TPA] ;AX=size ot TPA in paragraphs
+ MOV DX,16
+ MUL DX ;DX:AX=size of TPA in bytes
+ OR DX,DX ;See if over 64K
+ JZ SAVSIZ ;OK if not
+ MOV AX,-1 ;If so, limit to 65535 bytes
+SAVSIZ:
+ MOV [BYTCNT],AX ;Max no. of bytes that can be buffered
+ CALL CRLF2
+GETCOM:
+ MOV AH,GETDRV
+ INT 21H
+ MOV [CURDRV],AL
+ ADD AL,"A"
+ CALL OUT ;Print letter for default drive
+ MOV AL,SYM
+ CALL OUT
+ MOV DS,[RESSEG] ;All batch work must use resident seg.
+ASSUME DS:RESGROUP
+ TEST [BATCH],-1
+ JNZ READBAT
+ PUSH CS
+ POP DS ;Need local segment to point to buffer
+ASSUME DS:TRANGROUP
+ MOV DX,OFFSET TRANGROUP:COMBUF
+ MOV AH,INBUF
+ INT 21H ;Get a command
+ JMP DOCOM
+
+;All batch proccessing has DS set to segment of resident portion
+ASSUME DS:RESGROUP
+NEEDPARM:
+ CALL GETBATBYT
+ CMP AL,"%" ;Check for two consecutive %
+ JZ SAVBATBYT
+ CMP AL,13 ;Check for end-of-line
+ JZ SAVBATBYT
+ SUB AL,"0"
+ JB RDBAT ;Ignore parameter reference if invalid
+ CMP AL,9
+ JA RDBAT
+ CBW
+ MOV SI,AX
+ SHL SI,1 ;Two bytes per entry
+ MOV SI,[SI+OFFSET RESGROUP:PARMTAB] ;Get pointer to corresponding parameter
+ CMP SI,-1 ;Check if parameter exists
+ JZ RDBAT ;Ignore if it doesn't
+ MOV AH,OUTCH
+RDPARM:
+ LODSB ;From resident segment
+ CMP AL,0DH ;Check for end of parameter
+ JZ RDBAT
+ STOSB ;To transient segment
+ MOV DL,AL
+ INT 33 ;Display paramters too
+ JMP SHORT RDPARM
+
+PROMPTBAT:
+ MOV AH,PRINTBUF
+ MOV DX,OFFSET RESGROUP:NEEDBAT
+ INT 33 ;Prompt for batch file
+ MOV AH,PRINTBUF
+ MOV DX,OFFSET RESGROUP:PROMPT
+ INT 33
+ MOV AX,0C00H+INCHAR
+ INT 33
+ JMP COMMAND
+
+BADCOMJ1:JMP BADCOM
+
+READBAT:
+ MOV DX,OFFSET RESGROUP:BATFCB
+ MOV AH,OPEN
+ INT 33 ;Make sure batch file still exists
+ OR AL,AL
+ JNZ PROMPTBAT ;If OPEN fails, prompt for disk
+ MOV WORD PTR [BATFCB+RECLEN],1
+ MOV DX,OFFSET RESGROUP:BATBYT
+ MOV AH,SETDMA
+ INT 33
+ MOV DI,OFFSET TRANGROUP:COMBUF+2
+RDBAT:
+ CALL GETBATBYT
+ CMP AL,"%" ;Check for parameter
+ JZ NEEDPARM
+SAVBATBYT:
+ STOSB
+ CALL OUT ;Display batched command line
+ CMP AL,0DH
+ JNZ RDBAT
+ SUB DI,OFFSET TRANGROUP:COMBUF+3
+ MOV AX,DI
+ MOV ES:[COMBUF+1],AL ;Set length of line
+ CALL GETBATBYT ;Eat linefeed
+ PUSH CS
+ POP DS ;Go back to local segment
+ASSUME DS:TRANGROUP
+DOCOM:
+;All segments are local for command line processing
+ MOV AL,10
+ CALL OUT
+ MOV SI,OFFSET TRANGROUP:COMBUF+2
+ MOV DI,OFFSET TRANGROUP:IDLEN
+ MOV AX,2901H ;Make FCB with blank scan-off
+ INT 21H
+ CMP AL,1 ;Check for ambiguous command name
+ JZ BADCOMJ1 ;Ambiguous commands not allowed
+ CMP AL,-1
+ JNZ DRVGD
+ JMP DRVBAD
+DRVGD:
+ MOV AL,[DI]
+ MOV [SPECDRV],AL
+ MOV AL," "
+ MOV CX,9
+ INC DI
+ REPNE SCASB ;Count no. of letters in command name
+ MOV AL,9
+ SUB AL,CL
+ MOV [IDLEN],AL
+ MOV DI,81H
+ MOV CX,0
+ PUSH SI
+COMTAIL:
+ LODSB
+ STOSB ;Move command tail to 80H
+ CMP AL,13
+ LOOPNZ COMTAIL
+ NOT CL
+ MOV BYTE PTR DS:[80H],CL
+ POP SI
+;If the command has 0 parameters must check here for
+;any switches that might be present.
+;SI -> first character after the command.
+ MOV [FLAGER],0 ;Set error flag before any calls to switch
+ CALL SWITCH ;Is the next character a "/"
+ MOV [COMSW],AX
+ MOV DI,FCB
+ MOV AX,2901H
+ INT 21H
+ MOV [PARM1],AL ;Save result of parse
+ CALL SWITCH
+ MOV [ARG1S],AX
+ MOV DI,FCB+10H
+ MOV AX,2901H
+ INT 21H ;Parse file name
+ MOV [PARM2],AL ;Save result
+ CALL SWITCH
+ MOV [ARG2S],AX
+ MOV AL,[IDLEN]
+ MOV DL,[SPECDRV]
+ OR DL,DL ;Check if drive was specified
+ JZ OK
+ JMP DRVCHK
+OK: DEC AL ;Check for null command
+ JNZ FNDCOM
+ JMP GETCOM
+
+RETSW:
+ XCHG AX,BX ;Put switches in AX
+ RET
+
+SWITCH:
+ XOR BX,BX ;Initialize - no switches set
+SWLOOP:
+ CALL SCANOFF ;Skip any delimiters
+ CMP AL,"/" ;Is it a switch specifier?
+ JNZ RETSW ;No -- we're finished
+ INC SI ;Skip over "/"
+ CALL SCANOFF
+ INC SI
+;Convert lower case input to upper case
+ CMP AL,"a"
+ JB SAVCHR
+ CMP AL,"z"
+ JA SAVCHR
+ SUB AL,20H ;Lower-case changed to upper-case
+SAVCHR:
+ MOV DI,OFFSET TRANGROUP:SWLIST
+ MOV CX,SWCOUNT
+ REPNE SCASB ;Look for matching switch
+ JNZ BADSW
+ MOV AX,1
+ SHL AX,CL ;Set a bit for the switch
+ OR BX,AX
+ JMP SHORT SWLOOP
+
+BADSW:
+ MOV [FLAGER],1 ;Record error in switch
+ JMP SHORT SWLOOP
+
+SWLIST DB "BAVPW"
+SWCOUNT EQU $-SWLIST
+
+DRVBAD:
+ MOV DX,OFFSET TRANGROUP:BADDRV
+ JMP ERROR
+
+FNDCOM:
+ MOV SI,OFFSET TRANGROUP:COMTAB ;Prepare to search command table
+ MOV CH,0
+FINDCOM:
+ MOV DI,OFFSET TRANGROUP:IDLEN
+ MOV CL,[SI]
+ JCXZ EXTERNAL
+ REPE CMPSB
+ LAHF
+ ADD SI,CX ;Bump to next position without affecting flags
+ SAHF
+ LODSB ;Get flag for drive check
+ MOV [CHKDRV],AL
+ LODSW ;Get address of command
+ JNZ FINDCOM
+ MOV DX,AX
+ CMP [CHKDRV],0
+ JZ NOCHECK
+ MOV AL,[PARM1]
+ OR AL,[PARM2] ;Check if either parm. had invalid drive
+ CMP AL,-1
+ JZ DRVBAD
+NOCHECK:CALL DX
+COMJMP: JMP COMMAND
+
+BADCOMJ:JMP BADCOM
+
+SETDRV1:
+ JMP SETDRV
+
+DRVCHK:
+ DEC DL ;Adjust for correct drive number
+ DEC AL ;Check if anything else is on line
+ JZ SETDRV1
+EXTERNAL:
+ MOV AL,[SPECDRV]
+ MOV [IDLEN],AL
+ MOV WORD PTR[COM],4F00H+"C" ;"CO"
+ MOV BYTE PTR[COM+2],"M"
+ MOV DX,OFFSET TRANGROUP:IDLEN
+ MOV AH,OPEN
+ INT 33 ;Check if command to be executed
+ MOV [FILTYP],AL ;0 for COM files, -1 for EXE files
+ OR AL,AL
+ JZ EXECUTE
+ MOV WORD PTR[COM],5800H+"E" ;"EX"
+ MOV BYTE PTR[COM+2],"E"
+ INT 33 ;Check for EXE file
+ OR AL,AL
+ JZ EXECUTE
+ MOV WORD PTR[COM],4100H+"B" ;"BA"
+ MOV BYTE PTR[COM+2],"T"
+ INT 33 ;Check if batch file to be executed
+ OR AL,AL
+ JNZ BADCOMJ
+BATCOM:
+;Batch parameters are read with ES set to segment of resident part
+ MOV ES,[RESSEG]
+ASSUME ES:RESGROUP
+ MOV DI,OFFSET RESGROUP:PARMTAB
+ MOV AX,-1
+ MOV CX,10
+ REP STOSW ;Zero parameter pointer table
+ MOV SI,OFFSET TRANGROUP:COMBUF+2
+ MOV DI,OFFSET RESGROUP:PARMBUF
+ MOV BX,OFFSET RESGROUP:PARMTAB
+EACHPARM:
+ CALL SCANOFF
+ CMP AL,0DH
+ JZ HAVPARM
+ MOV ES:[BX],DI ;Set pointer table to point to actual parameter
+ INC BX
+ INC BX
+MOVPARM:
+ LODSB
+ CALL DELIM
+ JZ ENDPARM ;Check for end of parameter
+ STOSB
+ CMP AL,0DH
+ JZ HAVPARM
+ JMP SHORT MOVPARM
+ENDPARM:
+ MOV AL,0DH
+ STOSB ;End-of-parameter marker
+ CMP BX,OFFSET RESGROUP:PARMTAB+20 ;Maximum number of parameters?
+ JB EACHPARM
+HAVPARM:
+ MOV SI,OFFSET TRANGROUP:IDLEN
+ MOV DI,OFFSET RESGROUP:BATFCB
+ MOV CX,16
+ REP MOVSW ;Move into private batch FCB
+ XOR AX,AX
+ PUSH ES
+ POP DS ;Simply batch FCB setup
+ASSUME DS:RESGROUP
+ MOV WORD PTR[BATFCB+RR],AX
+ MOV WORD PTR[BATFCB+RR+2],AX ;Zero RR field
+ INC AX
+ MOV WORD PTR[BATFCB+RECLEN],AX ;Set record length to 1 byte
+ MOV [BATCH],AL ;Flag batch job in progress
+ JMP COMMAND
+ASSUME DS:TRANGROUP,ES:TRANGROUP
+
+EXECUTE:
+ MOV AX,WORD PTR[IDLEN+16]
+ OR AX,WORD PTR[IDLEN+18] ;See if zero length
+ JZ BADCOM ;If so, error
+ XOR AX,AX
+ MOV WORD PTR[IDLEN+RR],AX
+ MOV WORD PTR[IDLEN+RR+2],AX ;Set RR field to zero
+ INC AX
+ MOV WORD PTR[IDLEN+RECLEN],AX ;Set record length field to 1
+ MOV DX,[TPA]
+ MOV BX,DX
+ MOV AH,SETBASE
+ INT 21H
+ TEST [FILTYP],-1 ;Check if file is COM or EXE
+ JZ COMLOAD
+ JMP EXELOAD
+COMLOAD:PUSH DS
+ MOV DS,DX
+ MOV DX,100H
+ MOV AH,SETDMA
+ INT 21H
+ POP DS
+ MOV CX,[BYTCNT]
+ SUB CX,100H
+ MOV DX,OFFSET TRANGROUP:IDLEN
+ MOV AH,RDBLK
+ INT 21H
+ DEC AL
+ MOV DX,OFFSET TRANGROUP:TOOBIG
+ JNZ ERROR
+;Set up exit conditions
+ MOV CX,[BYTCNT]
+ MOV DS,BX
+ MOV ES,BX
+ CLI
+ MOV SS,BX
+ MOV SP,CX
+ STI
+ SUB CX,100H ;Allow some stack space
+ XOR AX,AX
+ PUSH AX
+ MOV AX,100H
+ PUSH BX
+ PUSH AX
+ CALL SETUP
+XXX PROC FAR
+ RET
+XXX ENDP
+BADCOM:
+ MOV DX,OFFSET TRANGROUP:BADNAM
+ERROR:
+ MOV AH,PRINTBUF
+ INT 21H
+ JMP COMMAND
+
+CHKCNT:
+ TEST [FILECNT],-1
+ JNZ ENDDIR
+ MOV DX,OFFSET TRANGROUP:NOTFND
+ JMP ERROR
+
+ENDDIR:
+;Make sure last line ends with CR/LF
+ MOV AL,[LINLEN]
+ CMP AL,[LINCNT] ;Will be equal if just had CR/LF
+ JZ MESSAGE
+ CALL CRLF2
+MESSAGE:
+ MOV SI,[FILECNT]
+ XOR DI,DI
+ CALL DISP32BITS
+ MOV DX,OFFSET TRANGROUP:DIRMES
+ MOV AH,PRINTBUF
+ INT 21H
+ RET
+
+CATALOG:
+ MOV AL,"?" ;*.* is default file spec.
+ MOV DI,5DH
+ MOV CX,11
+ REP STOSB
+ MOV SI,81H
+ CALL SWITCH
+ MOV DI,5CH
+ MOV AX,41*100H+0DH ;Parse with default name and extension
+ INT 33
+
+;Begin by processing any switches that may have been specified.
+;BITS will contain any information about switches that was
+;found when the command line was parsed.
+
+SETSWT:
+ MOV AX,[COMSW] ;Get switches from command
+ OR AX,[ARG1S] ;OR in switches from first parameter
+ MOV [BITS],AX
+ MOV BYTE PTR[FULLSCR],LINPERPAG
+ TEST AL,1 ;Look for /W
+ MOV AL,NORMPERLIN
+ JZ DIR
+ MOV AL,WIDEPERLIN
+DIR:
+ MOV [LINLEN],AL ;Set number of entries per line
+ MOV [LINCNT],AL
+ MOV [FILECNT],0 ;Keep track of how many files found
+ MOV DX,OFFSET TRANGROUP:DIRBUF ;Set Disk transfer address
+ MOV AH,SETDMA
+ INT 21H
+ MOV AH,SRCHFRST
+SHOWDIR:
+ MOV DX,5CH ;DX -> Unopened FCB
+ INT 21H ;Search for a file to match FCB
+ INC AL ;FF = file not found
+ JNZ AGAIN ;Either an error or we are finished
+ JMP CHKCNT
+AGAIN:
+ INC [FILECNT] ;Keep track of how many we find
+ MOV SI,OFFSET TRANGROUP:DIRBUF+1 ;SI -> information returned by sys call
+ CALL SHONAME
+ TEST BYTE PTR[BITS],1 ;/W set?
+ JNZ NEXENT ;If so, no size, date, or time
+ CALL DISPSIZE ;Print size of file
+ CALL TWOSPC
+ MOV AX,WORD PTR[DIRBUF+25] ;Get date
+ OR AX,AX
+ JZ NEXENT ;Skip if no date
+ MOV DX,AX
+ MOV CL,5
+ SHR AX,CL ;Align month
+ AND AL,0FH
+ MOV BH,"0"-" " ;Enable zero suppression
+ CALL OUT2
+ MOV AL,"-"
+ CALL OUT
+ MOV AL,DL
+ AND AL,1FH ;Mask to day
+ CALL OUT2
+ MOV AL,"-"
+ CALL OUT
+ MOV AL,DH
+ SHR AL,1 ;Align year
+ ADD AX,80 ;Relative 1980
+ CMP AL,100
+ JB MILLENIUM
+ SUB AL,100
+MILLENIUM:
+ CALL OUT2
+ MOV BX,WORD PTR[DIRBUF+23] ;Get time
+ OR BX,BX ;Time field present?
+ JZ NEXENT
+ CALL TWOSPC
+ SHR BX,1
+ SHR BX,1
+ SHR BX,1
+ SHR BL,1
+ SHR BL,1 ;Hours in BH, minutes in BL
+ MOV AL,BH
+ MOV DH,"a" ;Assume A.M.
+ CMP AL,12 ;In the afternoon?
+ JB MORN
+ MOV DH,"p"
+ JE MORN
+ SUB AL,12 ;Keep it to 12 hours or less
+MORN:
+ OR AL,AL ;Before 1 am?
+ JNZ SHOHOURS
+ MOV AL,12
+SHOHOURS:
+ MOV BH,"0"-" " ;Enable zero suppression
+ CALL OUT2
+ MOV AL,":"
+ CALL OUT
+ MOV AL,BL ;Output minutes
+ CALL OUT2
+ MOV AL,DH ;Get "a" or "p"
+ CALL OUT
+NEXENT:
+ DEC [LINCNT]
+ JNZ SAMLIN
+NEXLIN:
+ MOV AL,[LINLEN]
+ MOV [LINCNT],AL
+ CALL CRLF2
+ TEST BYTE PTR[BITS],2 ;/P switch present?
+ JZ SCROLL ;If not, just continue
+ DEC BYTE PTR[FULLSCR]
+ JNZ SCROLL
+ MOV BYTE PTR[FULLSCR],LINPERPAG
+ MOV AH,PRINTBUF
+ MOV DX,OFFSET TRANGROUP:PAUSMES
+ INT 33
+ MOV AX,0C08H ;Wait for any character to be typed
+ INT 21H
+ CALL CRLF2
+SCROLL:
+ MOV AH,SRCHNXT
+ JMP SHOWDIR
+
+SAMLIN:
+ MOV AL,9 ;Output a tab
+ CALL OUT
+ JMP SHORT SCROLL
+
+SHONAME:
+ MOV CX,8
+ CALL OUTCNT
+ CALL ONESPC
+ MOV CX,3
+OUTCNT:
+ LODSB
+ CALL OUT
+ LOOP OUTCNT
+ RET
+
+TWOSPC:
+ CALL ONESPC
+ONESPC:
+ MOV AL," "
+ JMP OUT
+
+CRLF2:
+ MOV AL,13
+ CALL OUT
+ MOV AL,10
+ JMP OUT
+
+DISPSIZE:
+ MOV SI,WORD PTR[DIRBUF+29]
+ MOV DI,WORD PTR[DIRBUF+31]
+DISP32BITS:
+;Prints the 32-bit number DI:SI on the console in decimal. Uses a total
+;of 9 digit positions with leading blanks.
+ XOR AX,AX
+ MOV BX,AX
+ MOV BP,AX
+ MOV CX,32
+CONVLP:
+ SHL SI,1
+ RCL DI,1
+ XCHG AX,BP
+ CALL CONVWRD
+ XCHG AX,BP
+ XCHG AX,BX
+ CALL CONVWRD
+ XCHG AX,BX
+ ADC AL,0
+ LOOP CONVLP
+; Conversion complete. Print 9-digit number.
+ MOV CX,1810H ;Allow leading zero blanking for 8 digits
+ XCHG DX,AX
+ CALL DIGIT
+ XCHG AX,BX
+ CALL OUTWORD
+ XCHG AX,BP
+OUTWORD:
+ PUSH AX
+ MOV DL,AH
+ CALL OUTBYTE
+ POP DX
+OUTBYTE:
+ MOV DH,DL
+ SHR DL,1
+ SHR DL,1
+ SHR DL,1
+ SHR DL,1
+ CALL DIGIT
+ MOV DL,DH
+DIGIT:
+ AND DL,0FH
+ JZ BLANKZER
+ MOV CL,0
+BLANKZER:
+ DEC CH
+ AND CL,CH
+ OR DL,30H
+ SUB DL,CL
+ MOV AH,OUTCH
+ INT 21H
+ RET
+
+CONVWRD:
+ ADC AL,AL
+ DAA
+ XCHG AL,AH
+ ADC AL,AL
+ DAA
+ XCHG AL,AH
+RET20: RET
+
+ERASE:
+ MOV CX,11
+ MOV SI,FCB+1
+AMBSPEC:
+ LODSB
+ CMP AL,"?"
+ JNZ ALLFIL
+ LOOP AMBSPEC
+ALLFIL:
+ CMP CX,0
+ JNZ NOPRMPT
+ASKAGN:
+ MOV DX,OFFSET TRANGROUP:SUREMES ;"Are you sure (Y/N)?"
+ MOV AH,PRINTBUF
+ INT 21H
+ MOV AX,0C00H+INCHAR
+ INT 21H
+ AND AL,5FH
+ CMP AL,"N"
+ JZ RET20
+ CMP AL,"Y"
+ CALL CRLF2
+ JZ NOPRMPT
+ JMP SHORT ASKAGN
+NOPRMPT:
+ MOV AH,DELETE
+ MOV BX,OFFSET TRANGROUP:NOTFND
+ CMP BYTE PTR DS:[FCB+1]," " ;Check if parameter exists
+ JMP SHORT OPFILE
+RENAME:
+ MOV AH,RENAM
+ MOV BX,OFFSET TRANGROUP:RENERR
+ CMP BYTE PTR DS:[FCB+16+1]," " ;Check if parameter exists
+OPFILE:
+ MOV DX,OFFSET TRANGROUP:MISNAM
+ JZ ERRJ ;Error if missing parameter
+ MOV DX,FCB
+ INT 21H
+ INC AL
+ JNZ RET20
+ MOV DX,BX
+ERRJ: JMP ERROR
+
+TYPEFIL:
+ MOV DS,[TPA]
+ XOR DX,DX
+ MOV AH,SETDMA
+ INT 21H
+ PUSH CS
+ POP DS
+ MOV DX,FCB
+ MOV AH,OPEN
+ INT 21H
+ OR AL,AL
+ MOV DX,OFFSET TRANGROUP:NOTFND
+ JNZ ERRJ
+ XOR AX,AX
+ MOV WORD PTR DS:[FCB+RR],AX ;Set RR field
+ MOV WORD PTR DS:[FCB+RR+2],AX
+ INC AX
+ MOV WORD PTR DS:[FCB+RECLEN],AX ;Set record length
+ MOV ES,[TPA]
+TYPELP:
+ MOV DX,FCB
+ MOV CX,[BYTCNT]
+ MOV AH,RDBLK
+ INT 21H
+ JCXZ RET30
+ XOR SI,SI ;Start at 0 in TPA
+OUTLP:
+ LODS BYTE PTR ES:[SI] ;In TPA segment
+ CMP AL,1AH
+ JZ RET30
+ MOV AH,OUTCH
+ MOV DL,AL
+ INT 21H
+ LOOP OUTLP
+ JMP SHORT TYPELP
+
+RET30: RET ;Need a nearby RET
+
+COPY:
+ XOR AX,AX
+ MOV [PLUS],AL ;Will keep track of "+"s
+ MOV [FILECNT],AX
+ MOV SI,81H ;Point to input line
+ CALL SWITCH ;Skip over switches on command
+ MOV BP,AX
+ MOV DI,FCB
+ CALL PARSNAM ;Scan first source
+ MOV [PARM1],DL ;Save ambiguous flag
+ MOV [SRCPT],SI ;Save pointer to command line
+;Parse each name to find destination and check for /V switch
+SCANNAM:
+ CALL PARSE
+ JNZ SCANNAM
+GETDEST:
+ MOV DI,OFFSET TRANGROUP:DEST
+ MOV BX,BP ;Remeber switches so far
+ XOR BP,BP ;Must have dest. swtiches alone
+ CALL PARSNAM
+ MOV [ARG2S],BP ;Remember switches on destination
+ JNZ HAVDESTNAM ;File name present?
+ INC DI ;Point to file name spot
+ MOV AL,"?" ;Substitute *.*
+ MOV CX,11
+ REP STOSB
+HAVDESTNAM:
+ OR BX,BP ;BX = all switches combined
+ AND BL,VSWITCH ;Verify requested?
+ JZ NOVER
+ MOV AX,46*100H+1 ;Set verify
+ MOV DL,0
+ INT 33
+NOVER:
+ MOV DI,OFFSET TRANGROUP:DESTNAME
+ MOV SI,OFFSET TRANGROUP:DEST+1
+ MOV BX,FCB+1
+ CALL BUILDNAME ;See if we can make it unambiguous
+ MOV DI,OFFSET TRANGROUP:DESTNAME
+ MOV AL,"?"
+ MOV CX,11
+ REPNE SCASB ;Scan for "?" to see if ambiguous
+ MOV AL,1 ;Flag if ambig.
+ JZ AMBIG
+ DEC AX ;AL=0 if unambig.
+AMBIG:
+ MOV DL,AL
+ MOV AH,[PLUS] ;1=found "+"
+ XOR AL,1 ;0=ambig, 1=unambig destination
+ AND AL,[PARM1] ;Source ambig. AND dest unambig.
+ OR AL,AH ;OR found "+" means concatenation
+ MOV [ASCII],AL ;Concatenation implies ASCII mode
+ MOV [INEXACT],AL ;ASCII implies inexact copy
+ SHL AL,1
+ OR AL,DL ;Combine multiple and concat flags
+ MOV [PARM2],AL
+ MOV AL,BYTE PTR[COMSW]
+ CALL SETASC ;Check /A,/B on command
+ MOV AL,BYTE PTR[ARG1S]
+ CALL SETASC ;Check for ASCII on first filename
+ MOV BYTE PTR[COMSW],AL ;Save starting switch values
+ MOV AH,SRCHFRST
+ CALL SEARCH ;Search for first source name
+MULTDEST:
+ JZ FIRSTSRC ;Find a first source name?
+ TEST [PARM2],1 ;If multiple, we're done
+ JNZ ENDCOPY
+ XOR AX,AX
+ MOV [NXTADD],AX
+ MOV [CFLAG],AL ;Flag nothing read yet
+NEXTSNG:
+ MOV DI,FCB
+ MOV SI,[SRCPT]
+ CALL PARSESRC ;Parse next file name into FCB
+ MOV [PARM1],DL ;Remember if it's ambiguous
+ MOV [SRCPT],SI
+ JZ SNGCLOS
+ MOV AH,SRCHFRST
+ CALL SEARCH ;Search for new file name
+ JNZ NEXTSNG ;If none, skip it and move to next name
+READSNG:
+ CALL CHECKREAD
+SNGLOOP:
+ CALL SEARCHNEXT ;See if any more of this name
+ JZ READSNG
+ JMP SHORT NEXTSNG
+
+SNGCLOS:
+ CALL CLOSEFIL
+ENDCOPY:
+ MOV SI,[FILECNT]
+ XOR DI,DI
+ CALL DISP32BITS
+ MOV DX,OFFSET TRANGROUP:COPIED
+ MOV AH,PRINTBUF
+ INT 21H
+ JMP COMMAND ;Stack could be messed up
+
+FIRSTSRC:
+ MOV SI,OFFSET TRANGROUP:DIRBUF+1
+ MOV DI,OFFSET TRANGROUP:SOURCE
+ MOV CX,11
+ REP MOVSB ;Copy first source name to SOURCE
+ MOV SI,OFFSET TRANGROUP:DESTNAME
+ MOV DI,OFFSET TRANGROUP:DEST+1
+ MOV BX,OFFSET TRANGROUP:SOURCE
+ CALL BUILDNAME ;Build destination name
+ XOR AX,AX
+ MOV [NXTADD],AX
+ MOV [CFLAG],AL
+ MOV [APPEND],AL
+ MOV [NOWRITE],AL
+ TEST [PARM2],1 ;Multiple destinations?
+ JZ NOPRT
+ MOV SI,OFFSET TRANGROUP:DIRBUF+1
+ CALL SHONAME ;If so, show first source
+ CALL CRLF2
+NOPRT:
+ CALL COMPNAME ;Source and dest. the same?
+ JNZ DOREAD ;If not, read source in
+ TEST [PARM2],2 ;Concatenation?
+ MOV DX,OFFSET TRANGROUP:OVERWR
+ JZ COPERRJ ;If not, overwrite error
+ MOV [APPEND],1 ;Set physical append
+ MOV AH,OPEN
+ MOV DX,OFFSET TRANGROUP:DEST
+ INT 33 ;Open (existing) destination
+ CMP [ASCII],0 ;ASCII flag set?
+ JZ BINARYAPP
+;ASCII append. Must find logical EOF, then seek there with dest. FCB
+ MOV [NOWRITE],1
+ CALL READIN ;Find EOF
+ CALL FLSHFIL ;Seek there
+ MOV [NOWRITE],0
+ CALL FLSHFIL ;Truncate file
+ JMP SHORT SNGLCHK
+
+SNGLOOPJ:JMP SNGLOOP
+
+COPERRJ:JMP COPERR
+
+BINARYAPP:
+ MOV WORD PTR[DEST+RECLEN],1 ;Set record length to 1
+ MOV SI,OFFSET TRANGROUP:DEST+16 ;Point to file size
+ MOV DI,OFFSET TRANGROUP:DEST+RR
+ MOVSW
+ MOVSW ;Seek to end of file
+ MOV [CFLAG],1
+ JMP SHORT SNGLCHK
+DOREAD:
+ CALL READIN
+SNGLCHK:
+ TEST [PARM2],1 ;Single or multiple destinations?
+ JZ SNGLOOPJ
+ MOV SI,[SRCPT]
+MULTAPP:
+ CALL PARSE
+ JZ MULTCLOS
+ PUSH SI
+ MOV SI,OFFSET TRANGROUP:DIRBUF+1
+ MOV DI,SI
+ MOV BX,OFFSET TRANGROUP:SOURCE
+ CALL BUILDNAME
+ CALL CHECKREAD
+ POP SI
+ JMP SHORT MULTAPP
+MULTCLOS:
+ CALL CLOSEFIL
+ MOV AL,BYTE PTR[COMSW]
+ MOV [ASCII],AL ;Restore ASCII flag
+ CALL SEARCHNEXT
+ JMP MULTDEST
+
+PARSE:
+ MOV DI,OFFSET TRANGROUP:DIRBUF
+PARSESRC:
+ CALL SCANOFF
+ CMP AL,"+"
+ JNZ RETZF
+ MOV [PLUS],1 ;Keep track of "+" signs
+ INC SI ;Skip over it
+PARSNAM:
+ MOV AX,2901H
+ INT 33 ;Parse file name
+ CMP AL,-1 ;Illegal?
+ MOV DX,OFFSET TRANGROUP:BADDRV
+ JZ COPERRJ
+ XCHG AX,DX ;Save parse flag in DL
+ MOV AL,BYTE PTR[DI] ;Get drive number
+ OR AL,AL ;Is it default?
+ JNZ PARSW
+ MOV AL,[CURDRV] ;Substitute actual drive
+ INC AX
+ MOV BYTE PTR[DI],AL
+PARSW:
+ PUSH BX
+ PUSH DI
+ CALL SWITCH ;Process switches
+ OR BP,AX ;Combine all switches
+ CALL SETASC ;Check for /A or /B
+ POP DI
+ POP BX
+ CMP BYTE PTR[DI+1]," " ;Did we even get a file name?
+ RET
+
+RETZF:
+ XOR AX,AX
+RET35: RET
+
+SEARCHNEXT:
+ MOV AL,[PARM1] ;Is name ambiguous?
+ DEC AL
+ JNZ RET35 ;Don't perform search if not
+ MOV AH,SRCHNXT
+SEARCH:
+ PUSH AX
+ MOV AH,SETDMA
+ MOV DX,OFFSET TRANGROUP:DIRBUF
+ INT 33 ;Put result of search in DIRBUF
+ POP AX ;Restore search first/next command
+ MOV DX,FCB
+ INT 33 ;Do the search
+ OR AL,AL
+ RET
+
+SETASC:
+;Given switch vector in AX,
+; Set ASCII switch if /A is set
+; Clear ASCII switch if /B is set
+; Leave ASCII unchanged if neither or both are set
+; Also sets INEXACT if ASCII is ever set. AL = ASCII on exit, flags set
+ AND AL,ASWITCH+BSWITCH
+ JPE LOADSW ;PE means both or neither are set
+ AND AL,ASWITCH
+ MOV [ASCII],AL
+ OR [INEXACT],AL
+LOADSW:
+ MOV AL,[ASCII]
+ OR AL,AL
+ RET
+
+BUILDNAME:
+; [SI] = Ambiguous input file name
+; [BX] = Source of replacement characters
+; [DI] = Destination
+; File name is copied from [SI] to [DI]. If "?"s are encountered,
+; they are replaced with the character in the same position at [BX].
+ MOV CX,11
+BUILDNAM:
+ LODSB
+ CMP AL,"?"
+ JNZ NOTAMBIG
+ MOV AL,BYTE PTR[BX]
+NOTAMBIG:
+ STOSB
+ INC BX
+ LOOP BUILDNAM
+ RET
+
+COMPNAME:
+ MOV SI,OFFSET TRANGROUP:DEST
+ MOV DI,OFFSET TRANGROUP:DIRBUF
+ MOV CX,6
+ REPE CMPSW
+ RET
+
+CHECKREAD:
+;Read file in (with READIN) if not identical to destination
+ CALL COMPNAME ;See if source and destination the same
+ JNZ READIN
+ CMP [APPEND],0 ;If physical append, it's OK
+ JNZ RET40
+ MOV DX,OFFSET TRANGROUP:LOSTERR ;Tell him he's not going to get it
+ MOV AH,PRINTBUF
+ INT 33
+RET40: RET
+
+READIN:
+;Open source file and read it in. If memory fills up, flush it out to
+;destination and keep reading. If /A switch set, chop file at first ^Z.
+; Inputs/Outputs:
+; [NXTADD] has current pointer in buffer
+; [CFLAG] <>0 if destination has been created
+
+ MOV DX,OFFSET TRANGROUP:DIRBUF
+ MOV AH,OPEN
+ INT 21H
+ OR AL,AL ;Successful open?
+ JNZ RET40 ;If not, just ignore it
+ XOR AX,AX
+ MOV WORD PTR[DIRBUF+RR],AX
+ MOV WORD PTR[DIRBUF+RR+2],AX
+ INC AX
+ MOV WORD PTR[DIRBUF+RECLEN],AX
+COPYLP:
+ MOV DX,[NXTADD]
+ MOV AH,SETDMA
+ PUSH DS
+ MOV DS,[TPA]
+ INT 33
+ POP DS
+ MOV CX,[BYTCNT]
+ SUB CX,DX ;Compute available space
+ MOV DX,OFFSET TRANGROUP:DIRBUF
+ MOV AH,RDBLK ;Read in source file
+ INT 21H
+ JCXZ RET40
+ CMP [ASCII],0
+ JZ BINREAD
+ MOV DX,CX
+ MOV DI,[NXTADD]
+ MOV AL,1AH
+ PUSH ES
+ MOV ES,[TPA]
+ REPNE SCASB ;Scan for EOF
+ POP ES
+ JNZ USEALL
+ INC CX
+USEALL:
+ SUB DX,CX
+ MOV CX,DX
+BINREAD:
+ ADD CX,[NXTADD]
+ MOV [NXTADD],CX
+ CMP CX,[BYTCNT] ;Is buffer full?
+ JB RET40 ;If not, we must have found EOF
+ CALL FLSHFIL
+ JMP SHORT COPYLP
+
+CLOSEFIL:
+ MOV AX,[NXTADD]
+ MOV BX,AX
+ OR AL,AH ;See if any data is loaded
+ OR AL,[CFLAG] ; or file was created
+ JZ RET50 ;Don't close or count if not created
+ MOV AL,BYTE PTR[ARG2S]
+ CALL SETASC ;Check for /B or /A on destination
+ JZ BINCLOS
+ CMP BX,[BYTCNT] ;Is memory full?
+ JNZ PUTZ
+ CALL FLSHFIL ;Empty it to make room for 1 lousy byte
+ XOR BX,BX
+PUTZ:
+ PUSH DS
+ MOV DS,[TPA]
+ MOV WORD PTR[BX],1AH ;Add End-of-file mark (Ctrl-Z)
+ POP DS
+ INC [NXTADD]
+BINCLOS:
+ CALL FLSHFIL
+ CMP [INEXACT],0 ;Copy not exact?
+ JNZ NODATE ;If so, don't copy date & time
+ MOV SI,OFFSET TRANGROUP:DIRBUF+OFFDATE
+ MOV DI,OFFSET TRANGROUP:DEST+OFFDATE ;Make date & time same as original
+ MOVSW ;Copy date
+ MOVSW ;Copy time
+NODATE:
+ MOV DX,OFFSET TRANGROUP:DEST
+ MOV AH,CLOSE
+ INT 21H
+ INC [FILECNT]
+RET50: RET
+
+FLSHFIL:
+;Write out any data remaining in memory.
+; Inputs:
+; [NXTADD] = No. of bytes to write
+; [CFLAG] <>0 if file has been created
+; Outputs:
+; [NXTADD] = 0
+
+ MOV AL,1
+ XCHG [CFLAG],AL
+ OR AL,AL
+ JNZ EXISTS
+ CMP [NOWRITE],0
+ JNZ SKPMAK ;Don't actually create if NOWRITE set
+ MOV DX,OFFSET TRANGROUP:DEST
+ MOV AH,MAKE
+ INT 21H
+ MOV DX,OFFSET TRANGROUP:FULDIR
+ OR AL,AL
+ JNZ COPERR
+SKPMAK:
+ XOR AX,AX
+ MOV WORD PTR[DEST+RR],AX
+ MOV WORD PTR[DEST+RR+2],AX
+ INC AX
+ MOV WORD PTR[DEST+RECLEN],AX
+EXISTS:
+ XOR CX,CX
+ XCHG CX,[NXTADD]
+ CMP [NOWRITE],0 ;If NOWRITE set, just seek CX bytes
+ JNZ SEEKEND
+ XOR DX,DX
+ PUSH DS
+ MOV DS,[TPA]
+ MOV AH,SETDMA
+ INT 33
+ POP DS
+ MOV DX,OFFSET TRANGROUP:DEST
+ MOV AH,WRBLK
+ INT 21H
+ OR AL,AL
+ JZ RET60
+ MOV DX,OFFSET TRANGROUP:DEST
+ MOV AH,CLOSE
+ INT 21H
+ MOV AH,DELETE
+ INT 33
+ MOV DX,OFFSET TRANGROUP:NOSPACE
+COPERR:
+ MOV AH,9
+ INT 21H
+ JMP ENDCOPY
+
+SEEKEND:
+ ADD WORD PTR[DEST+RR],CX
+ ADC WORD PTR[DEST+RR+2],0 ;Propagate carry
+RET60: RET
+
+GETBATBYT:
+;Get one byte from the batch file and return it in AL. End-of-file
+;returns <CR> and ends batch mode. DS must be set to resident segment.
+;AH, CX, DX destroyed.
+ASSUME DS:RESGROUP
+ MOV DX,OFFSET RESGROUP:BATFCB
+ MOV AH,RDBLK
+ MOV CX,1
+ INT 33 ;Get one more byte from batch file
+ JCXZ BATEOF
+ MOV AL,[BATBYT]
+ CMP AL,1AH
+ JNZ RET70
+BATEOF:
+ MOV AL,0DH ;If end-of-file, then end of line
+ MOV [BATCH],0 ;And turn off batch mode
+RET70: RET
+ASSUME DS:TRANGROUP
+
+SCANOFF:
+ LODSB
+ CALL DELIM
+ JZ SCANOFF
+ DEC SI ;Point to first non-delimiter
+ RET
+
+DELIM:
+ CMP AL," "
+ JZ RET80
+ CMP AL,"="
+ JZ RET80
+ CMP AL,","
+ JZ RET80
+ CMP AL,9 ;Check for TAB character
+RET80: RET
+
+PAUSE:
+ MOV DX,OFFSET TRANGROUP:PAUSMES
+ MOV AH,PRINTBUF
+ INT 33
+ MOV AX,0C00H+INCHAR ;Get character with KB buffer flush
+ INT 33
+RET90: RET
+
+;Date and time are set during initialization and use
+;this routines since they need to do a long return
+
+DATINIT:
+ PUSH ES
+ PUSH DS ;Going to use the previous stack
+ MOV AX,CS ;Set up the appropriate segment registers
+ MOV ES,AX
+ MOV DS,AX
+ MOV WORD PTR DS:[81H],13 ;Want to prompt for date during initialization
+ CALL DATE
+ CALL TIME
+ POP DS
+ POP ES
+YYY PROC FAR
+ RET
+YYY ENDP
+
+; DATE - Gets and sets the time
+
+DATE:
+ MOV SI,81H ;Accepting argument for date inline
+ CALL SCANOFF
+ CMP AL,13
+ JZ PRMTDAT
+ MOV BX,2F00H+"-" ;"/-"
+ CALL INLINE
+ JMP COMDAT
+
+PRMTDAT:
+ MOV DX,OFFSET TRANGROUP:CURDAT
+ MOV AH,PRINTBUF
+ INT 33 ;Print "Current date is "
+ MOV AH,GETDATE
+ INT 33 ;Get date in CX:DX
+ CBW
+ MOV SI,AX
+ SHL SI,1
+ ADD SI,AX ;SI=AX*3
+ ADD SI,OFFSET TRANGROUP:WEEKTAB
+ MOV BX,CX
+ MOV CX,3
+ CALL OUTCNT
+ MOV AL," "
+ CALL OUT
+ MOV AX,BX
+ MOV CX,DX
+ MOV DL,100
+ DIV DL
+ XCHG AL,AH
+ XCHG AX,DX
+ MOV BL,"-"
+ CALL SHOW
+GETDAT:
+ MOV DX,OFFSET TRANGROUP:NEWDAT
+ MOV BX,2F00H+"-" ;"/-" in BX
+ CALL GETBUF
+COMDAT: JZ RET90
+ JC DATERR
+ LODSB
+ CMP AL,BL
+ JZ SEPGD
+ CMP AL,BH
+ JNZ DATERR
+SEPGD: CALL GETNUM
+ JC DATERR
+ MOV CX,1900
+ CMP BYTE PTR[SI],13
+ JZ BIAS
+ MOV AL,100
+ MUL AH
+ MOV CX,AX
+ CALL GETNUM
+ JC DATERR
+BIAS:
+ MOV AL,AH
+ MOV AH,0
+ ADD CX,AX
+ LODSB
+ CMP AL,13
+ JNZ DATERR
+ MOV AH,SETDATE
+ INT 33
+ OR AL,AL
+ JNZ DATERR
+ JMP RET90
+DATERR:
+ MOV DX,OFFSET TRANGROUP:BADDAT
+ MOV AH,PRINTBUF
+ INT 33
+ JMP GETDAT
+
+; TIME gets and sets the time
+
+TIME:
+ MOV SI,81H ;Accepting argument for time inline
+ CALL SCANOFF
+ CMP AL,13
+ JZ PRMTTIM
+ MOV BX,3A00H+":"
+ CALL INLINE
+ JMP COMTIM
+
+PRMTTIM:
+ MOV DX,OFFSET TRANGROUP:CURTIM
+ MOV AH,PRINTBUF
+ INT 33 ;Print "Current time is "
+ MOV AH,GETTIME
+ INT 33 ;Get time in CX:DX
+ MOV BL,":"
+ CALL SHOW
+GETTIM:
+ XOR CX,CX ;Initialize hours and minutes to zero
+ MOV DX,OFFSET TRANGROUP:NEWTIM
+ MOV BX,3A00H+":"
+ CALL GETBUF
+COMTIM: JZ RET100 ;If no time present, don't change it
+ JC TIMERR
+ MOV CX,DX
+ XOR DX,DX
+ LODSB
+ CMP AL,13
+ JZ SAVTIM
+ CMP AL,BL
+ JNZ TIMERR
+ MOV BL,"."
+ CALL GETNUM
+ JC TIMERR
+ MOV DH,AH ;Position seconds
+ LODSB
+ CMP AL,13
+ JZ SAVTIM
+ CMP AL,BL
+ JNZ TIMERR
+ CALL GETNUM
+ JC TIMERR
+ MOV DL,AH
+ LODSB
+ CMP AL,13
+ JNZ TIMERR
+SAVTIM:
+ MOV AH,SETTIME
+ INT 33
+ OR AL,AL
+ JZ RET100 ;Error in time?
+TIMERR:
+ MOV DX,OFFSET TRANGROUP:BADTIM
+ MOV AH,PRINTBUF
+ INT 33 ;Print error message
+ JMP GETTIM ;Try again
+
+GETBUF:
+ MOV AH,PRINTBUF
+ INT 33 ;Print "Enter new date: "
+ MOV AH,INBUF
+ MOV DX,OFFSET TRANGROUP:COMBUF
+ INT 33 ;Get input line
+ CALL CRLF2
+ MOV SI,OFFSET TRANGROUP:COMBUF+2
+ CMP BYTE PTR[SI],13 ;Check if new date entered
+ JZ RET100
+INLINE:
+ CALL GETNUM ;Get one or two digit number
+ JC RET100
+ MOV DH,AH ;Put in position
+ LODSB
+ CMP AL,BL
+ JZ NEXT
+ CMP BL,":" ;Is it a date seperator?
+ JNZ DATESEP
+ DEC SI
+ MOV DL,0
+RET100: RET ;Time may have only an hour specified
+DATESEP:
+ CMP AL,BH
+ STC
+ JNZ RET100
+NEXT: CALL GETNUM
+ MOV DL,AH ;Put in position
+ RET
+
+GETNUM:
+ CALL INDIG
+ JC RET100
+ MOV AH,AL ;Save first digit
+ CALL INDIG ;Another digit?
+ JC OKRET
+ AAD ;Convert unpacked BCD to decimal
+ MOV AH,AL
+OKRET:
+ OR AL,1
+RET110: RET
+
+INDIG:
+ MOV AL,BYTE PTR[SI]
+ SUB AL,"0"
+ JC RET110
+ CMP AL,10
+ CMC
+ JC RET110
+ INC SI
+ RET
+
+SHOW:
+ MOV AL,CH
+ MOV BH,"0"-" " ;Enable leading zero suppression
+ CALL OUT2
+ MOV AL,BL
+ CALL OUT
+ MOV AL,CL
+ CALL OUT2
+ MOV AL,BL
+ CALL OUT
+ MOV AL,DH
+ CALL OUT2
+ CMP BL,":" ;Are we outputting time?
+ JNZ SKIPIT
+ MOV AL,"."
+ CALL OUT
+SKIPIT: MOV AL,DL
+OUT2: ;Output binary number as two ASCII digits
+ AAM ;Convert binary to unpacked BCD
+ XCHG AL,AH
+ OR AX,3030H ;Add "0" bias to both digits
+ CMP AL,"0" ;Is MSD zero?
+ JNZ NOSUP
+ SUB AL,BH ;Suppress leading zero if enabled
+NOSUP:
+ MOV BH,0 ;Disable zero suppression
+ CALL OUT
+ MOV AL,AH
+OUT:
+;Print char in AL without affecting registers
+ XCHG AX,DX
+ PUSH AX
+ MOV AH,OUTCH
+ INT 33
+ POP AX
+ XCHG AX,DX
+ RET
+
+EXELOAD:
+ MOV AX,CS
+ ADD AX,LOADSEG
+ MOV [EXEEND],AX ;Store in EXEEND
+ MOV DX,OFFSET TRANGROUP:RUNVAR ;Read header in here
+ MOV AH,SETDMA
+ INT 33
+ MOV CX,RUNVARSIZ ;Amount of header info we need
+ MOV DX,OFFSET TRANGROUP:EXEFCB
+ MOV AH,RDBLK
+ INT 33 ;Read in header
+ OR AL,AL
+ JNZ BADEXE ;Must not reach EOF
+ MOV AX,[HEADSIZ] ;Size of header in paragraphs
+;Convert header size to 512-byte pages by multiplying by 32 & rounding up
+ ADD AX,31 ;Round up first
+ MOV CL,5
+ SHR AX,CL ;Multiply by 32
+ MOV [EXEFCB+RR],AX ;Position in file of program
+ MOV WORD PTR[EXEFCB+RECLEN],512 ;Set record size
+ ADD BX,10H ;First paragraph above parameter area
+ MOV DX,[PAGES] ;Total size of file in 512-byte pages
+ SUB DX,AX ;Size of program in pages
+ MOV [PSIZE],DX
+ SHL DX,CL ;Convert pages back to paragraphs
+ MOV AX,DX
+ ADD DX,BX ;Size + start = minimum memory (paragr.)
+ MOV CX,[EXEEND] ;Get memory size in paragraphs
+ CMP DX,CX ;Enough memory?
+ JA SHRTERR
+ MOV DX,[INITSP]
+ ADD DX,15
+ SHR DX,1
+ SHR DX,1
+ SHR DX,1
+ SHR DX,1
+ ADD DX,[INITSS]
+ ADD DX,BX ;Adjusted value of SP
+ CMP DX,CX ;Is it valid?
+ JA SHRTERR
+ CMP [LOADLOW],-1 ;Load low or high?
+ JZ LOAD ;If low, load at segment BX
+ SUB CX,AX ;Memory size - program size = load addr.
+ MOV BX,CX
+LOAD:
+ MOV BP,BX ;Save load segment
+LOAD1:
+LOADSEG EQU (LOAD1-ZERO)/16
+ PUSH DS
+ MOV DS,BX
+ XOR DX,DX ;Address 0 in segment
+ MOV AH,SETDMA
+ INT 33 ;Set load address
+ POP DS
+ MOV CX,[PSIZE] ;Number of records to read
+ MOV DX,OFFSET TRANGROUP:EXEFCB
+ MOV AH,RDBLK
+ INT 33 ;Read in up to 64K
+ SUB [PSIZE],CX ;Decrement count by amount read
+ JZ HAVEXE ;Did we get it all?
+ TEST AL,1 ;Check return code if not
+ JNZ BADEXE ;Must be zero if more to come
+ ADD BX,1000H-20H ;Bump data segment 64K minus one record
+ JMP SHORT LOAD1 ;Get next 64K block
+
+BADEXE:
+ MOV DX,OFFSET TRANGROUP:EXEBAD
+ JMP ERROR
+
+SHRTERR:
+ MOV DX,OFFSET TRANGROUP:TOOBIG
+ JMP ERROR
+
+HAVEXE:
+ MOV AX,[RELTAB] ;Get position of table
+ MOV [EXEFCB+RR],AX ;Set in random record field
+ MOV WORD PTR[EXEFCB+RECLEN],1 ;Set one-byte record
+ MOV DX,OFFSET TRANGROUP:RELPT ;4-byte buffer for relocation address
+ MOV AH,SETDMA
+ INT 33
+ CMP [RELCNT],0
+ JZ NOREL
+RELOC:
+ MOV AH,RDBLK
+ MOV DX,OFFSET TRANGROUP:EXEFCB
+ MOV CX,4
+ INT 33 ;Read in one relocation pointer
+ OR AL,AL ;Check return code
+ JNZ BADEXE
+ MOV DI,[RELPT] ;Get offset of relocation pointer
+ MOV AX,[RELSEG] ;Get segment
+ ADD AX,BP ;Bias segment with actual load segment
+ MOV ES,AX
+ ADD WORD PTR ES:[DI],BP ;Relocate
+ DEC [RELCNT] ;Count off
+ JNZ RELOC
+;Set up exit conditions
+NOREL:
+ MOV AX,[INITSS]
+ ADD AX,BP
+ CLI
+ MOV SS,AX ;Initialize SS
+ MOV SP,[INITSP]
+ STI
+ ADD [INITCS],BP
+ MOV AX,[TPA] ;Get pointer to parameter area
+ MOV CX,[BYTCNT] ;Size of TPA segment
+ MOV ES,AX
+ MOV DS,AX ;Set segment registers to point to it
+ CALL SETUP
+ JMP DWORD PTR CS:[INITIP] ;Long jump to program
+
+SETUP:
+ AND CL,0F0H ;Adjust to even paragraph boundary
+ MOV AX,WORD PTR DS:[6] ;Get current memory size
+ SUB AX,CX ;Find out how much we're changing it
+ MOV WORD PTR DS:[6],CX
+ MOV CL,4
+ SAR AX,CL ;Convert to a segment address
+ ADD WORD PTR DS:[8],AX ;Adjust long jump to go to same place
+ MOV DX,80H
+ MOV AH,SETDMA
+ INT 33 ;Set default disk transfer address
+ MOV AX,WORD PTR CS:[PARM1] ;Pass on info about FCBs
+ XOR CX,CX
+ MOV DX,CX ;Assume no batch file
+ASSUME CS:RESGROUP
+ TEST CS:[BATCH],-1 ;Batch file in progress?
+ASSUME CS:TRANGROUP
+ JZ RET120 ;If not, all set up
+ MOV CX,CS:[RESSEG]
+ MOV DX,OFFSET RESGROUP:BATFCB ;CX:DX points to batch FCB
+RET120: RET
+TRANCODESIZE EQU $-ZERO
+TRANCODE ENDS
+COMLEN EQU TRANDATASIZE+TRANCODESIZE-102H ;End of COMMAND load. ZERO Needed to make COMLEN absolute
+TRNLEN EQU (PRETRLEN+TRANCODESIZE+TRANDATASIZE+15)/16 ;Length of transient in paragraphs
+ END PROGSTART
+\1a
\ No newline at end of file
--- /dev/null
+; HEX2BIN version 1.02
+; Converts Intel hex format files to straight binary
+
+FCB: EQU 5CH
+READ: EQU 20
+SETDMA: EQU 26
+OPEN: EQU 15
+CLOSE: EQU 16
+CREATE: EQU 22
+DELETE: EQU 19
+BLKWRT: EQU 40
+GETSEG: EQU 38
+BUFSIZ: EQU 1024
+
+ ORG 100H
+ PUT 100H
+
+HEX2BIN:
+ MOV DI,FCB+9
+ CMP B,[DI]," "
+ JNZ HAVEXT
+ MOV SI,HEX
+ MOVB
+ MOVW
+HAVEXT:
+;Get load offset (default is -100H)
+ MOV CL,4 ;Needed for shifts
+ MOV [OFFSET],-100H
+ MOV SI,FCB+11H ;Scan second FCB for offset
+ LODB
+ CMP AL," " ;Check if offset present
+ JZ HAVOFF
+ MOV B,[SIGN],0 ;Assume positive sign for now
+ CMP AL,"+"
+ JZ GETOFF ;Get a positive offset
+ CMP AL,"-"
+ JNZ GETOFF1 ;If not + or -, then not signed
+ MOV B,[SIGN],1 ;Flag as negative offset
+GETOFF:
+ LODB ;Eat sign
+GETOFF1:
+ CALL HEXCHK ;Check for valid hex character
+ JC HAVOFF ;No offset if not valid
+ XOR BX,BX ;Intialize offset sum to 0
+CONVOFF:
+ SHL BX,CL ;Multiply current sum by 16
+ OR BL,AL ;Add in current hex digit
+ LODB ;Get next digit
+ CALL HEXCHK ;And convert it to binary
+ JNC CONVOFF ;Loop until all hex digits read
+ TEST B,[SIGN],-1 ;Check if offset was to be negative
+ JZ SAVOFF
+ NEG BX
+SAVOFF:
+ MOV [OFFSET],BX
+HAVOFF:
+ MOV DX,STARTSEG
+ MOV AX,DS
+ ADD DX,AX ;Compute load segment
+ MOV AH,GETSEG
+ INT 33
+ MOV ES,DX
+ SEG ES
+ MOV CX,[6] ;Get size of segment
+ MOV [SEGSIZ],CX
+ XOR AX,AX
+ MOV DI,AX
+ MOV BP,AX
+ SHR CX
+ REP
+ STOW ;Fill entire segment with zeros
+ MOV AH,OPEN
+ MOV DX,FCB
+ INT 21H
+ OR AL,AL
+ JNZ NOFIL
+ MOV B,[FCB+32],0
+ MOV [FCB+14],BUFSIZ ;Set record size to buffer size
+ MOV DX,BUFFER
+ MOV AH,SETDMA
+ INT 33
+ MOV AH,READ
+ MOV DX,FCB ;All set up for sequential reads
+ MOV SI,BUFFER+BUFSIZ ;Flag input buffer as empty
+READHEX:
+ CALL GETCH
+ CMP AL,":" ;Search for : to start line
+ JNZ READHEX
+ CALL GETBYT ;Get byte count
+ MOV CL,AL
+ MOV CH,0
+ JCXZ DONE
+ CALL GETBYT ;Get high byte of load address
+ MOV BH,AL
+ CALL GETBYT ;Get low byte of load address
+ MOV BL,AL
+ ADD BX,[OFFSET] ;Add in offset
+ MOV DI,BX
+ CALL GETBYT ;Throw away type byte
+READLN:
+ CMP DI,[SEGSIZ]
+ JAE ADERR
+ CALL GETBYT ;Get data byte
+ STOB
+ CMP DI,BP ;Check if this is the largest address so far
+ JBE HAVBIG
+ MOV BP,DI ;Save new largest
+HAVBIG:
+ LOOP READLN
+ JP READHEX
+
+NOFIL:
+ MOV DX,NOFILE
+QUIT:
+ MOV AH,9
+ INT 21H
+ INT 20H
+
+ADERR:
+ MOV DX,ADDR
+ JMP SHOWERR
+
+GETCH:
+ CMP SI,BUFFER+BUFSIZ
+ JNZ NOREAD
+ INT 21H
+ CMP AL,1
+ JZ ERROR
+ MOV SI,BUFFER
+NOREAD:
+ LODB
+ CMP AL,1AH
+ JZ DONE
+ RET
+
+GETBYT:
+ CALL HEXDIG
+ MOV BL,AL
+ CALL HEXDIG
+ SHL BL
+ SHL BL
+ SHL BL
+ SHL BL
+ OR AL,BL
+ RET
+
+HEXCHK:
+ SUB AL,"0"
+ JC RET
+ CMP AL,10
+ JC CMCRET
+ SUB AL,"A"-"0"-10
+ JC RET
+ CMP AL,16
+CMCRET:
+ CMC
+ RET
+
+HEXDIG:
+ CALL GETCH
+ CALL HEXCHK
+ JNC RET
+ERROR:
+ MOV DX,ERRMES
+SHOWERR:
+ MOV AH,9
+ INT 21H
+DONE:
+ MOV [FCB+9],4F00H+"C" ;"CO"
+ MOV B,[FCB+11],"M"
+ MOV DX,FCB
+ MOV AH,CREATE
+ INT 21H
+ OR AL,AL
+ JNZ NOROOM
+ XOR AX,AX
+ MOV [FCB+33],AX
+ MOV [FCB+35],AX ;Set RR field
+ INC AX
+ MOV [FCB+14],AX ;Set record size
+ XOR DX,DX
+ PUSH DS
+ PUSH ES
+ POP DS ;Get load segment
+ MOV AH,SETDMA
+ INT 21H
+ POP DS
+ MOV CX,BP
+ MOV AH,BLKWRT
+ MOV DX,FCB
+ INT 21H
+ MOV AH,CLOSE
+ INT 21H
+EXIT:
+ INT 20H
+
+NOROOM:
+ MOV DX,DIRFUL
+ JMP QUIT
+
+HEX: DB "HEX"
+ERRMES: DB "Error in HEX file--conversion aborted$"
+NOFILE: DB "File not found$"
+ADDR: DB "Address out of range--conversion aborted$"
+DIRFUL: DB "Disk directory full$"
+
+OFFSET: DS 2
+SEGSIZ: DS 2
+SIGN: DS 1
+BUFFER: DS BUFSIZ
+
+START:
+STARTSEG EQU (START+15)/16
+\1a
\ No newline at end of file
--- /dev/null
+; I/O System for 86-DOS version 1.20 and later. Revised 8-02-82.
+;
+; Assumes a CPU Support card at F0 hex for character I/O,
+; with disk drivers for SCP, Tarbell, or Cromemco controllers.
+;
+; Select whether console input is interrupt-driven or polled.
+INTINP: EQU 1
+;
+; Select whether the auxiliary port is the Support Card parallel port
+; or on channel 1 of a Multiport Serial card addressed at 10H.
+PARALLELAUX: EQU 1
+SERIALAUX: EQU 0
+;
+; Select whether the printer is connected to the Support card parallel
+; output port (standard) or channel 0 of a Multiport Serial card
+; addressed at 10H.
+PARALLELPRN: EQU 1
+SERIALPRN: EQU 0
+;
+; If the Multiport Serial was chosen for either the auxiliary or the
+; printer, select the baud rate here. Refer to Multiport Serial manual
+; page 11 to pick the correct value for a given baud rate.
+PRNBAUD:EQU 7 ; 1200 baud
+AUXBAUD:EQU 0FH ; 19200 baud
+;
+; Select disk controller here.
+SCP: EQU 1
+TARBELLSD: EQU 0
+TARBELLDD: EQU 0
+CROMEMCO4FDC: EQU 0
+CROMEMCO16FDC: EQU 0
+;
+; Select if you want a special conversion version which can read/write
+; both the new Microsoft format and the old SCP format.
+; For a two drive system, drives A and B are the new Microsoft format,
+; and drives C and D are the old SCP format (where C is the same physical
+; drive as A, and D is the same drive as B). CONVERT has no effect
+; on 5.25-inch drives.
+CONVERT:EQU 1
+;
+; Select disk configuration:
+LARGE: EQU 1 ; Large drives.
+COMBIN: EQU 0 ; Two 8-inch and one 5.25-inch.
+SMALL: EQU 0 ; Three 5.25-inch drives.
+CUSTOM: EQU 0 ; User defined.
+;
+; If 8-inch drives are PerSci, select FASTSEEK here:
+; (Fastseek with Tarbell controllers doesn't work yet).
+FASTSEEK: EQU 1
+;
+; For double-density controllers, select double-sided operation of
+; 8-inch disks in double-density mode.
+LARGEDS: EQU 0
+;
+; For double-density controllers, select double-sided operation of
+; 5.25-inch disks in double-density mode.
+SMALLDS: EQU 0
+;
+; Use table below to select head step speed. Step times for 5" drives
+; are double that shown in the table. Times for Fast Seek mode (using
+; PerSci drives) is very small - 200-400 microseconds.
+;
+; Step value 1771 1793
+;
+; 0 6ms 3ms
+; 1 6ms 6ms
+; 2 10ms 10ms
+; 3 20ms 15ms
+;
+STPSPD: EQU 0
+;
+; ****** End of selections ********************************************
+;
+BIOSSEG:EQU 40H ; I/O system segment.
+BIOSLEN:EQU 2048 ; Maximum length of I/O system.
+DOSLEN: EQU 8192 ; Maximum length of MS-DOS.
+QSIZE: EQU 80 ; Input queue size.
+PBUFSIZ:EQU 128 ; Size of print buffer
+BASE: EQU 0F0H ; CPU Support card base port number.
+SIOBASE:EQU 10H ; Base port number of Multiport Serial card.
+STAT: EQU BASE+7 ; Serial I/O status port.
+DATA: EQU BASE+6 ; Serial I/O data port.
+DAV: EQU 2 ; Data available bit.
+TBMT: EQU 1 ; Transmitter buffer empty bit.
+SERIAL: EQU SERIALPRN+SERIALAUX
+STCDATA:EQU BASE+4 ; Ports for 9513 Timer chip.
+STCCOM: EQU BASE+5
+
+ IF SERIALAUX
+AUXSTAT:EQU SIOBASE+3
+AUXDATA:EQU SIOBASE+2
+ ENDIF
+
+ IF PARALLELAUX
+AUXSTAT:EQU BASE+13
+AUXDATA:EQU BASE+12
+ ENDIF
+
+ IF SERIALPRN
+PRNSTAT:EQU SIOBASE+1
+PRNDATA:EQU SIOBASE+0
+ ENDIF
+
+ IF PARALLELPRN
+PRNSTAT:EQU BASE+13
+PRNDATA:EQU BASE+12
+ ENDIF
+
+ ORG 0
+ PUT 100H
+
+ JMP INIT
+ JMP STATUS
+ JMP INP
+ JMP OUTP
+ JMP PRINT
+ JMP AUXIN
+ JMP AUXOUT
+ JMP READ
+ JMP WRITE
+ JMP DSKCHG
+ JMP SETDATE
+ JMP SETTIME
+ JMP GETTIME
+ JMP FLUSH
+ JMP MAPDEV
+MAPDEV:
+ RET L
+
+INIT:
+ XOR BP,BP ; Set up stack just below I/O system.
+ MOV SS,BP
+ MOV SP,BIOSSEG*16
+
+ IF INTINP-1
+ MOV AL,0FFH ; Mask all interrupts.
+ OUTB BASE+3
+ ENDIF
+
+ IF INTINP
+ DI ; Set up keyboard interrupt vector.
+ MOV [BP+64H],KBINT
+ MOV [BP+66H],CS
+ EI
+ ENDIF
+
+ MOV [BP+4*38H],PRNFCB
+ MOV [BP+4*38H+2],CS
+ PUSH CS
+ POP DS
+;
+; Initialize time-of-day clock.
+;
+ MOV SI,STCTAB
+ MOV CX,4 ;Initialize 4 registers
+ UP
+INITSTC:
+ LODB
+ OUT STCCOM ;Select register to initialize
+ LODB
+ OUT STCDATA
+ LODB
+ OUT STCDATA
+ LOOP INITSTC
+
+ IF SERIAL
+ MOV CX,4
+SERINIT:
+ LODB
+ OUT SIOBASE+1
+ OUT SIOBASE+3
+ LOOP SERINIT
+ LODB ;Baud rate for channel 0
+ OUT SIOBASE+8
+ LODB ;Baud rate for channel 1
+ OUT SIOBASE+9
+ ENDIF
+;
+; Move MS-DOS down to the first segment just above the I/O system.
+;
+ MOV SI,BIOSLEN ; Source points to where MS-DOS currently is.
+ MOV AX,DOSSEG ; Destination is beginning of DOSSEG.
+ MOV ES,AX
+ SUB DI,DI
+ MOV CX,DOSLEN/2 ; CX is number of words to move.
+ REP
+ MOVSW
+
+ MOV SI,INITTAB
+ MOV DX,1 ; Do auto memory scan.
+ CALL 0,DOSSEG
+;
+; Change disk read and write vectors (INT 37 and INT 38) to go to
+; DIRECTREAD and DIRECTWRITE rather than READ and WRITE.
+;
+ SUB BP,BP
+ MOV W,[BP+37*4],DIRECTREAD
+ MOV W,[BP+38*4],DIRECTWRITE
+
+ MOV DX,100H
+ MOV AH,26 ;Set DMA address
+ INT 33
+ MOV CX,[6] ;Get size of segment
+ MOV BX,DS ;Save segment for later
+;
+; DS must be set to CS so we can point to the FCB.
+;
+ MOV AX,CS
+ MOV DS,AX
+ MOV DX,FCB ;File Control Block for COMMAND.COM
+ MOV AH,15
+ INT 33 ;Open COMMAND.COM
+ OR AL,AL
+ JNZ COMERR ;Error if file not found
+ XOR AX,AX
+ MOV [FCB+33],AX ; Set 4-byte Random Record field to
+ MOV [FCB+35],AX ; beginning of file.
+ INC AX
+ MOV [FCB+14],AX ;Set record length field
+ MOV AH,39 ;Block read (CX already set)
+ INT 33
+ JCXZ COMERR ;Error if no records read
+ TEST AL,1
+ JZ COMERR ;Error if not end-of-file
+;
+; Make all segment registers the same.
+;
+ MOV DS,BX
+ MOV ES,BX
+ MOV SS,BX
+ MOV SP,5CH ;Set stack to standard value
+ XOR AX,AX
+ PUSH AX ;Put zero on top of stack for return
+ MOV DX,80H
+ MOV AH,26
+ INT 33 ;Set default transfer address (DS:0080)
+ PUSH BX ;Put segment on stack
+ MOV AX,100H
+ PUSH AX ;Put address to execute within segment on stack
+ RET L ;Jump to COMMAND
+
+COMERR:
+ MOV DX,BADCOM
+ MOV AH,9 ;Print string
+ INT 33
+ EI
+STALL: JP STALL
+
+STCTAB: DB 17H ;Select master mode register
+ DW 84F3H ;Enable time-of-day
+ DB 1 ;Counter 1 mode register
+ DW 0138H
+ DB 2
+ DW 0038H
+ DB 3
+ DW 0008H ;Set counter 3 to count days
+
+ IF SERIAL
+ DB 0B7H, 77H, 4EH, 37H, PRNBAUD, AUXBAUD
+ ENDIF
+
+BADCOM: DB 13,10,"Error in loading Command Interpreter",13,10,"$"
+FCB: DB 1,"COMMAND COM"
+ DS 25
+;
+; ************ Time and Date ************
+;
+GETTIME:
+ MOV AL,0A7H ;Save counters 1,2,3
+ OUT STCCOM
+ MOV AL,0E0H ;Enable data pointer sequencing
+ OUT STCCOM
+ MOV AL,19H ;Select hold 1 / hold cycle
+ OUT STCCOM
+ CALL STCTIME ;Get seconds & 1/100's
+ XCHG AX,DX
+ CALL STCTIME ;Get hours & minutes
+ XCHG AX,CX
+ IN STCDATA
+ MOV AH,AL
+ IN STCDATA
+ XCHG AL,AH ;Count of days
+ JP POINTSTAT
+
+STCTIME:
+ CALL STCBYTE
+ MOV CL,AH
+STCBYTE:
+ IN STCDATA
+ MOV AH,AL
+ SHR AH
+ SHR AH
+ SHR AH
+ SHR AH
+ AND AL,0FH ;Unpack BCD digits
+ AAD ;Convert to binary
+ MOV AH,AL
+ MOV AL,CL
+ RET
+
+SETTIME:
+ PUSH CX
+ PUSH DX
+ CALL LOAD0 ;Put 0 into load registers to condition timer
+ MOV AL,43H ;Load counters 1 & 2
+ OUT STCCOM
+ POP DX
+ POP CX
+ CALL LOAD
+ MOV AL,43H
+ OUT STCCOM ;Load counters 1&2
+ CALL LOAD0
+ MOV AL,27H ;Arm counters 1,2,3
+ OUT STCCOM
+ JP POINTSTAT
+
+LOAD0:
+ XOR CX,CX
+ MOV DX,CX
+LOAD:
+ MOV AL,09 ;Counter 1 load register
+ CALL OUTDX
+ MOV AL,0AH ;Counter 2 load register
+ MOV DX,CX
+OUTDX:
+ OUT STCCOM ;Select a load register
+ MOV AL,DL
+ CALL OUTBCD
+ MOV AL,DH
+OUTBCD:
+ AAM ;Convert binary to unpacked BCD
+ SHL AH
+ SHL AH
+ SHL AH
+ SHL AH
+ OR AL,AH ;Packed BCD
+ OUT STCDATA
+ RET
+
+SETDATE:
+ XCHG AX,DX ;Put date in DX
+ MOV AL,0BH ;Select Counter 3 load register
+ OUT STCCOM
+ XCHG AX,DX
+ OUT STCDATA
+ MOV AL,AH
+ OUT STCDATA
+ MOV AL,44H ;Load counter 3
+ OUT STCCOM
+POINTSTAT:
+ PUSH AX
+ MOV AL,1FH ;Point to status register
+ OUT STCCOM ; so power-off glitches won't hurt
+ POP AX
+ RET L
+;
+; ************ CONSOLE INPUT ************
+;
+
+ IF INTINP-1 ; Non-interrupt driven input.
+STATUS:
+ IN STAT
+ AND AL,DAV
+ JZ NOTHING ; Jump if nothing there.
+ PUSHF ; Save Z flag.
+ INB DATA
+ AND AL,7FH
+ SEG CS
+ MOV [QUEUE],AL ; Put new character in buffer.
+ POPF ; Return with Z flag clear.
+ RET L
+NOTHING:
+ SEG CS
+ MOV AL,[QUEUE] ; See if there's anything in the buffer.
+ NOT AL ; Set up the Z flag.
+ TEST AL,80H
+ PUSHF
+ NOT AL
+ POPF
+ RET L
+
+INP:
+ MOV AL,-1
+ SEG CS
+ XCHG AL,[QUEUE] ; Remove the character from the buffer.
+ AND AL,AL
+ JNS INRET ; Return if we have a character.
+INLOOP:
+ IN STAT ; Wait till a character is available.
+ AND AL,DAV
+ JZ INLOOP
+ IN DATA
+ AND AL,7FH
+INRET:
+FLUSH:
+ RET L
+
+QUEUE: DB -1 ; For storing characters from STATUS to INP.
+ ENDIF
+
+ IF INTINP ; Interrupt-driven input.
+;
+; Console keyboard interrupt handler.
+;
+KBINT:
+ PUSH AX
+ PUSH SI
+ MOV AL,20H ;End of Interrupt command
+ OUT BASE+2 ;Send to slave
+ IN DATA ;Get the character
+ AND AL,7FH
+ CMP AL,"C"-"@"
+ JZ FLSH
+ CMP AL,"S"-"@"
+ JZ FLSH
+ CMP AL,"F"-"@"
+ JNZ SAVKY
+FLSH:
+ CALL 13*3,BIOSSEG ; Call I/O system keyboard buffer flush.
+SAVKY:
+ SEG CS
+ MOV SI,[REAR] ;Pointer to rear of queue
+ CALL INCQ
+ SEG CS
+ CMP SI,[FRONT] ;Any room in queue?
+ JZ QFULL
+ SEG CS
+ MOV [SI],AL ;Put character in queue
+ SEG CS
+ MOV [REAR],SI ;Save pointer
+LEAVINT:
+ POP SI
+ POP AX
+ IRET
+QFULL:
+ MOV AL,7 ; BELL character.
+ CALL 3*3,BIOSSEG ; Call I/O system console output function.
+ JMPS LEAVINT
+
+STATUS:
+ PUSH SI
+;See if printer ready
+ IN PRNSTAT
+ AND AL,TBMT
+ JZ NOPRN
+ SEG CS
+ MOV SI,[PFRONT]
+ SEG CS
+ CMP SI,[PREAR] ;Anything in print queue?
+ JNZ SENDPRN
+ SEG CS
+ CMP B,[PRNFCB],-1 ;Print spooling in progress?
+ JZ NOPRN ;If not, nothing to print
+;Print spooling in progress. Get next buffer
+ PUSH DS
+ PUSH CS
+ POP DS
+ PUSH AX
+ PUSH CX
+ PUSH DX
+ PUSH [STKSAV]
+ PUSH [STKSAV+2]
+ PUSH [DMAADD]
+ PUSH [DMAADD+2]
+ MOV DX,PQUEUE
+ MOV AH,26 ;Set DMA address
+ INT 33
+ MOV DX,PRNFCB
+ MOV CX,PBUFSIZ
+ MOV AH,39 ;Read buffer
+ INT 33
+ OR AL,AL
+ JZ NOTEOF
+ MOV B,[PRNFCB],-1 ;Turn off print spooling at EOF
+NOTEOF:
+ POP [DMAADD+2]
+ POP [DMAADD]
+ POP [STKSAV+2]
+ POP [STKSAV]
+ MOV SI,CX
+ POP DX
+ POP CX
+ POP AX
+ POP DS
+ OR SI,SI
+ JZ NOPRN
+ ADD SI,PQUEUE-1
+ SEG CS
+ MOV [PREAR],SI
+ MOV SI,ENDPQ-1
+SENDPRN:
+ CALL INCPQ
+ SEG CS
+ MOV [PFRONT],SI
+ SEG CS
+ LODSB ;Get character to print
+ OUT PRNDATA
+NOPRN:
+ DI ; Disable interrupts while checking queue.
+ SEG CS
+ MOV SI,[FRONT]
+ SEG CS
+ CMP SI,[REAR] ; Anything in queue?
+ JZ NOCHR ; Jump if nothing in queue.
+ CALL INCQ
+ SEG CS
+ LODSB ;Get character (if there is one)
+ OR SI,SI ;Reset zero flag
+NOCHR:
+ EI
+ POP SI
+ RET L ;Zero clear if we have a character
+
+INP:
+ CALL STATUS,BIOSSEG ; Get I/O system console input status.
+ JZ INP
+ PUSH SI
+ DI ; Disable interrupts while changing queue pointers.
+ SEG CS
+ MOV SI,[FRONT]
+ CALL INCQ ; Permanently remove char from queue
+ SEG CS
+ MOV [FRONT],SI
+ EI
+ POP SI
+ RET L
+
+FLUSH:
+ DI
+ SEG CS
+ MOV [REAR],QUEUE
+ SEG CS
+ MOV [FRONT],QUEUE
+ EI
+ RET L
+
+INCQ:
+ INC SI
+ CMP SI,ENDQ ;Exceeded length of queue?
+ JB RET
+ MOV SI,QUEUE
+ RET
+
+INCPQ:
+ INC SI
+ CMP SI,ENDPQ ;Exceeded length of queue?
+ JB RET
+ MOV SI,PQUEUE
+ RET
+
+FRONT: DW QUEUE
+REAR: DW QUEUE
+QUEUE: DS QSIZE
+ENDQ: EQU $
+PFRONT: DW PQUEUE
+PREAR: DW PQUEUE
+PQUEUE: DS PBUFSIZ
+ENDPQ: EQU $
+PRNFCB: DB -1
+ DS 36
+ ENDIF
+
+;
+; ************ Console and Printer Output ************
+;
+OUTP:
+ PUSH AX
+OUTLP:
+ IN STAT
+ AND AL,TBMT
+ JZ OUTLP
+ POP AX
+ OUT DATA
+ RET L
+
+PRINT:
+ PUSH SI
+ SEG CS
+ MOV SI,[PREAR]
+ CALL INCPQ
+PRINLP:
+ SEG CS
+ CMP SI,[PFRONT]
+ JNZ PRNCHR
+;Print queue is full
+ PUSH AX
+ CALL STATUS,BIOSSEG ;Poll and maybe print something
+ POP AX
+ JMPS PRINLP
+PRNCHR:
+ SEG CS
+ MOV [PREAR],SI
+ SEG CS
+ MOV [SI],AL
+ POP SI
+ RET L
+;
+; ************ Auxiliary I/O ************
+;
+AUXIN:
+ IN AUXSTAT
+ AND AL,DAV
+ JZ AUXIN
+ IN AUXDATA
+ RET L
+
+AUXOUT:
+ PUSH AX
+AUXLP:
+ IN AUXSTAT
+ AND AL,TBMT
+ JZ AUXLP
+ POP AX
+ OUT AUXDATA
+ RET L
+;
+; ************ 1771/1793-type controller disk I/O ************
+;
+TARBELL:EQU TARBELLSD+TARBELLDD
+CROMEMCO:EQU CROMEMCO4FDC+CROMEMCO16FDC
+
+WD1791: EQU SCP+TARBELLDD+CROMEMCO16FDC
+WD1771: EQU TARBELLSD+CROMEMCO4FDC
+
+ IF WD1791
+READCOM:EQU 80H
+WRITECOM:EQU 0A0H
+ ENDIF
+
+ IF WD1771
+READCOM:EQU 88H
+WRITECOM:EQU 0A8H
+ ENDIF
+
+ IF SCP
+SMALLBIT:EQU 10H
+BACKBIT:EQU 04H
+DDENBIT:EQU 08H
+DONEBIT:EQU 01H
+DISK: EQU 0E0H
+ ENDIF
+
+ IF TARBELL
+BACKBIT:EQU 40H
+DDENBIT:EQU 08H
+DONEBIT:EQU 80H
+DISK: EQU 78H
+ ENDIF
+
+ IF CROMEMCO
+SMALLBIT:EQU 10H
+BACKBIT:EQU 0FDH ; Send this to port 4 to select back.
+DDENBIT:EQU 40H
+DONEBIT:EQU 01H
+DISK: EQU 30H
+ ENDIF
+
+ IF SMALLDS-1
+SMALLDDSECT: EQU 8
+ ENDIF
+
+ IF SMALLDS
+SMALLDDSECT: EQU 16
+ ENDIF
+
+ IF LARGEDS-1
+LARGEDDSECT: EQU 8
+ ENDIF
+
+ IF LARGEDS
+LARGEDDSECT: EQU 16
+ ENDIF
+;
+; Disk change function.
+; On entry:
+; AL = disk drive number.
+; On exit:
+; AH = -1 (FF hex) if disk is changed.
+; AH = 0 if don't know.
+; AH = 1 if not changed.
+;
+; CF clear if no disk error.
+; AL = disk I/O driver number.
+;
+; CF set if disk error.
+; AL = disk error code (see disk read below).
+;
+ IF WD1771
+DSKCHG:
+ MOV AH,0 ; AH = 0 in case we don't know.
+ SEG CS
+ CMP AL,[CURDRV]
+ JNZ RETL
+ PUSH AX ; Save drive number.
+
+ IF CROMEMCO
+ INB DISK+4
+ ENDIF
+
+ IF TARBELL
+ INB DISK
+ ENDIF
+
+ AND AL,20H ; Look at head load bit
+ POP AX
+ JZ RETL
+ MOV AH,1 ; AH = 1, disk not changed.
+RETL:
+ CLC ; No disk error.
+ RET L
+ ENDIF ; End of 1771 DSKCHG.
+
+ IF WD1791
+DSKCHG:
+ MOV AH,0 ; AH = 0 in case we don't know.
+ SEG CS
+ CMP AL,[CURDRV]
+ JNZ DENSCHK ; Check density if not same drive.
+ PUSH AX
+
+ IF SCP+CROMEMCO
+ INB DISK+4
+ ENDIF
+
+ IF TARBELL
+ INB DISK
+ ENDIF
+
+ AND AL,20H ; Look at head load bit
+ POP AX
+ JZ DENSCHK ; Check density if head not loaded.
+ MOV AH,1 ; AH = 1, disk not changed.
+ MOV BX,PREVDENS
+ SEG CS
+ XLAT ; Get previous density
+ CLC ; No disk error.
+ RET L
+DENSCHK:
+ CALL CHKNEW ; Unload head if selecting new drive.
+ CBW
+ XCHG AX,SI
+ ADD SI,PREVDENS
+ MOV CX,4 ; Try each density twice
+ MOV AH,0 ; Disk may not have been changed.
+CHKDENS:
+ SEG CS
+ MOV AL,[SI] ; Get previous disk I/O driver number.
+ MOV BX,DRVTAB
+ SEG CS
+ XLAT ; Get drive select byte for previous density
+
+ IF CROMEMCO16FDC
+ CALL MOTOR ; Wait for motor to come up to speed.
+ ENDIF
+
+ OUT DISK+4 ; Select disk
+ MOV AL,0C4H ; READ ADDRESS command
+ CALL DCOM
+ AND AL,98H
+ IN DISK+3 ; Eat last byte to reset DRQ
+ JZ HAVDENS ; Jump if no error in reading address.
+ NOT AH ; AH = -1 (disk changed) if new density works.
+ SEG CS
+ XOR B,[SI],1 ; Try other density
+ LOOP CHKDENS
+ MOV AX,2 ; Couldn't read disk at all, AH = 0 for don't
+ STC ; know if disk changed, AL = error code 2 -
+ RET L ; disk not ready, carry set to indicate error.
+
+HAVDENS:
+ SEG CS
+ LODSB ; AL = disk I/O driver number.
+ CLC ; No disk error.
+ RET L
+
+PREVDENS:DB 1,3,5,7,9,11,13 ; Table of previous disk I/O driver numbers.
+ ENDIF ; End of 1793 DSKCHG function.
+
+CHKNEW:
+ MOV AH,AL ; Save disk drive number in AH.
+ SEG CS ; AL = previous disk drive number,
+ XCHG AL,[CURDRV] ; make new drive current.
+ CMP AL,AH ; Changing drives?
+ JZ RET
+;
+; If changing drives, unload head so the head load delay one-shot will
+; fire again. Do it by seeking to the same track with the H bit reset.
+;
+ IN DISK+1 ; Get current track number
+ OUT DISK+3 ; Make it the track to seek to
+ MOV AL,10H ; Seek and unload head
+ CALL DCOM
+ MOV AL,AH ; Restore current drive number
+ RET
+
+ IF CROMEMCO16FDC
+MOTOR:
+ PUSH AX
+ MOV AH,AL
+ IN DISK+4 ; See if the motor is on.
+ TEST AL,08H
+ MOV AL,AH
+ OUTB DISK+4 ; Select drive & start motor.
+ JNZ MOTORSON ; No delay if motors already on.
+ PUSH CX
+ MOV CX,43716 ; Loop count for 1 second.
+MOTORDELAY: ; (8 MHz, 16-bit memory).
+ AAM ; 83 clocks.
+ AAM ; 83 clocks.
+ LOOP MOTORDELAY ; 17 clocks.
+ POP CX
+MOTORSON:
+ POP AX
+ RET
+ ENDIF
+;
+; Disk read function.
+;
+; On entry:
+; AL = Disk I/O driver number
+; BX = Disk transfer address in DS
+; CX = Number of sectors to transfer
+; DX = Logical record number of transfer
+; On exit:
+; CF clear if transfer complete
+;
+; CF set if hard disk error.
+; CX = number of sectors left to transfer.
+; AL = disk error code
+; 0 = write protect error
+; 2 = not ready error
+; 4 = "data" (CRC) error
+; 6 = seek error
+; 8 = sector not found
+; 10 = write fault
+; 12 = "disk" (none of the above) error
+;
+READ:
+ CALL SEEK ;Position head
+ JC ERROR
+ PUSH ES ; Make ES same as DS.
+ MOV BX,DS
+ MOV ES,BX
+RDLP:
+ CALL READSECT ;Perform sector read
+ JC POPESERROR
+ INC DH ;Next sector number
+ LOOP RDLP ;Read each sector requested
+ CLC ; No errors.
+ POP ES ; Restore ES register.
+ RET L
+;
+; Disk write function.
+; Registers same on entry and exit as read above.
+;
+WRITE:
+ CALL SEEK ;Position head
+ JC ERROR
+WRTLP:
+ CALL WRITESECT ;Perform sector write
+ JC ERROR
+ INC DH ;Bump sector counter
+ LOOP WRTLP ;Write CX sectors
+ CLC ; No errors.
+WRITERET:
+ RET L
+
+POPESERROR:
+ POP ES ; Restore ES register.
+ERROR:
+ MOV BL,-1
+ SEG CS
+ MOV [DI],BL ; Indicate we don't know where head is.
+ MOV SI,ERRTAB
+GETCOD:
+ INC BL ; Increment to next error code.
+ SEG CS
+ LODB
+ TEST AH,AL ; See if error code matches disk status.
+ JZ GETCOD ; Try another if not.
+ MOV AL,BL ; Now we've got the code.
+ SHL AL ; Multiply by two.
+ STC
+ RET L
+
+ERRTAB:
+ DB 40H ;Write protect error
+ DB 80H ;Not ready error
+ DB 8 ;CRC error
+ DB 2 ;Seek error
+ DB 10H ;Sector not found
+ DB 20H ;Write fault
+ DB 7 ;"Disk" error
+;
+; Direct disk read and write from INT 37 and INT 38. Subroutine GETIODRIVER
+; calls DSKCHG to convert disk drive number to I/O driver number.
+;
+; Setting CURDRV to -1 before calling DSKCHG forces DSKCHG to check the disk's
+; density before returning the I/O driver number. This is necessary because
+; programs such as FORMAT could change the density of a disk and leave the
+; head loaded. If the head is loaded DSKCHG assumes the disk hasn't been
+; changed and returns the old I/O driver number which could be wrong.
+;
+; CURDRV is set to -1 before returning so when DSKCHG is called by the
+; operating system, it will tell the operating system the disk may have
+; been changed (because it may have been).
+;
+DIRECTREAD:
+
+ IF WD1791
+ CALL GETIODRIVER ; Convert drive number to I/O driver number.
+ JC DIRECTRET ; Return if DSKCHG returned error.
+ ENDIF
+
+ CALL 7*3,BIOSSEG ; Call READ.
+ JMPS DIRECTRET
+
+DIRECTWRITE:
+
+ IF WD1791
+ CALL GETIODRIVER ; Convert drive number to I/O driver number.
+ JC DIRECTRET ; Return if DSKCHG returned error.
+ ENDIF
+
+ CALL 8*3,BIOSSEG ; Call WRITE.
+DIRECTRET:
+ SEG CS
+ MOV B,[CURDRV],-1 ; Force DSKCHG to do density check.
+ RET L
+
+ IF WD1791
+GETIODRIVER:
+ SEG CS
+ MOV B,[CURDRV],-1 ; Force DSKCHG to do density check.
+ PUSH BX
+ PUSH CX
+ CALL 9*3,BIOSSEG ; Call DSKCHG.
+ POP CX
+ POP BX
+ RET
+ ENDIF
+;
+; Function:
+; Seeks to proper track.
+; On entry:
+; Same as for disk read or write above.
+; On exit:
+; AH = Drive select byte
+; DL = Track number
+; DH = Sector number
+; SI = Disk transfer address in DS
+; DI = pointer to drive's track counter in CS
+; CX unchanged (number of sectors)
+;
+SEEK:
+ MOV SI,BX ; Save transfer address
+ CBW
+ MOV BX,AX ; Prepare to index on drive number
+
+ IF WD1791 ; If two disk formats per drive.
+ SHR AL ; Convert to physical disk drive number.
+ ENDIF
+
+ CALL CHKNEW ; Unload head if changing drives.
+ SEG CS
+ MOV AL,[BX+DRVTAB] ; Get drive-select byte.
+
+ IF CROMEMCO16FDC
+ CALL MOTOR ; Wait for the motors to come up to speed.
+ ENDIF
+
+ OUTB DISK+4 ; Select drive.
+
+ IF CROMEMCO
+ OR AL,80H ; Set auto-wait bit.
+ ENDIF
+
+ MOV AH,AL ; Save drive-select byte in AH.
+ XCHG AX,DX ; AX = logical sector number.
+ MOV DL,26 ; 26 sectors/track unless changed below
+
+ IF SCP
+ TEST DH,SMALLBIT ; Check if small disk.
+ JZ BIGONE ; Jump if big disk.
+ MOV DL,18 ; Assume 18 sectors on small track.
+ TEST DH,DDENBIT ; Check if double-density.
+ JZ HAVSECT ; Jump if not.
+ MOV DL,SMALLDDSECT ; Number of sectors on small DD track.
+ JP HAVSECT
+BIGONE:
+ TEST DH,DDENBIT ; Check if double-density.
+ JZ HAVSECT ; Jump if not.
+ MOV DL,LARGEDDSECT ; Number of sectors on big DD track.
+ ENDIF
+
+ IF TARBELLDD ; Tarbell DD controller.
+ TEST DH,DDENBIT ; Check for double-density.
+ JZ HAVSECT
+ MOV DL,LARGEDDSECT ; Number of sectors on DD track.
+ ENDIF
+
+ IF CROMEMCO4FDC
+ TEST DH,SMALLBIT ; Check if small disk.
+ JNZ HAVSECT ; Jump if not.
+ MOV DL,18 ; 18 sectors on small disk track.
+ ENDIF
+
+ IF CROMEMCO16FDC
+ TEST DH,SMALLBIT ; Check if small disk.
+ JNZ BIGONE ; Jump if big disk.
+ MOV DL,18 ; Assume 18 sectors on small track.
+ TEST DH,DDENBIT ; Check if double-density.
+ JZ HAVSECT ; Jump if not.
+ MOV DL,SMALLDDSECT ; Number of sectors on small DD track.
+ JP HAVSECT
+BIGONE:
+ TEST DH,DDENBIT ; Check if double-density.
+ JZ HAVSECT ; Jump if not.
+ MOV DL,LARGEDDSECT ; Number of sectors on big DD track.
+ ENDIF
+
+HAVSECT:
+ DIV AL,DL ; AL = track, AH = sector.
+ XCHG AX,DX ; AH has drive-select byte, DX = track & sector.
+ INC DH ; Sectors start at one, not zero.
+ SEG CS
+ MOV BL,[BX+TRKPT] ; Get this drive's displacement into track table.
+ ADD BX,TRKTAB ; BX now points to track counter for this drive.
+ MOV DI,BX
+ MOV AL,DL ; Move new track number into AL.
+ SEG CS
+ XCHG AL,[DI] ; Xchange current track with desired track
+ OUT DISK+1 ; Inform controller chip of current track
+ CMP AL,DL ; See if we're at the right track.
+ JZ RET
+ MOV BH,2 ; Seek retry count
+ CMP AL,-1 ; Head position known?
+ JNZ NOHOME ; If not, home head
+TRYSK:
+ CALL HOME
+ JC SEEKERR
+NOHOME:
+ MOV AL,DL ; AL = new track number.
+ OUT DISK+3
+ MOV AL,1CH+STPSPD ; Seek command.
+ CALL MOVHEAD
+ AND AL,98H ; Accept not ready, seek, & CRC error bits.
+ JZ RET
+ JS SEEKERR ; No retries if not ready
+ DEC BH
+ JNZ TRYSK
+SEEKERR:
+ MOV AH,AL ; Put status in AH.
+ TEST AL,80H ; See if it was a Not Ready error.
+ STC
+ JNZ RET ; Status is OK for Not Ready error.
+ MOV AH,2 ; Everything else is seek error.
+ RET
+
+SETUP:
+ MOV BL,DH ; Move sector number to BL to play with
+
+ IF SCP+CROMEMCO16FDC
+ TEST AH,DDENBIT ; Check for double density.
+ JZ CHECKSMALL ; Not DD, check size for SD.
+ ENDIF
+
+ IF TARBELLDD
+ TEST AH,DDENBIT ; Check for double density.
+ JZ CHECK26 ; Not DD.
+ ENDIF
+
+ IF WD1791
+
+ IF (SCP+TARBELL)*LARGEDS+SCP*SMALLDS
+ MOV AL,AH ; Select front side of disk.
+ OUT DISK+4
+ ENDIF
+
+ IF CROMEMCO*(LARGEDS+SMALLDS)
+ MOV AL,0FFH ; Select front side of disk.
+ OUT 04H
+ ENDIF
+
+ CMP BL,8 ; See if legal DD sector number.
+ JBE PUTSEC ; Jump if ok.
+
+ IF (LARGEDS-1)*((SMALLDS*(SCP+CROMEMCO))-1)
+ JP STEP ; If only SS drives, we gotta step.
+ ENDIF
+
+ IF SCP*LARGEDS*(SMALLDS-1)
+ TEST AH,SMALLBIT ; Check for 5.25 inch disk.
+ JNZ STEP ; Jump if small because SMALLDS is off.
+ ENDIF
+
+ IF SCP*SMALLDS*(LARGEDS-1)
+ TEST AH,SMALLBIT ; Check for 8 inch disk.
+ JZ STEP ; Jump if large because LARGEDS is off.
+ ENDIF
+
+ IF CROMEMCO16FDC*LARGEDS*(SMALLDS-1)
+ TEST AH,SMALLBIT ; Check for 5.25 inch disk.
+ JZ STEP ; Jump if small because SMALLDS is off.
+ ENDIF
+
+ IF CROMEMCO16FDC*SMALLDS*(LARGEDS-1)
+ TEST AH,SMALLBIT ; Check for 8 inch disk.
+ JNZ STEP ; Jump if large because LARGEDS is off.
+ ENDIF
+
+ IF LARGEDS+SMALLDS*(SCP+CROMEMCO)
+ SUB BL,8 ; Find true sector for back side.
+ CMP BL,8 ; See if ok now.
+ JA STEP ; Have to step if still too big.
+
+ IF SCP+TARBELLDD
+ MOV AL,AH ; Move drive select byte into AL.
+ OR AL,BACKBIT ; Select back side.
+ OUT DISK+4
+ ENDIF
+
+ IF CROMEMCO16FDC
+ MOV AL,BACKBIT ; Select back side.
+ OUT 04H
+ ENDIF
+
+ JP PUTSEC
+ ENDIF
+
+ ENDIF
+
+ IF SCP
+CHECKSMALL:
+ TEST AH,SMALLBIT ; See if big disk.
+ JZ CHECK26 ; Jump if big.
+ ENDIF
+
+ IF CROMEMCO
+CHECKSMALL:
+ TEST AH,SMALLBIT ; See if big disk.
+ JNZ CHECK26 ; Jump if big.
+ ENDIF
+
+ IF SCP+CROMEMCO
+ CMP BL,18 ; See if legal small SD/SS sector.
+ JA STEP ; Jump if not.
+ ENDIF
+
+CHECK26:
+ CMP BL,26 ; See if legal large SD/SS sector.
+ JBE PUTSEC ; Jump if ok.
+STEP:
+ INC DL ; Increment track number.
+ MOV AL,58H ; Step in with update.
+ CALL DCOM
+ SEG CS
+ INC B,[DI] ; Increment the track pointer.
+ MOV DH,1 ; After step, do first sector.
+ MOV BL,DH ; Fix temporary sector number also.
+PUTSEC:
+ MOV AL,BL ; Output sector number to controller.
+ OUT DISK+2
+ DI ; Interrupts not allowed until I/O done
+
+ IF SCP+CROMEMCO
+ INB DISK+4 ; Get head-load bit.
+ ENDIF
+
+ IF TARBELL
+ INB DISK
+ ENDIF
+
+ NOT AL
+ AND AL,20H ; Check head load status
+ JZ RET
+ MOV AL,4
+ RET
+
+READSECT:
+ CALL SETUP
+ MOV BL,10 ; Retry count for hard error.
+ XCHG DI,SI ; Transfer address to DI.
+ PUSH DX ; Save track & sector number.
+ MOV DL,DISK+3 ; Disk controller data port.
+RDAGN:
+ OR AL,READCOM
+ OUT DISK
+
+ IF CROMEMCO
+ MOV AL,AH ; Turn on auto-wait.
+ OUT DISK+4
+ ENDIF
+
+ MOV BP,DI ; Save address for retry.
+ JMPS RLOOPENTRY
+RLOOP:
+ STOB ; Write into memory.
+RLOOPENTRY:
+
+ IF SCP
+ IN DISK+5 ; Wait for DRQ or INTRQ.
+ ENDIF
+
+ IF TARBELL+CROMEMCO
+ IN DISK+4
+ ENDIF
+
+ IF TARBELL
+ SHL AL
+ INB DX ; Read data from disk controller chip.
+ JC RLOOP
+ ENDIF
+
+ IF SCP+CROMEMCO
+ SHR AL
+ INB DX ; Read data from disk controller chip.
+ JNC RLOOP
+ ENDIF
+
+ EI ; Interrupts OK now
+ CALL GETSTAT
+ AND AL,9CH
+ JZ RDPOP
+ MOV DI,BP ; Get origainal address back for retry.
+ MOV BH,AL ; Save error status for report
+ MOV AL,0
+ DEC BL
+ JNZ RDAGN
+ MOV AH,BH ; Put error status in AH.
+ STC
+RDPOP:
+ POP DX ; Get back track & sector number.
+ XCHG SI,DI ; Address back to SI.
+
+ IF TARBELL
+FORCINT:
+ MOV AL,0D0H ; Tarbell controllers need this Force Interrupt
+ OUT DISK ; so that Type I status is always available
+ MOV AL,10 ; at the 1771/1793 status port so we can find
+INTDLY: ; out if the head is loaded. SCP and Cromemco
+ DEC AL ; controllers have head-load status available
+ JNZ INTDLY ; at the DISK+4 status port.
+ ENDIF
+
+ RET
+
+WRITESECT:
+ CALL SETUP
+ MOV BL,10
+ PUSH DX ; Save track & sector number.
+ MOV DL,DISK+3 ; Disk controller data port.
+WRTAGN:
+ OR AL,WRITECOM
+ OUT DISK
+
+ IF CROMEMCO
+ MOV AL,AH ; Turn on auto-wait.
+ OUT DISK+4
+ ENDIF
+
+ MOV BP,SI
+WRLOOP:
+
+ IF SCP
+ INB DISK+5
+ ENDIF
+
+ IF TARBELL+CROMEMCO
+ INB DISK+4
+ ENDIF
+
+ IF SCP+CROMEMCO
+ SHR AL
+ LODB ; Get data from memory.
+ OUTB DX ; Write to disk.
+ JNC WRLOOP
+ ENDIF
+
+ IF TARBELL
+ SHL AL
+ LODB ; Get data from memory.
+ OUTB DX ; Write to disk.
+ JC WRLOOP
+ ENDIF
+
+ EI ; Interrupts OK now.
+ DEC SI
+ CALL GETSTAT
+ AND AL,0FCH
+ JZ WRPOP
+ MOV SI,BP
+ MOV BH,AL
+ MOV AL,0
+ DEC BL
+ JNZ WRTAGN
+ MOV AH,BH ; Error status to AH.
+ STC
+WRPOP:
+ POP DX ; Get back track & sector number.
+
+ IF TARBELL
+ JMPS FORCINT
+ ENDIF
+
+ IF SCP+CROMEMCO
+ RET
+ ENDIF
+;
+; Subroutine to restore the read/write head to track 0.
+;
+ IF SCP+CROMEMCO+TARBELL*(FASTSEEK-1)
+HOME:
+ ENDIF
+
+ IF FASTSEEK*CROMEMCO
+ TEST AH,SMALLBIT ; Check for large disk.
+ JNZ RESTORE ; Big disks are fast seek PerSci.
+ ENDIF
+
+ MOV BL,3
+TRYHOM:
+
+ IF SCP*FASTSEEK
+ MOV AL,AH ; Turn on Restore to PerSci.
+ OR AL,80H
+ OUTB DISK+4
+ ENDIF
+
+ MOV AL,0CH+STPSPD ; Restore with verify command.
+ CALL DCOM
+ AND AL,98H
+
+ IF SCP*FASTSEEK
+ MOV AL,AH ; Restore off.
+ OUTB DISK+4
+ ENDIF
+
+ JZ RET
+ JS HOMERR ; No retries if not ready
+ MOV AL,58H+STPSPD ; Step in with update
+ CALL DCOM
+ DEC BL
+ JNZ TRYHOM
+HOMERR:
+ STC
+ RET
+;
+; RESTORE for PerSci drives.
+; Doesn't exist yet for Tarbell controllers.
+;
+ IF FASTSEEK*TARBELL
+HOME:
+RESTORE:
+ RET
+ ENDIF
+
+ IF FASTSEEK*CROMEMCO4FDC
+RESTORE:
+ MOV AL,0C4H ;READ ADDRESS command to keep head loaded
+ OUT DISK
+ MOV AL,77H
+ OUT 4
+CHKRES:
+ IN 4
+ AND AL,40H
+ JZ RESDONE
+ IN DISK+4
+ TEST AL,DONEBIT
+ JZ CHKRES
+ IN DISK
+ JP RESTORE ;Reload head
+RESDONE:
+ MOV AL,7FH
+ OUT 4
+ CALL GETSTAT
+ MOV AL,0
+ OUT DISK+1 ;Tell 1771 we're now on track 0
+ RET
+ ENDIF
+
+ IF FASTSEEK*CROMEMCO16FDC
+RESTORE:
+ MOV AL,0D7H ; Turn on Drive-Select and Restore.
+ OUTB 4
+ PUSH AX
+ AAM ; 10 uS delay.
+ POP AX
+RESWAIT:
+ INB 4 ; Wait till Seek Complete is active.
+ TEST AL,40H
+ JNZ RESWAIT
+ MOV AL,0FFH ; Turn off Drive-Select and Restore.
+ OUTB 4
+ SUB AL,AL ; Tell 1793 we're on track 0.
+ OUTB DISK+1
+ RET
+ ENDIF
+;
+; Subroutine to move the read/write head to the desired track.
+; Usually falls through to DCOM unless special handling for
+; PerSci drives is required in which case go to FASTSK.
+;
+ IF SCP+CROMEMCO+TARBELL*(FASTSEEK-1)
+MOVHEAD:
+ ENDIF
+
+ IF CROMEMCO*FASTSEEK
+ TEST AH,SMALLBIT ; Check for PerSci.
+ JNZ FASTSK
+ ENDIF
+
+DCOM:
+ OUT DISK
+ PUSH AX
+ AAM ;Delay 10 microseconds
+ POP AX
+GETSTAT:
+ IN DISK+4
+ TEST AL,DONEBIT
+
+ IF TARBELL
+ JNZ GETSTAT
+ ENDIF
+
+ IF SCP+CROMEMCO
+ JZ GETSTAT
+ ENDIF
+
+ IN DISK
+ RET
+;
+; Fast seek code for PerSci drives.
+; Tarbell not installed yet.
+;
+ IF FASTSEEK*TARBELL
+MOVHEAD:
+FASTSK:
+ RET
+ ENDIF
+
+ IF FASTSEEK*CROMEMCO
+FASTSK:
+ MOV AL,6FH
+ OUT 4
+ MOV AL,18H
+ CALL DCOM
+SKWAIT:
+ IN 4
+ TEST AL,40H
+ JNZ SKWAIT
+ MOV AL,7FH
+ OUT 4
+ MOV AL,0
+ RET
+ ENDIF
+
+CURDRV: DB -1
+;
+; Explanation of tables below.
+;
+; DRVTAB is a table of bytes which are sent to the disk controller as drive-
+; select bytes to choose which physical drive is selected for each disk I/O
+; driver. It also selects whether the disk is 5.25-inch or 8-inch, single-
+; density or double-density. Always select side 0 in the drive-select byte if
+; a side-select bit is available. There should be one entry in the DRVTAB
+; table for each disk I/O driver. Exactly which bits in the drive-select byte
+; do what depends on which disk controller is used.
+;
+; TRKTAB is a table of bytes used to store which track the read/write
+; head of each drive is on. Each physical drive should have its own
+; entry in TRKTAB.
+;
+; TRKPT is a table of bytes which indicates which TRKTAB entry each
+; disk I/O driver should use. Since each physical drive may be used for
+; more than one disk I/O driver, more than one entry in TRKPT may point
+; to the same entry in TRKTAB. Drives such as PerSci 277s which use
+; the same head positioner for more than one drive should share entrys
+; in TRKTAB.
+;
+; INITTAB is the initialization table for 86-DOS as described in the
+; 86-DOS Programer's Manual under "Customizing the I/O System."
+;
+ IF SCP*COMBIN*FASTSEEK
+;
+; A PerSci 277 or 299 and one 5.25-inch drive.
+;
+DRVTAB: DB 00H,08H,01H,09H,10H,18H,00H,08H,01H,09H
+TRKPT: DB 0,0,0,0,1,1,0,0,0,0
+TRKTAB: DB -1,-1
+INITTAB:
+ IF CONVERT-1
+ DB 6 ; Number of disk I/O drivers.
+ ENDIF
+
+ IF CONVERT
+ DB 10
+ ENDIF
+
+ DB 0 ; Disk I/O driver 0 uses disk drive 0.
+ DW LSDRIVE ; Disk I/O driver 0 is 8-inch single-density.
+ DB 0 ; Disk I/O driver 1 uses disk drive 0.
+ DW LDDRIVE ; Disk I/O driver 1 is 8-inch double-density.
+ DB 1 ; Etc.
+ DW LSDRIVE
+ DB 1
+ DW LDDRIVE
+ DB 2
+ DW SSDRIVE
+ DB 2
+ DW SDDRIVE
+
+ IF CONVERT
+ DB 3
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLDDRIVE
+ DB 4
+ DW OLDLSDRIVE
+ DB 4
+ DW OLDLDDRIVE
+ ENDIF
+ ENDIF
+
+ IF SCP*LARGE*FASTSEEK
+;
+; PerSci 277 or 299.
+;
+DRVTAB: DB 00H,08H,01H,09H,00H,08H,01H,09H
+TRKPT: DB 0,0,0,0,0,0,0,0
+TRKTAB: DB -1
+INITTAB:
+ IF CONVERT-1
+ DB 4
+ ENDIF
+
+ IF CONVERT
+ DB 8
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 0
+ DW LDDRIVE
+ DB 1
+ DW LSDRIVE
+ DB 1
+ DW LDDRIVE
+
+ IF CONVERT
+ DB 2
+ DW OLDLSDRIVE
+ DB 2
+ DW OLDLDDRIVE
+ DB 3
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLDDRIVE
+ ENDIF
+ ENDIF
+
+ IF TARBELLDD
+;
+; Two 8-inch Shugart-type drives.
+;
+DRVTAB: DB 0,8,10H,18H,0,8,10H,18H
+TRKPT: DB 0,0,1,1,0,0,1,1
+TRKTAB: DB -1,-1
+INITTAB:
+
+ IF CONVERT-1
+ DB 4
+ ENDIF
+
+ IF CONVERT
+ DB 8
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 0
+ DW LDDRIVE
+ DB 1
+ DW LSDRIVE
+ DB 1
+ DW LDDRIVE
+
+ IF CONVERT
+ DB 2
+ DW OLDLSDRIVE
+ DB 2
+ DW OLDLDDRIVE
+ DB 3
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLDDRIVE
+ ENDIF
+ ENDIF
+
+ IF TARBELLSD
+;
+; Four 8-inch Shugart-type drives.
+;
+DRVTAB: DB 0F2H,0E2H,0F2H,0E2H
+TRKPT: DB 0,1,0,1
+TRKTAB: DB -1,-1
+INITTAB:
+
+ IF CONVERT-1
+ DB 2
+ ENDIF
+
+ IF CONVERT
+ DB 4
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 1
+ DW LSDRIVE
+
+ IF CONVERT
+ DB 2
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLSDRIVE
+ ENDIF
+ ENDIF
+;
+; Cromemco drive select byte is derived as follows:
+; Bit 7 = 0
+; Bit 6 = 1 if double density (if 16FDC)
+; Bit 5 = 1 (motor on)
+; Bit 4 = 0 for 5", 1 for 8" drives
+; Bit 3 = 1 for drive 3
+; Bit 2 = 1 for drive 2
+; Bit 1 = 1 for drive 1
+; Bit 0 = 1 for drive 0
+;
+ IF CROMEMCO4FDC*LARGE
+;
+; PerSci 277 drive.
+;
+DRVTAB: DB 31H,32H,31H,32H
+TRKPT: DB 0,0,0,0
+TRKTAB: DB -1
+INITTAB:
+
+ IF CONVERT-1
+ DB 2
+ ENDIF
+
+ IF CONVERT
+ DB 4
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 1
+ DW LSDRIVE
+
+ IF CONVERT
+ DB 2
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLSDRIVE
+ ENDIF
+ ENDIF
+
+ IF CROMEMCO4FDC*COMBIN
+;
+; A PerSci 277 and one 5.25-inch drive.
+;
+DRVTAB: DB 31H,32H,24H,31H,32H
+TRKPT: DB 0,0,1,0,0
+TRKTAB: DB -1,-1
+INITTAB:
+
+ IF CONVERT-1
+ DB 3
+ ENDIF
+
+ IF CONVERT
+ DB 5
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 1
+ DW LSDRIVE
+ DB 2
+ DW SSDRIVE
+
+ IF CONVERT
+ DB 3
+ DW OLDLSDRIVE
+ DB 4
+ DW OLDLSDRIVE
+ ENDIF
+ ENDIF
+
+ IF CROMEMCO4FDC*SMALL
+;
+; Three 5.25-inch drives.
+;
+DRVTAB: DB 21H,22H,24H
+TRKPT: DB 0,1,2
+TRKTAB: DB -1,-1,-1
+INITTAB:DB 3
+ DB 0
+ DW SSDRIVE
+ DB 1
+ DW SSDRIVE
+ DB 2
+ DW SSDRIVE
+ ENDIF
+
+ IF CUSTOM
+;
+; Cromemco 4FDC with two 8-inch Shugart-type drives.
+;
+DRVTAB: DB 31H,32H,31H,32H
+TRKPT: DB 0,1,0,1
+TRKTAB: DB -1,-1
+INITTAB:
+ IF CONVERT-1
+ DB 2
+ ENDIF
+
+ IF CONVERT
+ DB 4
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 1
+ DW LSDRIVE
+
+ IF CONVERT
+ DB 2
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLSDRIVE
+ ENDIF
+ ENDIF
+
+ IF CROMEMCO16FDC*SMALL
+;
+; Three 5.25-inch drives.
+;
+DRVTAB: DB 21H,61H,22H,62H,24H,64H
+TRKPT: DB 0,0,1,1,2,2
+TRKTAB: DB -1,-1,-1
+INITTAB:DB 6
+ DB 0
+ DW SSDRIVE
+ DB 0
+ DW SDDRIVE
+ DB 1
+ DW SSDRIVE
+ DB 1
+ DW SDDRIVE
+ DB 2
+ DW SSDRIVE
+ DB 2
+ DW SDDRIVE
+ ENDIF
+
+ IF CROMEMCO16FDC*COMBIN
+;
+; A PerSci 277 or 299 and one 5.25-inch drive.
+;
+DRVTAB: DB 31H,71H,32H,72H,24H,64H,31H,71H,32H,72H
+TRKPT: DB 0,0,0,0,1,1,0,0,0,0
+TRKTAB: DB -1,-1
+INITTAB:
+ IF CONVERT-1
+ DB 6
+ ENDIF
+
+ IF CONVERT
+ DB 10
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 0
+ DW LDDRIVE
+ DB 1
+ DW LSDRIVE
+ DB 1
+ DW LDDRIVE
+ DB 2
+ DW SSDRIVE
+ DB 2
+ DW SDDRIVE
+
+ IF CONVERT
+ DB 3
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLDDRIVE
+ DB 4
+ DW OLDLSDRIVE
+ DB 4
+ DW OLDLDDRIVE
+ ENDIF
+ ENDIF
+
+ IF CROMEMCO16FDC*LARGE
+;
+; A PerSci 277 or 299.
+;
+DRVTAB: DB 31H,71H,32H,72H,31H,71H,32H,72H
+TRKPT: DB 0,0,0,0,0,0,0,0
+TRKTAB: DB -1
+INITTAB:
+ IF CONVERT-1
+ DB 4
+ ENDIF
+
+ IF CONVERT
+ DB 8
+ ENDIF
+
+ DB 0
+ DW LSDRIVE
+ DB 0
+ DW LDDRIVE
+ DB 1
+ DW LSDRIVE
+ DB 1
+ DW LDDRIVE
+
+ IF CONVERT
+ DB 2
+ DW OLDLSDRIVE
+ DB 2
+ DW OLDLDDRIVE
+ DB 3
+ DW OLDLSDRIVE
+ DB 3
+ DW OLDLDDRIVE
+ ENDIF
+ ENDIF
+
+ IF SMALL+COMBIN
+SSDRIVE:
+ DW 128 ; Sector size in bytes.
+ DB 2 ; Sector per allocation unit.
+ DW 54 ; Reserved sectors.
+ DB 2 ; Number of allocation tables.
+ DW 64 ; Number of directory entrys.
+ DW 720 ; Number of sectors on the disk.
+
+ IF SMALLDS-1
+SDDRIVE: ; This is the IBM Personal Computer
+ DW 512 ; disk format.
+ DB 1
+ DW 1
+ DB 2
+ DW 64
+ DW 320
+ ENDIF
+
+ IF SMALLDS
+SDDRIVE:
+ DW 512
+ DB 2
+ DW 1
+ DB 2
+ DW 112
+ DW 640
+ ENDIF
+ ENDIF ; End of small drive DPTs.
+
+ IF COMBIN+LARGE
+LSDRIVE:
+ DW 128 ; Size of sector in bytes.
+ DB 4 ; Sectors per allocation unit.
+ DW 1 ; Number of reserved sectors.
+ DB 2 ; Number of File Allocation Tables.
+ DW 68 ; Number of directory entrys.
+ DW 77*26 ; Number of sectors on the disk.
+
+ IF CONVERT
+OLDLSDRIVE:
+ DW 128
+ DB 4
+ DW 52 ; Old format had two tracks reserved.
+ DB 2
+ DW 64 ; 64 directory entrys.
+ DW 77*26
+ ENDIF
+
+ IF LARGEDS-1
+OLDLDDRIVE:
+LDDRIVE:
+ DW 1024
+ DB 1
+ DW 1
+ DB 2
+ DW 96
+ DW 77*8
+ ENDIF
+
+ IF LARGEDS
+LDDRIVE:
+ DW 1024
+ DB 1
+ DW 1
+ DB 2
+ DW 192 ; 192 directory entrys in new 8-inch DD/DS format.
+ DW 77*8*2
+
+ IF CONVERT
+OLDLDDRIVE:
+ DW 1024
+ DB 1
+ DW 1
+ DB 2
+ DW 128 ; 128 directory entrys in old 8-inch DD/DS format.
+ DW 77*8*2
+ ENDIF
+ ENDIF
+
+ ENDIF ; End of large drive DPTs.
+
+DOSSEG: EQU ($+15)/16+BIOSSEG ; Compute segment to use for 86-DOS.
+DOSDIF: EQU 16*(DOSSEG-BIOSSEG)
+STKSAV: EQU 1701H+DOSDIF
+DMAADD: EQU 15B4H+DOSDIF
+ END
+\1a
\ No newline at end of file
--- /dev/null
+; 86-DOS High-performance operating system for the 8086 version 1.25
+; by Tim Paterson
+
+
+; ****************** Revision History *************************
+; >> EVERY change must noted below!! <<
+;
+; 0.34 12/29/80 General release, updating all past customers
+; 0.42 02/25/81 32-byte directory entries added
+; 0.56 03/23/81 Variable record and sector sizes
+; 0.60 03/27/81 Ctrl-C exit changes, including register save on user stack
+; 0.74 04/15/81 Recognize I/O devices with file names
+; 0.75 04/17/81 Improve and correct buffer handling
+; 0.76 04/23/81 Correct directory size when not 2^N entries
+; 0.80 04/27/81 Add console input without echo, Functions 7 & 8
+; 1.00 04/28/81 Renumber for general release
+; 1.01 05/12/81 Fix bug in `STORE'
+; 1.10 07/21/81 Fatal error trapping, NUL device, hidden files, date & time,
+; RENAME fix, general cleanup
+; 1.11 09/03/81 Don't set CURRENT BLOCK to 0 on open; fix SET FILE SIZE
+; 1.12 10/09/81 Zero high half of CURRENT BLOCK after all (CP/M programs don't)
+; 1.13 10/29/81 Fix classic "no write-through" error in buffer handling
+; 1.20 12/31/81 Add time to FCB; separate FAT from DPT; Kill SMALLDIR;
+; Add FLUSH and MAPDEV calls; allow disk mapping in DSKCHG;
+; Lots of smaller improvements
+; 1.21 01/06/82 HIGHMEM switch to run DOS in high memory
+; 1.22 01/12/82 Add VERIFY system call to enable/disable verify after write
+; 1.23 02/11/82 Add defaulting to parser; use variable escape character
+; Don't zero extent field in IBM version (back to 1.01!)
+; 1.24 03/01/82 Restore fcn. 27 to 1.0 level; add fcn. 28
+; 1.25 03/03/82 Put marker (00) at end of directory to speed searches
+;
+; *************************************************************
+
+
+; Interrupt Entry Points:
+
+; INTBASE: ABORT
+; INTBASE+4: COMMAND
+; INTBASE+8: BASE EXIT ADDRESS
+; INTBASE+C: CONTROL-C ABORT
+; INTBASE+10H: FATAL ERROR ABORT
+; INTBASE+14H: BIOS DISK READ
+; INTBASE+18H: BIOS DISK WRITE
+; INTBASE+40H: Long jump to CALL entry point
+
+ IF IBM
+ESCCH EQU 0
+CANCEL EQU 1BH ;Cancel with ESC
+TOGLINS EQU TRUE ;One key toggles insert mode
+TOGLPRN EQU TRUE ;One key toggles printer echo
+NUMDEV EQU 6 ;Include "COM1" as I/O device name
+ZEROEXT EQU TRUE
+ ELSE
+ESCCH EQU 1BH
+CANCEL EQU "X"-"@" ;Cancel with Ctrl-X
+TOGLINS EQU FALSE ;Separate keys for insert mode on and off
+TOGLPRN EQU FALSE ;Separate keys for printer echo on and off
+NUMDEV EQU 5 ;Number of I/O device names
+ZEROEXT EQU FALSE
+ ENDIF
+
+MAXCALL EQU 36
+MAXCOM EQU 46
+INTBASE EQU 80H
+INTTAB EQU 20H
+ENTRYPOINTSEG EQU 0CH
+ENTRYPOINT EQU INTBASE+40H
+CONTC EQU INTTAB+3
+EXIT EQU INTBASE+8
+LONGJUMP EQU 0EAH
+LONGCALL EQU 9AH
+MAXDIF EQU 0FFFH
+SAVEXIT EQU 10
+
+; Field definition for FCBs
+
+FCBLOCK STRUC
+ DB 12 DUP (?) ;Drive code and name
+EXTENT DW ?
+RECSIZ DW ? ;Size of record (user settable)
+FILSIZ DW ? ;Size of file in bytes
+DRVBP DW ? ;BP for SEARCH FIRST and SEARCH NEXT
+FDATE DW ? ;Date of last writing
+FTIME DW ? ;Time of last writing
+DEVID DB ? ;Device ID number, bits 0-5
+ ;bit 7=0 for file, bit 7=1 for I/O device
+ ;If file, bit 6=0 if dirty
+ ;If I/O device, bit 6=0 if EOF (input)
+FIRCLUS DW ? ;First cluster of file
+LSTCLUS DW ? ;Last cluster accessed
+CLUSPOS DW ? ;Position of last cluster accessed
+ DB ? ;Forces NR to offset 32
+NR DB ? ;Next record
+RR DB 3 DUP (?) ;Random record
+FCBLOCK ENDS
+FILDIRENT = FILSIZ ;Used only by SEARCH FIRST and SEARCH NEXT
+
+; Description of 32-byte directory entry (same as returned by SEARCH FIRST
+; and SEARCH NEXT, functions 17 and 18).
+;
+; Location bytes Description
+;
+; 0 11 File name and extension ( 0E5H if empty)
+; 11 1 Attributes. Bits 1 or 2 make file hidden
+; 12 10 Zero field (for expansion)
+; 22 2 Time. Bits 0-4=seconds/2, bits 5-10=minute, 11-15=hour
+; 24 2 Date. Bits 0-4=day, bits 5-8=month, bits 9-15=year-1980
+; 26 2 First allocation unit ( < 4080 )
+; 28 4 File size, in bytes (LSB first, 30 bits max.)
+;
+; The File Allocation Table uses a 12-bit entry for each allocation unit on
+; the disk. These entries are packed, two for every three bytes. The contents
+; of entry number N is found by 1) multiplying N by 1.5; 2) adding the result
+; to the base address of the Allocation Table; 3) fetching the 16-bit word at
+; this address; 4) If N was odd (so that N*1.5 was not an integer), shift the
+; word right four bits; 5) mask to 12 bits (AND with 0FFF hex). Entry number
+; zero is used as an end-of-file trap in the OS and as a flag for directory
+; entry size (if SMALLDIR selected). Entry 1 is reserved for future use. The
+; first available allocation unit is assigned entry number two, and even
+; though it is the first, is called cluster 2. Entries greater than 0FF8H are
+; end of file marks; entries of zero are unallocated. Otherwise, the contents
+; of a FAT entry is the number of the next cluster in the file.
+
+
+; Field definition for Drive Parameter Block
+
+DPBLOCK STRUC
+DEVNUM DB ? ;I/O driver number
+DRVNUM DB ? ;Physical Unit number
+SECSIZ DW ? ;Size of physical sector in bytes
+CLUSMSK DB ? ;Sectors/cluster - 1
+CLUSSHFT DB ? ;Log2 of sectors/cluster
+FIRFAT DW ? ;Starting record of FATs
+FATCNT DB ? ;Number of FATs for this drive
+MAXENT DW ? ;Number of directory entries
+FIRREC DW ? ;First sector of first cluster
+MAXCLUS DW ? ;Number of clusters on drive + 1
+FATSIZ DB ? ;Number of records occupied by FAT
+FIRDIR DW ? ;Starting record of directory
+FAT DW ? ;Pointer to start of FAT
+DPBLOCK ENDS
+
+DPBSIZ EQU 20 ;Size of the structure in bytes
+DIRSEC = FIRREC ;Number of dir. sectors (init temporary)
+DSKSIZ = MAXCLUS ;Size of disk (temp used during init only)
+
+;The following are all of the segments used
+;They are declared in the order that they should be placed in the executable
+
+CODE SEGMENT
+CODE ENDS
+
+CONSTANTS SEGMENT BYTE
+CONSTANTS ENDS
+
+DATA SEGMENT WORD
+DATA ENDS
+
+DOSGROUP GROUP CODE,CONSTANTS,DATA
+
+SEGBIOS SEGMENT
+SEGBIOS ENDS
+
+
+; BOIS entry point definitions
+
+ IF IBM
+BIOSSEG EQU 60H
+ ENDIF
+ IF NOT IBM
+BIOSSEG EQU 40H
+ ENDIF
+
+SEGBIOS SEGMENT AT BIOSSEG
+ ORG 0
+ DB 3 DUP (?) ;Reserve room for jump to init code
+BIOSSTAT DB 3 DUP (?) ;Console input status check
+BIOSIN DB 3 DUP (?) ;Get console character
+BIOSOUT DB 3 DUP (?) ;Output console character
+BIOSPRINT DB 3 DUP (?) ;Output to printer
+BIOSAUXIN DB 3 DUP (?) ;Get byte from auxilliary
+BIOSAUXOUT DB 3 DUP (?) ;Output byte to auxilliary
+BIOSREAD DB 3 DUP (?) ;Disk read
+BIOSWRITE DB 3 DUP (?) ;Disk write
+BIOSDSKCHG DB 3 DUP (?) ;Dsik-change status
+BIOSSETDATE DB 3 DUP (?) ;Set date
+BIOSSETTIME DB 3 DUP (?) ;Set time
+BIOSGETTIME DB 3 DUP (?) ;Get time and date
+BIOSFLUSH DB 3 DUP (?) ;Clear console input buffer
+BIOSMAPDEV DB 3 DUP (?) ;Dynamic disk table mapper
+
+SEGBIOS ENDS
+; Location of user registers relative user stack pointer
+
+STKPTRS STRUC
+AXSAVE DW ?
+BXSAVE DW ?
+CXSAVE DW ?
+DXSAVE DW ?
+SISAVE DW ?
+DISAVE DW ?
+BPSAVE DW ?
+DSSAVE DW ?
+ESSAVE DW ?
+IPSAVE DW ?
+CSSAVE DW ?
+FSAVE DW ?
+STKPTRS ENDS
+
+; Start of code
+
+CODE SEGMENT
+ASSUME CS:DOSGROUP,DS:DOSGROUP,ES:DOSGROUP,SS:DOSGROUP
+
+ ORG 0
+CODSTRT EQU $
+ JMP DOSINIT
+
+ESCCHAR DB ESCCH ;Lead-in character for escape sequences
+ESCTAB:
+ IF NOT IBM
+ DB "S" ;Copy one char
+ DB "V" ;Skip one char
+ DB "T" ;Copy to char
+ DB "W" ;Skip to char
+ DB "U" ;Copy line
+ DB "E" ;Kill line (no change in template)
+ DB "J" ;Reedit line (new template)
+ DB "D" ;Backspace
+ DB "P" ;Enter insert mode
+ DB "Q" ;Exit insert mode
+ DB "R" ;Escape character
+ DB "R" ;End of table
+ ENDIF
+ IF IBM
+ DB 64 ;Crtl-Z - F6
+ DB 77 ;Copy one char - -->
+ DB 59 ;Copy one char - F1
+ DB 83 ;Skip one char - DEL
+ DB 60 ;Copy to char - F2
+ DB 62 ;Skip to char - F4
+ DB 61 ;Copy line - F3
+ DB 61 ;Kill line (no change to template ) - Not used
+ DB 63 ;Reedit line (new template) - F5
+ DB 75 ;Backspace - <--
+ DB 82 ;Enter insert mode - INS (toggle)
+ DB 65 ;Escape character - F7
+ DB 65 ;End of table
+ ENDIF
+
+ESCTABLEN EQU $-ESCTAB
+ IF NOT IBM
+HEADER DB 13,10,"MS-DOS version 1.25"
+ IF HIGHMEM
+ DB "H"
+ ENDIF
+ IF DSKTEST
+ DB "D"
+ ENDIF
+
+ DB 13,10
+ DB "Copyright 1981,82 Microsoft, Inc.",13,10,"$"
+ ENDIF
+
+QUIT:
+ MOV AH,0
+ JMP SHORT SAVREGS
+
+COMMAND: ;Interrupt call entry point
+ CMP AH,MAXCOM
+ JBE SAVREGS
+BADCALL:
+ MOV AL,0
+IRET: IRET
+
+ENTRY: ;System call entry point and dispatcher
+ POP AX ;IP from the long call at 5
+ POP AX ;Segment from the long call at 5
+ POP CS:[TEMP] ;IP from the CALL 5
+ PUSHF ;Start re-ordering the stack
+ CLI
+ PUSH AX ;Save segment
+ PUSH CS:[TEMP] ;Stack now ordered as if INT had been used
+ CMP CL,MAXCALL ;This entry point doesn't get as many calls
+ JA BADCALL
+ MOV AH,CL
+SAVREGS:
+ PUSH ES
+ PUSH DS
+ PUSH BP
+ PUSH DI
+ PUSH SI
+ PUSH DX
+ PUSH CX
+ PUSH BX
+ PUSH AX
+
+ IF DSKTEST
+ MOV AX,CS:[SPSAVE]
+ MOV CS:[NSP],AX
+ MOV AX,CS:[SSSAVE]
+ MOV CS:[NSS],AX
+ POP AX
+ PUSH AX
+ ENDIF
+
+ MOV CS:[SPSAVE],SP
+ MOV CS:[SSSAVE],SS
+ MOV SP,CS
+ MOV SS,SP
+REDISP:
+ MOV SP,OFFSET DOSGROUP:IOSTACK
+ STI ;Stack OK now
+ MOV BL,AH
+ MOV BH,0
+ SHL BX,1
+ CLD
+ CMP AH,12
+ JLE SAMSTK
+ MOV SP,OFFSET DOSGROUP:DSKSTACK
+SAMSTK:
+ CALL CS:[BX+DISPATCH]
+LEAVE:
+ CLI
+ MOV SP,CS:[SPSAVE]
+ MOV SS,CS:[SSSAVE]
+ MOV BP,SP
+ MOV BYTE PTR [BP.AXSAVE],AL
+
+ IF DSKTEST
+ MOV AX,CS:[NSP]
+ MOV CS:[SPSAVE],AX
+ MOV AX,CS:[NSS]
+ MOV CS:[SSSAVE],AX
+ ENDIF
+
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ POP SI
+ POP DI
+ POP BP
+ POP DS
+ POP ES
+ IRET
+; Standard Functions
+DISPATCH DW ABORT ;0
+ DW CONIN
+ DW CONOUT
+ DW READER
+ DW PUNCH
+ DW LIST ;5
+ DW RAWIO
+ DW RAWINP
+ DW IN
+ DW PRTBUF
+ DW BUFIN ;10
+ DW CONSTAT
+ DW FLUSHKB
+ DW DSKRESET
+ DW SELDSK
+ DW OPEN ;15
+ DW CLOSE
+ DW SRCHFRST
+ DW SRCHNXT
+ DW DELETE
+ DW SEQRD ;20
+ DW SEQWRT
+ DW CREATE
+ DW RENAME
+ DW INUSE
+ DW GETDRV ;25
+ DW SETDMA
+ DW GETFATPT
+ DW GETFATPTDL
+ DW GETRDONLY
+ DW SETATTRIB ;30
+ DW GETDSKPT
+ DW USERCODE
+ DW RNDRD
+ DW RNDWRT
+ DW FILESIZE ;35
+ DW SETRNDREC
+; Extended Functions
+ DW SETVECT
+ DW NEWBASE
+ DW BLKRD
+ DW BLKWRT ;40
+ DW MAKEFCB
+ DW GETDATE
+ DW SETDATE
+ DW GETTIME
+ DW SETTIME ;45
+ DW VERIFY
+
+INUSE:
+GETIO:
+SETIO:
+GETRDONLY:
+SETATTRIB:
+USERCODE:
+ MOV AL,0
+ RET
+
+VERIFY:
+ AND AL,1
+ MOV CS:VERFLG,AL
+ RET
+
+FLUSHKB:
+ PUSH AX
+ CALL FAR PTR BIOSFLUSH
+ POP AX
+ MOV AH,AL
+ CMP AL,1
+ JZ REDISPJ
+ CMP AL,6
+ JZ REDISPJ
+ CMP AL,7
+ JZ REDISPJ
+ CMP AL,8
+ JZ REDISPJ
+ CMP AL,10
+ JZ REDISPJ
+ MOV AL,0
+ RET
+
+REDISPJ:JMP REDISP
+
+READER:
+AUXIN:
+ CALL STATCHK
+ CALL FAR PTR BIOSAUXIN
+ RET
+
+PUNCH:
+ MOV AL,DL
+AUXOUT:
+ PUSH AX
+ CALL STATCHK
+ POP AX
+ CALL FAR PTR BIOSAUXOUT
+ RET
+
+
+UNPACK:
+
+; Inputs:
+; DS = CS
+; BX = Cluster number
+; BP = Base of drive parameters
+; SI = Pointer to drive FAT
+; Outputs:
+; DI = Contents of FAT for given cluster
+; Zero set means DI=0 (free cluster)
+; No other registers affected. Fatal error if cluster too big.
+
+ CMP BX,[BP.MAXCLUS]
+ JA HURTFAT
+ LEA DI,[SI+BX]
+ SHR BX,1
+ MOV DI,[DI+BX]
+ JNC HAVCLUS
+ SHR DI,1
+ SHR DI,1
+ SHR DI,1
+ SHR DI,1
+ STC
+HAVCLUS:
+ RCL BX,1
+ AND DI,0FFFH
+ RET
+HURTFAT:
+ PUSH AX
+ MOV AH,80H ;Signal Bad FAT to INT 24H handler
+ MOV DI,0FFFH ;In case INT 24H returns (it shouldn't)
+ CALL FATAL
+ POP AX ;Try to ignore bad FAT
+ RET
+
+
+PACK:
+
+; Inputs:
+; DS = CS
+; BX = Cluster number
+; DX = Data
+; SI = Pointer to drive FAT
+; Outputs:
+; The data is stored in the FAT at the given cluster.
+; BX,DX,DI all destroyed
+; No other registers affected
+
+ MOV DI,BX
+ SHR BX,1
+ ADD BX,SI
+ ADD BX,DI
+ SHR DI,1
+ MOV DI,[BX]
+ JNC ALIGNED
+ SHL DX,1
+ SHL DX,1
+ SHL DX,1
+ SHL DX,1
+ AND DI,0FH
+ JMP SHORT PACKIN
+ALIGNED:
+ AND DI,0F000H
+PACKIN:
+ OR DI,DX
+ MOV [BX],DI
+ RET
+
+DEVNAME:
+ MOV SI,OFFSET DOSGROUP:IONAME ;List of I/O devices with file names
+ MOV BH,NUMDEV ;BH = number of device names
+LOOKIO:
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,4 ;All devices are 4 letters
+ REPE CMPSB ;Check for name in list
+ JZ IOCHK ;If first 3 letters OK, check for the rest
+ ADD SI,CX ;Point to next device name
+ DEC BH
+ JNZ LOOKIO
+CRET:
+ STC ;Not found
+ RET
+
+IOCHK:
+ IF IBM
+ CMP BH,NUMDEV ;Is it the first device?
+ JNZ NOTCOM1
+ MOV BH,2 ;Make it the same as AUX
+NOTCOM1:
+ ENDIF
+ NEG BH
+ MOV CX,2 ;Check rest of name but not extension
+ MOV AX,2020H
+ REPE SCASW ;Make sure rest of name is blanks
+ JNZ CRET
+RET1: RET ;Zero set so CREATE works
+
+GETFILE:
+; Same as GETNAME except ES:DI points to FCB on successful return
+ CALL MOVNAME
+ JC RET1
+ PUSH DX
+ PUSH DS
+ CALL FINDNAME
+ POP ES
+ POP DI
+RET2: RET
+
+
+GETNAME:
+
+; Inputs:
+; DS,DX point to FCB
+; Function:
+; Find file name in disk directory. First byte is
+; drive number (0=current disk). "?" matches any
+; character.
+; Outputs:
+; Carry set if file not found
+; ELSE
+; Zero set if attributes match (always except when creating)
+; BP = Base of drive parameters
+; DS = CS
+; ES = CS
+; BX = Pointer into directory buffer
+; SI = Pointer to First Cluster field in directory entry
+; [DIRBUF] has directory record with match
+; [NAME1] has file name
+; All other registers destroyed.
+
+ CALL MOVNAME
+ JC RET2 ;Bad file name?
+FINDNAME:
+ MOV AX,CS
+ MOV DS,AX
+ CALL DEVNAME
+ JNC RET2
+ CALL STARTSRCH
+CONTSRCH:
+ CALL GETENTRY
+ JC RET2
+SRCH:
+ MOV AH,BYTE PTR [BX]
+ OR AH,AH ;End of directory?
+ JZ FREE
+ CMP AH,[DELALL] ;Free entry?
+ JZ FREE
+ MOV SI,BX
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,11
+WILDCRD:
+ REPE CMPSB
+ JZ FOUND
+ CMP BYTE PTR [DI-1],"?"
+ JZ WILDCRD
+NEXTENT:
+ CALL NEXTENTRY
+ JNC SRCH
+RET3: RET
+
+FREE:
+ CMP [ENTFREE],-1 ;Found a free entry before?
+ JNZ TSTALL ;If so, ignore this one
+ MOV CX,[LASTENT]
+ MOV [ENTFREE],CX
+TSTALL:
+ CMP AH,[DELALL] ;At end of directory?
+ JZ NEXTENT ;No - continue search
+ STC ;Report not found
+ RET
+
+FOUND:
+;Check if attributes allow finding it
+ MOV AH,[ATTRIB] ;Attributes of search
+ NOT AH
+ AND AH,[SI] ;Compare with attributes of file
+ ADD SI,15
+ AND AH,6 ;Only look at bits 1 and 2
+ JZ RET3
+ TEST BYTE PTR [CREATING],-1 ;Pass back mismatch if creating
+ JZ NEXTENT ;Otherwise continue searching
+ RET
+
+
+GETENTRY:
+
+; Inputs:
+; [LASTENT] has previously searched directory entry
+; Function:
+; Locates next sequential directory entry in preparation for search
+; Outputs:
+; Carry set if none
+; ELSE
+; AL = Current directory block
+; BX = Pointer to next directory entry in [DIRBUF]
+; DX = Pointer to first byte after end of DIRBUF
+; [LASTENT] = New directory entry number
+
+ MOV AX,[LASTENT]
+ INC AX ;Start with next entry
+ CMP AX,[BP.MAXENT]
+ JAE NONE
+GETENT:
+ MOV [LASTENT],AX
+ MOV CL,4
+ SHL AX,CL
+ XOR DX,DX
+ SHL AX,1
+ RCL DX,1 ;Account for overflow in last shift
+ MOV BX,[BP.SECSIZ]
+ AND BL,255-31 ;Must be multiple of 32
+ DIV BX
+ MOV BX,DX ;Position within sector
+ MOV AH,[BP.DEVNUM] ;AL=Directory sector no.
+ CMP AX,[DIRBUFID]
+ JZ HAVDIRBUF
+ PUSH BX
+ CALL DIRREAD
+ POP BX
+HAVDIRBUF:
+ MOV DX,OFFSET DOSGROUP:DIRBUF
+ ADD BX,DX
+ ADD DX,[BP.SECSIZ]
+ RET
+
+NEXTENTRY:
+
+; Inputs:
+; Same as outputs of GETENTRY, above
+; Function:
+; Update AL, BX, and [LASTENT] for next directory entry.
+; Carry set if no more.
+
+ MOV DI,[LASTENT]
+ INC DI
+ CMP DI,[BP.MAXENT]
+ JAE NONE
+ MOV [LASTENT],DI
+ ADD BX,32
+ CMP BX,DX
+ JB HAVIT
+ INC AL ;Next directory sector
+ PUSH DX ;Save limit
+ CALL DIRREAD
+ POP DX
+ MOV BX,OFFSET DOSGROUP:DIRBUF
+HAVIT:
+ CLC
+ RET
+
+NONE:
+ CALL CHKDIRWRITE
+ STC
+RET4: RET
+
+
+DELETE: ; System call 19
+ CALL MOVNAME
+ MOV AL,-1
+ JC RET4
+ MOV AL,CS:[ATTRIB]
+ AND AL,6 ;Look only at hidden bits
+ CMP AL,6 ;Both must be set
+ JNZ NOTALL
+ MOV CX,11
+ MOV AL,"?"
+ MOV DI,OFFSET DOSGROUP:NAME1
+ REPE SCASB ;See if name is *.*
+ JNZ NOTALL
+ MOV BYTE PTR CS:[DELALL],0 ;DEL *.* - flag deleting all
+NOTALL:
+ CALL FINDNAME
+ MOV AL,-1
+ JC RET4
+ OR BH,BH ;Check if device name
+ JS RET4 ;Can't delete I/O devices
+DELFILE:
+ MOV BYTE PTR [DIRTYDIR],-1
+ MOV AH,[DELALL]
+ MOV BYTE PTR [BX],AH
+ MOV BX,[SI]
+ MOV SI,[BP.FAT]
+ OR BX,BX
+ JZ DELNXT
+ CMP BX,[BP.MAXCLUS]
+ JA DELNXT
+ CALL RELEASE
+DELNXT:
+ CALL CONTSRCH
+ JNC DELFILE
+ CALL FATWRT
+ CALL CHKDIRWRITE
+ XOR AL,AL
+ RET
+
+
+RENAME: ;System call 23
+ CALL MOVNAME
+ JC ERRET
+ ADD SI,5
+ MOV DI,OFFSET DOSGROUP:NAME2
+ CALL LODNAME
+ JC ERRET ;Report error if second name invalid
+ CALL FINDNAME
+ JC ERRET
+ OR BH,BH ;Check if I/O device name
+ JS ERRET ;If so, can't rename it
+ MOV SI,OFFSET DOSGROUP:NAME1
+ MOV DI,OFFSET DOSGROUP:NAME3
+ MOV CX,6 ;6 words (12 bytes)--include attribute byte
+ REP MOVSW ;Copy name to search for
+RENFIL:
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV SI,OFFSET DOSGROUP:NAME2
+ MOV CX,11
+NEWNAM:
+ LODSB
+ CMP AL,"?"
+ JNZ NOCHG
+ MOV AL,[BX]
+NOCHG:
+ STOSB
+ INC BX
+ LOOP NEWNAM
+ MOV BYTE PTR [DI],6 ;Stop duplicates with any attributes
+ CALL DEVNAME ;Check if giving it a device name
+ JNC RENERR
+ PUSH [LASTENT] ;Save position of match
+ MOV [LASTENT],-1 ;Search entire directory for duplicate
+ CALL CONTSRCH ;See if new name already exists
+ POP AX
+ JNC RENERR ;Error if found
+ CALL GETENT ;Re-read matching entry
+ MOV DI,BX
+ MOV SI,OFFSET DOSGROUP:NAME1
+ MOV CX,5
+ MOVSB
+ REP MOVSW ;Replace old name with new one
+ MOV BYTE PTR [DIRTYDIR],-1 ;Flag change in directory
+ MOV SI,OFFSET DOSGROUP:NAME3
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,6 ;Include attribute byte
+ REP MOVSW ;Copy name back into search buffer
+ CALL CONTSRCH
+ JNC RENFIL
+ CALL CHKDIRWRITE
+ XOR AL,AL
+ RET
+
+RENERR:
+ CALL CHKDIRWRITE
+ERRET:
+ MOV AL,-1
+RET5: RET
+
+
+MOVNAME:
+
+; Inputs:
+; DS, DX point to FCB or extended FCB
+; Outputs:
+; DS:DX point to normal FCB
+; ES = CS
+; If file name OK:
+; BP has base of driver parameters
+; [NAME1] has name in upper case
+; All registers except DX destroyed
+; Carry set if bad file name or drive
+
+ MOV CS:WORD PTR [CREATING],0E500H ;Not creating, not DEL *.*
+ MOV AX,CS
+ MOV ES,AX
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV SI,DX
+ LODSB
+ MOV CS:[EXTFCB],AL ;Set flag if extended FCB in use
+ MOV AH,0 ;Set default attributes
+ CMP AL,-1 ;Is it an extended FCB?
+ JNZ HAVATTRB
+ ADD DX,7 ;Adjust to point to normal FCB
+ ADD SI,6 ;Point to drive select byte
+ MOV AH,[SI-1] ;Get attribute byte
+ LODSB ;Get drive select byte
+HAVATTRB:
+ MOV CS:[ATTRIB],AH ;Save attributes
+ CALL GETTHISDRV
+LODNAME:
+; This entry point copies a file name from DS,SI
+; to ES,DI converting to upper case.
+ CMP BYTE PTR [SI]," " ;Don't allow blank as first letter
+ STC ;In case of error
+ JZ RET5
+ MOV CX,11
+MOVCHK:
+ CALL GETLET
+ JB RET5
+ JNZ STOLET ;Is it a delimiter?
+ CMP AL," " ;This is the only delimiter allowed
+ STC ;In case of error
+ JNZ RET5
+STOLET:
+ STOSB
+ LOOP MOVCHK
+ CLC ;Got through whole name - no error
+RET6: RET
+
+GETTHISDRV:
+ CMP CS:[NUMDRV],AL
+ JC RET6
+ DEC AL
+ JNS PHYDRV
+ MOV AL,CS:[CURDRV]
+PHYDRV:
+ MOV CS:[THISDRV],AL
+ RET
+
+
+OPEN: ;System call 15
+ CALL GETFILE
+DOOPEN:
+; Enter here to perform OPEN on file already found
+; in directory. DS=CS, BX points to directory
+; entry in DIRBUF, SI points to First Cluster field, and
+; ES:DI point to the FCB to be opened. This entry point
+; is used by CREATE.
+ JC ERRET
+ OR BH,BH ;Check if file is I/O device
+ JS OPENDEV ;Special handler if so
+ MOV AL,[THISDRV]
+ INC AX
+ STOSB
+ XOR AX,AX
+ IF ZEROEXT
+ ADD DI,11
+ STOSW ;Zero low byte of extent field if IBM only
+ ENDIF
+ IF NOT ZEROEXT
+ ADD DI,12 ;Point to high half of CURRENT BLOCK field
+ STOSB ;Set it to zero (CP/M programs set low byte)
+ ENDIF
+ MOV AL,128 ;Default record size
+ STOSW ;Set record size
+ LODSW ;Get starting cluster
+ MOV DX,AX ;Save it for the moment
+ MOVSW ;Transfer size to FCB
+ MOVSW
+ MOV AX,[SI-8] ;Get date
+ STOSW ;Save date in FCB
+ MOV AX,[SI-10] ;Get time
+ STOSW ;Save it in FCB
+ MOV AL,[BP.DEVNUM]
+ OR AL,40H
+ STOSB
+ MOV AX,DX ;Restore starting cluster
+ STOSW ; first cluster
+ STOSW ; last cluster accessed
+ XOR AX,AX
+ STOSW ; position of last cluster
+ RET
+
+
+OPENDEV:
+ ADD DI,13 ;point to 2nd half of extent field
+ XOR AX,AX
+ STOSB ;Set it to zero
+ MOV AL,128
+ STOSW ;Set record size to 128
+ XOR AX,AX
+ STOSW
+ STOSW ;Set current size to zero
+ CALL DATE16
+ STOSW ;Date is todays
+ XCHG AX,DX
+ STOSW ;Use current time
+ MOV AL,BH ;Get device number
+ STOSB
+ XOR AL,AL ;No error
+ RET
+FATERR:
+ XCHG AX,DI ;Put error code in DI
+ MOV AH,2 ;While trying to read FAT
+ MOV AL,[THISDRV] ;Tell which drive
+ CALL FATAL1
+ JMP SHORT FATREAD
+STARTSRCH:
+ MOV AX,-1
+ MOV [LASTENT],AX
+ MOV [ENTFREE],AX
+FATREAD:
+
+; Inputs:
+; DS = CS
+; Function:
+; If disk may have been changed, FAT is read in and buffers are
+; flagged invalid. If not, no action is taken.
+; Outputs:
+; BP = Base of drive parameters
+; Carry set if invalid drive returned by MAPDEV
+; All other registers destroyed
+
+ MOV AL,[THISDRV]
+ XOR AH,AH ;Set default response to zero & clear carry
+ CALL FAR PTR BIOSDSKCHG ;See what BIOS has to say
+ JC FATERR
+ CALL GETBP
+ MOV AL,[THISDRV] ;Use physical unit number
+ MOV SI,[BP.FAT]
+ OR AH,[SI-1] ;Dirty byte for FAT
+ JS NEWDSK ;If either say new disk, then it's so
+ JNZ MAPDRV
+ MOV AH,1
+ CMP AX,WORD PTR [BUFDRVNO] ;Does buffer have dirty sector of this drive?
+ JZ MAPDRV
+NEWDSK:
+ CMP AL,[BUFDRVNO] ;See if buffer is for this drive
+ JNZ BUFOK ;If not, don't touch it
+ MOV [BUFSECNO],0 ;Flag buffers invalid
+ MOV WORD PTR [BUFDRVNO],00FFH
+BUFOK:
+ MOV [DIRBUFID],-1
+ CALL FIGFAT
+NEXTFAT:
+ PUSH AX
+ CALL DSKREAD
+ POP AX
+ JC BADFAT
+ SUB AL,[BP.FATCNT]
+ JZ NEWFAT
+ CALL FATWRT
+NEWFAT:
+ MOV SI,[BP.FAT]
+ MOV AL,[BP.DEVNUM]
+ MOV AH,[SI] ;Get first byte of FAT
+ OR AH,0F8H ;Put in range
+ CALL FAR PTR BIOSMAPDEV
+ MOV AH,0
+ MOV [SI-2],AX ;Set device no. and reset dirty bit
+MAPDRV:
+ MOV AL,[SI-2] ;Get device number
+GETBP:
+ MOV BP,[DRVTAB] ;Just in case drive isn't valid
+ AND AL,3FH ;Mask out dirty bit
+ CMP AL,[NUMIO]
+ CMC
+ JC RET7
+ PUSH AX
+ MOV AH,DPBSIZ
+ MUL AH
+ ADD BP,AX
+ POP AX
+RET7: RET
+
+BADFAT:
+ MOV CX,DI
+ ADD DX,CX
+ DEC AL
+ JNZ NEXTFAT
+ CALL FIGFAT ;Reset registers
+ CALL DREAD ;Try first FAT once more
+ JMP SHORT NEWFAT
+
+OKRET1:
+ MOV AL,0
+ RET
+
+CLOSE: ;System call 16
+ MOV DI,DX
+ CMP BYTE PTR [DI],-1 ;Check for extended FCB
+ JNZ NORMFCB3
+ ADD DI,7
+NORMFCB3:
+ TEST BYTE PTR [DI.DEVID],0C0H ;Allow only dirty files
+ JNZ OKRET1 ;can't close if I/O device, or not writen
+ MOV AL,[DI] ;Get physical unit number
+ DEC AL ;Make zero = drive A
+ MOV AH,1 ;Look for dirty buffer
+ CMP AX,CS:WORD PTR [BUFDRVNO]
+ JNZ FNDDIR
+;Write back dirty buffer if on same drive
+ PUSH DX
+ PUSH DS
+ PUSH CS
+ POP DS
+ MOV BYTE PTR [DIRTYBUF],0
+ MOV BX,[BUFFER]
+ MOV CX,1
+ MOV DX,[BUFSECNO]
+ MOV BP,[BUFDRVBP]
+ CALL DWRITE
+ POP DS
+ POP DX
+FNDDIR:
+ CALL GETFILE
+BADCLOSEJ:
+ JC BADCLOSE
+ MOV CX,ES:[DI.FIRCLUS]
+ MOV [SI],CX
+ MOV DX,ES:WORD PTR [DI.FILSIZ]
+ MOV [SI+2],DX
+ MOV DX,ES:WORD PTR [DI.FILSIZ+2]
+ MOV [SI+4],DX
+ MOV DX,ES:[DI.FDATE]
+ MOV [SI-2],DX
+ MOV DX,ES:[DI.FTIME]
+ MOV [SI-4],DX
+ CALL DIRWRITE
+
+CHKFATWRT:
+; Do FATWRT only if FAT is dirty and uses same I/O driver
+ MOV SI,[BP.FAT]
+ MOV AL,[BP.DEVNUM]
+ MOV AH,1
+ CMP [SI-2],AX ;See if FAT dirty and uses same driver
+ JNZ OKRET
+
+FATWRT:
+
+; Inputs:
+; DS = CS
+; BP = Base of drive parameter table
+; Function:
+; Write the FAT back to disk and reset FAT
+; dirty bit.
+; Outputs:
+; AL = 0
+; BP unchanged
+; All other registers destroyed
+
+ CALL FIGFAT
+ MOV BYTE PTR [BX-1],0
+EACHFAT:
+ PUSH DX
+ PUSH CX
+ PUSH BX
+ PUSH AX
+ CALL DWRITE
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ ADD DX,CX
+ DEC AL
+ JNZ EACHFAT
+OKRET:
+ MOV AL,0
+ RET
+
+BADCLOSE:
+ MOV SI,[BP.FAT]
+ MOV BYTE PTR [SI-1],0
+ MOV AL,-1
+ RET
+
+
+FIGFAT:
+; Loads registers with values needed to read or
+; write a FAT.
+ MOV AL,[BP.FATCNT]
+ MOV BX,[BP.FAT]
+ MOV CL,[BP.FATSIZ] ;No. of records occupied by FAT
+ MOV CH,0
+ MOV DX,[BP.FIRFAT] ;Record number of start of FATs
+ RET
+
+
+DIRCOMP:
+; Prepare registers for directory read or write
+ CBW
+ ADD AX,[BP.FIRDIR]
+ MOV DX,AX
+ MOV BX,OFFSET DOSGROUP:DIRBUF
+ MOV CX,1
+ RET
+
+
+CREATE: ;System call 22
+ CALL MOVNAME
+ JC ERRET3
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,11
+ MOV AL,"?"
+ REPNE SCASB
+ JZ ERRET3
+ MOV CS:BYTE PTR [CREATING],-1
+ PUSH DX
+ PUSH DS
+ CALL FINDNAME
+ JNC EXISTENT
+ MOV AX,[ENTFREE] ;First free entry found in FINDNAME
+ CMP AX,-1
+ JZ ERRPOP
+ CALL GETENT ;Point at that free entry
+ JMP SHORT FREESPOT
+ERRPOP:
+ POP DS
+ POP DX
+ERRET3:
+ MOV AL,-1
+ RET
+
+EXISTENT:
+ JNZ ERRPOP ;Error if attributes don't match
+ OR BH,BH ;Check if file is I/O device
+ JS OPENJMP ;If so, no action
+ MOV CX,[SI] ;Get pointer to clusters
+ JCXZ FREESPOT
+ CMP CX,[BP.MAXCLUS]
+ JA FREESPOT
+ PUSH BX
+ MOV BX,CX
+ MOV SI,[BP.FAT]
+ CALL RELEASE ;Free any data already allocated
+ CALL FATWRT
+ POP BX
+FREESPOT:
+ MOV DI,BX
+ MOV SI,OFFSET DOSGROUP:NAME1
+ MOV CX,5
+ MOVSB
+ REP MOVSW
+ MOV AL,[ATTRIB]
+ STOSB
+ XOR AX,AX
+ MOV CL,5
+ REP STOSW
+ CALL DATE16
+ XCHG AX,DX
+ STOSW
+ XCHG AX,DX
+ STOSW
+ XOR AX,AX
+ PUSH DI
+ MOV CL,6
+SMALLENT:
+ REP STOSB
+ PUSH BX
+ CALL DIRWRITE
+ POP BX
+ POP SI
+OPENJMP:
+ CLC ;Clear carry so OPEN won't fail
+ POP ES
+ POP DI
+ JMP DOOPEN
+
+
+DIRREAD:
+
+; Inputs:
+; DS = CS
+; AL = Directory block number
+; BP = Base of drive parameters
+; Function:
+; Read the directory block into DIRBUF.
+; Outputs:
+; AX,BP unchanged
+; All other registers destroyed.
+
+ PUSH AX
+ CALL CHKDIRWRITE
+ POP AX
+ PUSH AX
+ MOV AH,[BP.DEVNUM]
+ MOV [DIRBUFID],AX
+ CALL DIRCOMP
+ CALL DREAD
+ POP AX
+RET8: RET
+
+
+DREAD:
+
+; Inputs:
+; BX,DS = Transfer address
+; CX = Number of sectors
+; DX = Absolute record number
+; BP = Base of drive parameters
+; Function:
+; Calls BIOS to perform disk read. If BIOS reports
+; errors, will call HARDERR for further action.
+; BP preserved. All other registers destroyed.
+
+ CALL DSKREAD
+ JNC RET8
+ MOV CS:BYTE PTR [READOP],0
+ CALL HARDERR
+ CMP AL,1 ;Check for retry
+ JZ DREAD
+ RET ;Ignore otherwise
+
+
+HARDERR:
+
+;Hard disk error handler. Entry conditions:
+; DS:BX = Original disk transfer address
+; DX = Original logical sector number
+; CX = Number of sectors to go (first one gave the error)
+; AX = Hardware error code
+; DI = Original sector transfer count
+; BP = Base of drive parameters
+; [READOP] = 0 for read, 1 for write
+
+ XCHG AX,DI ;Error code in DI, count in AX
+ SUB AX,CX ;Number of sectors successfully transferred
+ ADD DX,AX ;First sector number to retry
+ PUSH DX
+ MUL [BP.SECSIZ] ;Number of bytes transferred
+ POP DX
+ ADD BX,AX ;First address for retry
+ MOV AH,0 ;Flag disk section in error
+ CMP DX,[BP.FIRFAT] ;In reserved area?
+ JB ERRINT
+ INC AH ;Flag for FAT
+ CMP DX,[BP.FIRDIR] ;In FAT?
+ JB ERRINT
+ INC AH
+ CMP DX,[BP.FIRREC] ;In directory?
+ JB ERRINT
+ INC AH ;Must be in data area
+ERRINT:
+ SHL AH,1 ;Make room for read/write bit
+ OR AH,CS:[READOP]
+FATAL:
+ MOV AL,[BP.DRVNUM] ;Get drive number
+FATAL1:
+ PUSH BP ;The only thing we preserve
+ MOV CS:[CONTSTK],SP
+ CLI ;Prepare to play with stack
+ MOV SS,CS:[SSSAVE]
+ MOV SP,CS:[SPSAVE] ;User stack pointer restored
+ INT 24H ;Fatal error interrupt vector
+ MOV CS:[SPSAVE],SP
+ MOV CS:[SSSAVE],SS
+ MOV SP,CS
+ MOV SS,SP
+ MOV SP,CS:[CONTSTK]
+ STI
+ POP BP
+ CMP AL,2
+ JZ ERROR
+ RET
+
+DSKREAD:
+ MOV AL,[BP.DEVNUM]
+ PUSH BP
+ PUSH BX
+ PUSH CX
+ PUSH DX
+ CALL FAR PTR BIOSREAD
+ POP DX
+ POP DI
+ POP BX
+ POP BP
+RET9: RET
+
+
+CHKDIRWRITE:
+ TEST BYTE PTR [DIRTYDIR],-1
+ JZ RET9
+
+DIRWRITE:
+
+; Inputs:
+; DS = CS
+; AL = Directory block number
+; BP = Base of drive parameters
+; Function:
+; Write the directory block into DIRBUF.
+; Outputs:
+; BP unchanged
+; All other registers destroyed.
+
+ MOV BYTE PTR [DIRTYDIR],0
+ MOV AL,BYTE PTR [DIRBUFID]
+ CALL DIRCOMP
+
+
+DWRITE:
+
+; Inputs:
+; BX,DS = Transfer address
+; CX = Number of sectors
+; DX = Absolute record number
+; BP = Base of drive parameters
+; Function:
+; Calls BIOS to perform disk write. If BIOS reports
+; errors, will call HARDERR for further action.
+; BP preserved. All other registers destroyed.
+
+ MOV AL,[BP.DEVNUM]
+ MOV AH,CS:VERFLG
+ PUSH BP
+ PUSH BX
+ PUSH CX
+ PUSH DX
+ CALL FAR PTR BIOSWRITE
+ POP DX
+ POP DI
+ POP BX
+ POP BP
+ JNC RET9
+ MOV CS:BYTE PTR [READOP],1
+ CALL HARDERR
+ CMP AL,1 ;Check for retry
+ JZ DWRITE
+ RET
+
+
+ABORT:
+ LDS SI,CS:DWORD PTR [SPSAVE]
+ MOV DS,[SI.CSSAVE]
+ XOR AX,AX
+ MOV ES,AX
+ MOV SI,SAVEXIT
+ MOV DI,EXIT
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ERROR:
+ MOV AX,CS
+ MOV DS,AX
+ MOV ES,AX
+ CALL WRTFATS
+ XOR AX,AX
+ CLI
+ MOV SS,[SSSAVE]
+ MOV SP,[SPSAVE]
+ MOV DS,AX
+ MOV SI,EXIT
+ MOV DI,OFFSET DOSGROUP:EXITHOLD
+ MOVSW
+ MOVSW
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ POP SI
+ POP DI
+ POP BP
+ POP DS
+ POP ES
+ STI ;Stack OK now
+ JMP CS:DWORD PTR [EXITHOLD]
+
+
+SEQRD: ;System call 20
+ CALL GETREC
+ CALL LOAD
+ JMP SHORT FINSEQ
+
+SEQWRT: ;System call 21
+ CALL GETREC
+ CALL STORE
+FINSEQ:
+ JCXZ SETNREX
+ ADD AX,1
+ ADC DX,0
+ JMP SHORT SETNREX
+
+RNDRD: ;System call 33
+ CALL GETRRPOS1
+ CALL LOAD
+ JMP SHORT FINRND
+
+RNDWRT: ;System call 34
+ CALL GETRRPOS1
+ CALL STORE
+ JMP SHORT FINRND
+
+BLKRD: ;System call 39
+ CALL GETRRPOS
+ CALL LOAD
+ JMP SHORT FINBLK
+
+BLKWRT: ;System call 40
+ CALL GETRRPOS
+ CALL STORE
+FINBLK:
+ LDS SI,DWORD PTR [SPSAVE]
+ MOV [SI.CXSAVE],CX
+ JCXZ FINRND
+ ADD AX,1
+ ADC DX,0
+FINRND:
+ MOV ES:WORD PTR [DI.RR],AX
+ MOV ES:[DI.RR+2],DL
+ OR DH,DH
+ JZ SETNREX
+ MOV ES:[DI.RR+3],DH ;Save 4 byte of RECPOS only if significant
+SETNREX:
+ MOV CX,AX
+ AND AL,7FH
+ MOV ES:[DI.NR],AL
+ AND CL,80H
+ SHL CX,1
+ RCL DX,1
+ MOV AL,CH
+ MOV AH,DL
+ MOV ES:[DI.EXTENT],AX
+ MOV AL,CS:[DSKERR]
+ RET
+
+GETRRPOS1:
+ MOV CX,1
+GETRRPOS:
+ MOV DI,DX
+ CMP BYTE PTR [DI],-1
+ JNZ NORMFCB1
+ ADD DI,7
+NORMFCB1:
+ MOV AX,WORD PTR [DI.RR]
+ MOV DX,WORD PTR [DI.RR+2]
+ RET
+
+NOFILERR:
+ XOR CX,CX
+ MOV BYTE PTR [DSKERR],4
+ POP BX
+ RET
+
+SETUP:
+
+; Inputs:
+; DS:DI point to FCB
+; DX:AX = Record position in file of disk transfer
+; CX = Record count
+; Outputs:
+; DS = CS
+; ES:DI point to FCB
+; BL = DEVID from FCB
+; CX = No. of bytes to transfer
+; BP = Base of drive parameters
+; SI = FAT pointer
+; [RECCNT] = Record count
+; [RECPOS] = Record position in file
+; [FCB] = DI
+; [NEXTADD] = Displacement of disk transfer within segment
+; [SECPOS] = Position of first sector
+; [BYTPOS] = Byte position in file
+; [BYTSECPOS] = Byte position in first sector
+; [CLUSNUM] = First cluster
+; [SECCLUSPOS] = Sector within first cluster
+; [DSKERR] = 0 (no errors yet)
+; [TRANS] = 0 (No transfers yet)
+; [THISDRV] = Physical drive unit number
+; If SETUP detects no records will be transfered, it returns 1 level up
+; with CX = 0.
+
+ PUSH AX
+ MOV AL,[DI]
+ DEC AL
+ MOV CS:[THISDRV],AL
+ MOV AL,[DI.DEVID]
+ MOV SI,[DI.RECSIZ]
+ OR SI,SI
+ JNZ HAVRECSIZ
+ MOV SI,128
+ MOV [DI.RECSIZ],SI
+HAVRECSIZ:
+ PUSH DS
+ POP ES ;Set ES to DS
+ PUSH CS
+ POP DS ;Set DS to CS
+ OR AL,AL ;Is it a device?
+ JNS NOTDEVICE
+ MOV AL,0 ;Fake in drive 0 so we can get SP
+NOTDEVICE:
+ CALL GETBP
+ POP AX
+ JC NOFILERR
+ CMP SI,64 ;Check if highest byte of RECPOS is significant
+ JB SMALREC
+ MOV DH,0 ;Ignore MSB if record >= 64 bytes
+SMALREC:
+ MOV [RECCNT],CX
+ MOV WORD PTR [RECPOS],AX
+ MOV WORD PTR [RECPOS+2],DX
+ MOV [FCB],DI
+ MOV BX,[DMAADD]
+ MOV [NEXTADD],BX
+ MOV BYTE PTR [DSKERR],0
+ MOV BYTE PTR [TRANS],0
+ MOV BX,DX
+ MUL SI
+ MOV WORD PTR [BYTPOS],AX
+ PUSH DX
+ MOV AX,BX
+ MUL SI
+ POP BX
+ ADD AX,BX
+ ADC DX,0 ;Ripple carry
+ JNZ EOFERR
+ MOV WORD PTR [BYTPOS+2],AX
+ MOV DX,AX
+ MOV AX,WORD PTR [BYTPOS]
+ MOV BX,[BP.SECSIZ]
+ CMP DX,BX ;See if divide will overflow
+ JNC EOFERR
+ DIV BX
+ MOV [SECPOS],AX
+ MOV [BYTSECPOS],DX
+ MOV DX,AX
+ AND AL,[BP.CLUSMSK]
+ MOV [SECCLUSPOS],AL
+ MOV AX,CX ;Record count
+ MOV CL,[BP.CLUSSHFT]
+ SHR DX,CL
+ MOV [CLUSNUM],DX
+ MUL SI ;Multiply by bytes per record
+ MOV CX,AX
+ ADD AX,[DMAADD] ;See if it will fit in one segment
+ ADC DX,0
+ JZ OK ;Must be less than 64K
+ MOV AX,[DMAADD]
+ NEG AX ;Amount of room left in segment
+ JNZ PARTSEG ;All 64K available?
+ DEC AX ;If so, reduce by one
+PARTSEG:
+ XOR DX,DX
+ DIV SI ;How many records will fit?
+ MOV [RECCNT],AX
+ MUL SI ;Translate that back into bytes
+ MOV BYTE PTR [DSKERR],2 ;Flag that trimming took place
+ MOV CX,AX
+ JCXZ NOROOM
+OK:
+ MOV BL,ES:[DI.DEVID]
+ MOV SI,[BP.FAT]
+ RET
+
+EOFERR:
+ MOV BYTE PTR [DSKERR],1
+ XOR CX,CX
+NOROOM:
+ POP BX ;Kill return address
+ RET
+
+BREAKDOWN:
+
+;Inputs:
+; DS = CS
+; CX = Length of disk transfer in bytes
+; BP = Base of drive parameters
+; [BYTSECPOS] = Byte position witin first sector
+;Outputs:
+; [BYTCNT1] = Bytes to transfer in first sector
+; [SECCNT] = No. of whole sectors to transfer
+; [BYTCNT2] = Bytes to transfer in last sector
+;AX, BX, DX destroyed. No other registers affected.
+
+ MOV AX,[BYTSECPOS]
+ MOV BX,CX
+ OR AX,AX
+ JZ SAVFIR ;Partial first sector?
+ SUB AX,[BP.SECSIZ]
+ NEG AX ;Max number of bytes left in first sector
+ SUB BX,AX ;Subtract from total length
+ JAE SAVFIR
+ ADD AX,BX ;Don't use all of the rest of the sector
+ XOR BX,BX ;And no bytes are left
+SAVFIR:
+ MOV [BYTCNT1],AX
+ MOV AX,BX
+ XOR DX,DX
+ DIV [BP.SECSIZ] ;How many whole sectors?
+ MOV [SECCNT],AX
+ MOV [BYTCNT2],DX ;Bytes remaining for last sector
+RET10: RET
+
+
+FNDCLUS:
+
+; Inputs:
+; DS = CS
+; CX = No. of clusters to skip
+; BP = Base of drive parameters
+; SI = FAT pointer
+; ES:DI point to FCB
+; Outputs:
+; BX = Last cluster skipped to
+; CX = No. of clusters remaining (0 unless EOF)
+; DX = Position of last cluster
+; DI destroyed. No other registers affected.
+
+ MOV BX,ES:[DI.LSTCLUS]
+ MOV DX,ES:[DI.CLUSPOS]
+ OR BX,BX
+ JZ NOCLUS
+ SUB CX,DX
+ JNB FINDIT
+ ADD CX,DX
+ XOR DX,DX
+ MOV BX,ES:[DI.FIRCLUS]
+FINDIT:
+ JCXZ RET10
+SKPCLP:
+ CALL UNPACK
+ CMP DI,0FF8H
+ JAE RET10
+ XCHG BX,DI
+ INC DX
+ LOOP SKPCLP
+ RET
+NOCLUS:
+ INC CX
+ DEC DX
+ RET
+
+
+BUFSEC:
+; Inputs:
+; AL = 0 if buffer must be read, 1 if no pre-read needed
+; BP = Base of drive parameters
+; [CLUSNUM] = Physical cluster number
+; [SECCLUSPOS] = Sector position of transfer within cluster
+; [BYTCNT1] = Size of transfer
+; Function:
+; Insure specified sector is in buffer, flushing buffer before
+; read if necessary.
+; Outputs:
+; SI = Pointer to buffer
+; DI = Pointer to transfer address
+; CX = Number of bytes
+; [NEXTADD] updated
+; [TRANS] set to indicate a transfer will occur
+
+ MOV DX,[CLUSNUM]
+ MOV BL,[SECCLUSPOS]
+ CALL FIGREC
+ MOV [PREREAD],AL
+ CMP DX,[BUFSECNO]
+ JNZ GETSEC
+ MOV AL,[BUFDRVNO]
+ CMP AL,[THISDRV]
+ JZ FINBUF ;Already have it?
+GETSEC:
+ XOR AL,AL
+ XCHG [DIRTYBUF],AL ;Read dirty flag and reset it
+ OR AL,AL
+ JZ RDSEC
+ PUSH DX
+ PUSH BP
+ MOV BP,[BUFDRVBP]
+ MOV BX,[BUFFER]
+ MOV CX,1
+ MOV DX,[BUFSECNO]
+ CALL DWRITE
+ POP BP
+ POP DX
+RDSEC:
+ TEST BYTE PTR [PREREAD],-1
+ JNZ SETBUF
+ XOR AX,AX
+ MOV [BUFSECNO],AX ;Set buffer valid in case of disk error
+ DEC AX
+ MOV [BUFDRVNO],AL
+ MOV BX,[BUFFER]
+ MOV CX,1
+ PUSH DX
+ CALL DREAD
+ POP DX
+SETBUF:
+ MOV [BUFSECNO],DX
+ MOV AL,[THISDRV]
+ MOV [BUFDRVNO],AL
+ MOV [BUFDRVBP],BP
+FINBUF:
+ MOV BYTE PTR [TRANS],1 ;A transfer is taking place
+ MOV DI,[NEXTADD]
+ MOV SI,DI
+ MOV CX,[BYTCNT1]
+ ADD SI,CX
+ MOV [NEXTADD],SI
+ MOV SI,[BUFFER]
+ ADD SI,[BYTSECPOS]
+ RET
+
+BUFRD:
+ XOR AL,AL ;Pre-read necessary
+ CALL BUFSEC
+ PUSH ES
+ MOV ES,[DMAADD+2]
+ SHR CX,1
+ JNC EVENRD
+ MOVSB
+EVENRD:
+ REP MOVSW
+ POP ES
+ RET
+
+BUFWRT:
+ MOV AX,[SECPOS]
+ INC AX ;Set for next sector
+ MOV [SECPOS],AX
+ CMP AX,[VALSEC] ;Has sector been written before?
+ MOV AL,1
+ JA NOREAD ;Skip preread if SECPOS>VALSEC
+ MOV AL,0
+NOREAD:
+ CALL BUFSEC
+ XCHG DI,SI
+ PUSH DS
+ PUSH ES
+ PUSH CS
+ POP ES
+ MOV DS,[DMAADD+2]
+ SHR CX,1
+ JNC EVENWRT
+ MOVSB
+EVENWRT:
+ REP MOVSW
+ POP ES
+ POP DS
+ MOV BYTE PTR [DIRTYBUF],1
+ RET
+
+NEXTSEC:
+ TEST BYTE PTR [TRANS],-1
+ JZ CLRET
+ MOV AL,[SECCLUSPOS]
+ INC AL
+ CMP AL,[BP.CLUSMSK]
+ JBE SAVPOS
+ MOV BX,[CLUSNUM]
+ CMP BX,0FF8H
+ JAE NONEXT
+ MOV SI,[BP.FAT]
+ CALL UNPACK
+ MOV [CLUSNUM],DI
+ INC [LASTPOS]
+ MOV AL,0
+SAVPOS:
+ MOV [SECCLUSPOS],AL
+CLRET:
+ CLC
+ RET
+NONEXT:
+ STC
+ RET
+
+TRANBUF:
+ LODSB
+ STOSB
+ CMP AL,13 ;Check for carriage return
+ JNZ NORMCH
+ MOV BYTE PTR [SI],10
+NORMCH:
+ CMP AL,10
+ LOOPNZ TRANBUF
+ JNZ ENDRDCON
+ CALL OUT ;Transmit linefeed
+ XOR SI,SI
+ OR CX,CX
+ JNZ GETBUF
+ OR AL,1 ;Clear zero flag--not end of file
+ENDRDCON:
+ MOV [CONTPOS],SI
+ENDRDDEV:
+ MOV [NEXTADD],DI
+ POP ES
+ JNZ SETFCBJ ;Zero set if Ctrl-Z found in input
+ MOV DI,[FCB]
+ AND ES:BYTE PTR [DI.DEVID],0FFH-40H ;Mark as no more data available
+SETFCBJ:
+ JMP SETFCB
+
+READDEV:
+ PUSH ES
+ LES DI,DWORD PTR [DMAADD]
+ INC BL
+ JZ READCON
+ INC BL
+ JNZ ENDRDDEV
+READAUX:
+ CALL AUXIN
+ STOSB
+ CMP AL,1AH
+ LOOPNZ READAUX
+ JMP SHORT ENDRDDEV
+
+READCON:
+ PUSH CS
+ POP DS
+ MOV SI,[CONTPOS]
+ OR SI,SI
+ JNZ TRANBUF
+ CMP BYTE PTR [CONBUF],128
+ JZ GETBUF
+ MOV WORD PTR [CONBUF],0FF80H ;Set up 128-byte buffer with no template
+GETBUF:
+ PUSH CX
+ PUSH ES
+ PUSH DI
+ MOV DX,OFFSET DOSGROUP:CONBUF
+ CALL BUFIN ;Get input buffer
+ POP DI
+ POP ES
+ POP CX
+ MOV SI,2 + OFFSET DOSGROUP:CONBUF
+ CMP BYTE PTR [SI],1AH ;Check for Ctrl-Z in first character
+ JNZ TRANBUF
+ MOV AL,1AH
+ STOSB
+ MOV AL,10
+ CALL OUT ;Send linefeed
+ XOR SI,SI
+ JMP SHORT ENDRDCON
+
+RDERR:
+ XOR CX,CX
+ JMP WRTERR
+
+RDLASTJ:JMP RDLAST
+
+LOAD:
+
+; Inputs:
+; DS:DI point to FCB
+; DX:AX = Position in file to read
+; CX = No. of records to read
+; Outputs:
+; DX:AX = Position of last record read
+; CX = No. of bytes read
+; ES:DI point to FCB
+; LSTCLUS, CLUSPOS fields in FCB set
+
+ CALL SETUP
+ OR BL,BL ;Check for named device I/O
+ JS READDEV
+ MOV AX,ES:WORD PTR [DI.FILSIZ]
+ MOV BX,ES:WORD PTR [DI.FILSIZ+2]
+ SUB AX,WORD PTR [BYTPOS]
+ SBB BX,WORD PTR [BYTPOS+2]
+ JB RDERR
+ JNZ ENUF
+ OR AX,AX
+ JZ RDERR
+ CMP AX,CX
+ JAE ENUF
+ MOV CX,AX
+ENUF:
+ CALL BREAKDOWN
+ MOV CX,[CLUSNUM]
+ CALL FNDCLUS
+ OR CX,CX
+ JNZ RDERR
+ MOV [LASTPOS],DX
+ MOV [CLUSNUM],BX
+ CMP [BYTCNT1],0
+ JZ RDMID
+ CALL BUFRD
+RDMID:
+ CMP [SECCNT],0
+ JZ RDLASTJ
+ CALL NEXTSEC
+ JC SETFCB
+ MOV BYTE PTR [TRANS],1 ;A transfer is taking place
+ONSEC:
+ MOV DL,[SECCLUSPOS]
+ MOV CX,[SECCNT]
+ MOV BX,[CLUSNUM]
+RDLP:
+ CALL OPTIMIZE
+ PUSH DI
+ PUSH AX
+ PUSH DS
+ MOV DS,[DMAADD+2]
+ PUSH DX
+ PUSH BX
+ PUSHF ;Save carry flag
+ CALL DREAD
+ POPF ;Restore carry flag
+ POP DI ;Initial transfer address
+ POP AX ;First sector transfered
+ POP DS
+ JC NOTBUFFED ;Was one of those sectors in the buffer?
+ CMP BYTE PTR [DIRTYBUF],0 ;Is buffer dirty?
+ JZ NOTBUFFED ;If not no problem
+;We have transfered in a sector from disk when a dirty copy of it is in the buffer.
+;We must transfer the sector from the buffer to correct memory address
+ SUB AX,[BUFSECNO] ;How many sectors into the transfer?
+ NEG AX
+ MOV CX,[BP.SECSIZ]
+ MUL CX ;How many bytes into the transfer?
+ ADD DI,AX
+ MOV SI,[BUFFER]
+ PUSH ES
+ MOV ES,[DMAADD+2] ;Get disk transfer segment
+ SHR CX,1
+ REP MOVSW
+ JNC EVENMOV
+ MOVSB
+EVENMOV:
+ POP ES
+NOTBUFFED:
+ POP CX
+ POP BX
+ JCXZ RDLAST
+ CMP BX,0FF8H
+ JAE SETFCB
+ MOV DL,0
+ INC [LASTPOS] ;We'll be using next cluster
+ JMP SHORT RDLP
+
+SETFCB:
+ MOV SI,[FCB]
+ MOV AX,[NEXTADD]
+ MOV DI,AX
+ SUB AX,[DMAADD] ;Number of bytes transfered
+ XOR DX,DX
+ MOV CX,ES:[SI.RECSIZ]
+ DIV CX ;Number of records
+ CMP AX,[RECCNT] ;Check if all records transferred
+ JZ FULLREC
+ MOV BYTE PTR [DSKERR],1
+ OR DX,DX
+ JZ FULLREC ;If remainder 0, then full record transfered
+ MOV BYTE PTR [DSKERR],3 ;Flag partial last record
+ SUB CX,DX ;Bytes left in last record
+ PUSH ES
+ MOV ES,[DMAADD+2]
+ XCHG AX,BX ;Save the record count temporarily
+ XOR AX,AX ;Fill with zeros
+ SHR CX,1
+ JNC EVENFIL
+ STOSB
+EVENFIL:
+ REP STOSW
+ XCHG AX,BX ;Restore record count to AX
+ POP ES
+ INC AX ;Add last (partial) record to total
+FULLREC:
+ MOV CX,AX
+ MOV DI,SI ;ES:DI point to FCB
+SETCLUS:
+ MOV AX,[CLUSNUM]
+ MOV ES:[DI.LSTCLUS],AX
+ MOV AX,[LASTPOS]
+ MOV ES:[DI.CLUSPOS],AX
+ADDREC:
+ MOV AX,WORD PTR [RECPOS]
+ MOV DX,WORD PTR [RECPOS+2]
+ JCXZ RET28 ;If no records read, don't change position
+ DEC CX
+ ADD AX,CX ;Update current record position
+ ADC DX,0
+ INC CX
+RET28: RET
+
+RDLAST:
+ MOV AX,[BYTCNT2]
+ OR AX,AX
+ JZ SETFCB
+ MOV [BYTCNT1],AX
+ CALL NEXTSEC
+ JC SETFCB
+ MOV [BYTSECPOS],0
+ CALL BUFRD
+ JMP SHORT SETFCB
+
+WRTDEV:
+ PUSH DS
+ LDS SI,DWORD PTR [DMAADD]
+ OR BL,40H
+ INC BL
+ JZ WRTCON
+ INC BL
+ JZ WRTAUX
+ INC BL
+ JZ ENDWRDEV ;Done if device is NUL
+WRTLST:
+ LODSB
+ CMP AL,1AH
+ JZ ENDWRDEV
+ CALL LISTOUT
+ LOOP WRTLST
+ JMP SHORT ENDWRDEV
+
+WRTAUX:
+ LODSB
+ CALL AUXOUT
+ CMP AL,1AH
+ LOOPNZ WRTAUX
+ JMP SHORT ENDWRDEV
+
+WRTCON:
+ LODSB
+ CMP AL,1AH
+ JZ ENDWRDEV
+ CALL OUT
+ LOOP WRTCON
+ENDWRDEV:
+ POP DS
+ MOV CX,[RECCNT]
+ MOV DI,[FCB]
+ JMP SHORT ADDREC
+
+HAVSTART:
+ MOV CX,AX
+ CALL SKPCLP
+ JCXZ DOWRTJ
+ CALL ALLOCATE
+ JNC DOWRTJ
+WRTERR:
+ MOV BYTE PTR [DSKERR],1
+LVDSK:
+ MOV AX,WORD PTR [RECPOS]
+ MOV DX,WORD PTR [RECPOS+2]
+ MOV DI,[FCB]
+ RET
+
+DOWRTJ: JMP DOWRT
+
+WRTEOFJ:
+ JMP WRTEOF
+
+STORE:
+
+; Inputs:
+; DS:DI point to FCB
+; DX:AX = Position in file of disk transfer
+; CX = Record count
+; Outputs:
+; DX:AX = Position of last record written
+; CX = No. of records written
+; ES:DI point to FCB
+; LSTCLUS, CLUSPOS fields in FCB set
+
+ CALL SETUP
+ CALL DATE16
+ MOV ES:[DI.FDATE],AX
+ MOV ES:[DI.FTIME],DX
+ OR BL,BL
+ JS WRTDEV
+ AND BL,3FH ;Mark file as dirty
+ MOV ES:[DI.DEVID],BL
+ CALL BREAKDOWN
+ MOV AX,WORD PTR [BYTPOS]
+ MOV DX,WORD PTR [BYTPOS+2]
+ JCXZ WRTEOFJ
+ DEC CX
+ ADD AX,CX
+ ADC DX,0 ;AX:DX=last byte accessed
+ DIV [BP.SECSIZ] ;AX=last sector accessed
+ MOV CL,[BP.CLUSSHFT]
+ SHR AX,CL ;Last cluster to be accessed
+ PUSH AX
+ MOV AX,ES:WORD PTR [DI.FILSIZ]
+ MOV DX,ES:WORD PTR [DI.FILSIZ+2]
+ DIV [BP.SECSIZ]
+ OR DX,DX
+ JZ NORNDUP
+ INC AX ;Round up if any remainder
+NORNDUP:
+ MOV [VALSEC],AX ;Number of sectors that have been written
+ POP AX
+ MOV CX,[CLUSNUM] ;First cluster accessed
+ CALL FNDCLUS
+ MOV [CLUSNUM],BX
+ MOV [LASTPOS],DX
+ SUB AX,DX ;Last cluster minus current cluster
+ JZ DOWRT ;If we have last clus, we must have first
+ JCXZ HAVSTART ;See if no more data
+ PUSH CX ;No. of clusters short of first
+ MOV CX,AX
+ CALL ALLOCATE
+ POP AX
+ JC WRTERR
+ MOV CX,AX
+ MOV DX,[LASTPOS]
+ INC DX
+ DEC CX
+ JZ NOSKIP
+ CALL SKPCLP
+NOSKIP:
+ MOV [CLUSNUM],BX
+ MOV [LASTPOS],DX
+DOWRT:
+ CMP [BYTCNT1],0
+ JZ WRTMID
+ MOV BX,[CLUSNUM]
+ CALL BUFWRT
+WRTMID:
+ MOV AX,[SECCNT]
+ OR AX,AX
+ JZ WRTLAST
+ ADD [SECPOS],AX
+ CALL NEXTSEC
+ MOV BYTE PTR [TRANS],1 ;A transfer is taking place
+ MOV DL,[SECCLUSPOS]
+ MOV BX,[CLUSNUM]
+ MOV CX,[SECCNT]
+WRTLP:
+ CALL OPTIMIZE
+ JC NOTINBUF ;Is one of the sectors buffered?
+ MOV [BUFSECNO],0 ;If so, invalidate the buffer since we're
+ MOV WORD PTR [BUFDRVNO],0FFH ; completely rewritting it
+NOTINBUF:
+ PUSH DI
+ PUSH AX
+ PUSH DS
+ MOV DS,[DMAADD+2]
+ CALL DWRITE
+ POP DS
+ POP CX
+ POP BX
+ JCXZ WRTLAST
+ MOV DL,0
+ INC [LASTPOS] ;We'll be using next cluster
+ JMP SHORT WRTLP
+WRTLAST:
+ MOV AX,[BYTCNT2]
+ OR AX,AX
+ JZ FINWRT
+ MOV [BYTCNT1],AX
+ CALL NEXTSEC
+ MOV [BYTSECPOS],0
+ CALL BUFWRT
+FINWRT:
+ MOV AX,[NEXTADD]
+ SUB AX,[DMAADD]
+ ADD AX,WORD PTR [BYTPOS]
+ MOV DX,WORD PTR [BYTPOS+2]
+ ADC DX,0
+ MOV CX,DX
+ MOV DI,[FCB]
+ CMP AX,ES:WORD PTR [DI.FILSIZ]
+ SBB CX,ES:WORD PTR [DI.FILSIZ+2]
+ JB SAMSIZ
+ MOV ES:WORD PTR [DI.FILSIZ],AX
+ MOV ES:WORD PTR [DI.FILSIZ+2],DX
+SAMSIZ:
+ MOV CX,[RECCNT]
+ JMP SETCLUS
+
+
+WRTERRJ:JMP WRTERR
+
+WRTEOF:
+ MOV CX,AX
+ OR CX,DX
+ JZ KILLFIL
+ SUB AX,1
+ SBB DX,0
+ DIV [BP.SECSIZ]
+ MOV CL,[BP.CLUSSHFT]
+ SHR AX,CL
+ MOV CX,AX
+ CALL FNDCLUS
+ JCXZ RELFILE
+ CALL ALLOCATE
+ JC WRTERRJ
+UPDATE:
+ MOV DI,[FCB]
+ MOV AX,WORD PTR [BYTPOS]
+ MOV ES:WORD PTR [DI.FILSIZ],AX
+ MOV AX,WORD PTR [BYTPOS+2]
+ MOV ES:WORD PTR [DI.FILSIZ+2],AX
+ XOR CX,CX
+ JMP ADDREC
+
+RELFILE:
+ MOV DX,0FFFH
+ CALL RELBLKS
+SETDIRT:
+ MOV BYTE PTR [SI-1],1
+ JMP SHORT UPDATE
+
+KILLFIL:
+ XOR BX,BX
+ XCHG BX,ES:[DI.FIRCLUS]
+ OR BX,BX
+ JZ UPDATE
+ CALL RELEASE
+ JMP SHORT SETDIRT
+
+
+OPTIMIZE:
+
+; Inputs:
+; DS = CS
+; BX = Physical cluster
+; CX = No. of records
+; DL = sector within cluster
+; BP = Base of drives parameters
+; [NEXTADD] = transfer address
+; Outputs:
+; AX = No. of records remaining
+; BX = Transfer address
+; CX = No. or records to be transferred
+; DX = Physical sector address
+; DI = Next cluster
+; Carry clear if a sector to transfer is in the buffer
+; Carry set otherwise
+; [CLUSNUM] = Last cluster accessed
+; [NEXTADD] updated
+; BP unchanged. Note that segment of transfer not set.
+
+ PUSH DX
+ PUSH BX
+ MOV AL,[BP.CLUSMSK]
+ INC AL ;Number of sectors per cluster
+ MOV AH,AL
+ SUB AL,DL ;AL = Number of sectors left in first cluster
+ MOV DX,CX
+ MOV SI,[BP.FAT]
+ MOV CX,0
+OPTCLUS:
+;AL has number of sectors available in current cluster
+;AH has number of sectors available in next cluster
+;BX has current physical cluster
+;CX has number of sequential sectors found so far
+;DX has number of sectors left to transfer
+;SI has FAT pointer
+ CALL UNPACK
+ ADD CL,AL
+ ADC CH,0
+ CMP CX,DX
+ JAE BLKDON
+ MOV AL,AH
+ INC BX
+ CMP DI,BX
+ JZ OPTCLUS
+ DEC BX
+FINCLUS:
+ MOV [CLUSNUM],BX ;Last cluster accessed
+ SUB DX,CX ;Number of sectors still needed
+ PUSH DX
+ MOV AX,CX
+ MUL [BP.SECSIZ] ;Number of sectors times sector size
+ MOV SI,[NEXTADD]
+ ADD AX,SI ;Adjust by size of transfer
+ MOV [NEXTADD],AX
+ POP AX ;Number of sectors still needed
+ POP DX ;Starting cluster
+ SUB BX,DX ;Number of new clusters accessed
+ ADD [LASTPOS],BX
+ POP BX ;BL = sector postion within cluster
+ CALL FIGREC
+ MOV BX,SI
+;Now let's see if any of these sectors are already in the buffer
+ CMP [BUFSECNO],DX
+ JC RET100 ;If DX > [BUFSECNO] then not in buffer
+ MOV SI,DX
+ ADD SI,CX ;Last sector + 1
+ CMP [BUFSECNO],SI
+ CMC
+ JC RET100 ;If SI <= [BUFSECNO] then not in buffer
+ PUSH AX
+ MOV AL,[BP.DEVNUM]
+ CMP AL,[BUFDRVNO] ;Is buffer for this drive?
+ POP AX
+ JZ RET100 ;If so, then we match
+ STC ;No match
+RET100: RET
+BLKDON:
+ SUB CX,DX ;Number of sectors in cluster we don't want
+ SUB AH,CL ;Number of sectors in cluster we accepted
+ DEC AH ;Adjust to mean position within cluster
+ MOV [SECCLUSPOS],AH
+ MOV CX,DX ;Anyway, make the total equal to the request
+ JMP SHORT FINCLUS
+
+
+FIGREC:
+
+;Inputs:
+; DX = Physical cluster number
+; BL = Sector postion within cluster
+; BP = Base of drive parameters
+;Outputs:
+; DX = physical sector number
+;No other registers affected.
+
+ PUSH CX
+ MOV CL,[BP.CLUSSHFT]
+ DEC DX
+ DEC DX
+ SHL DX,CL
+ OR DL,BL
+ ADD DX,[BP.FIRREC]
+ POP CX
+ RET
+
+GETREC:
+
+; Inputs:
+; DS:DX point to FCB
+; Outputs:
+; CX = 1
+; DX:AX = Record number determined by EXTENT and NR fields
+; DS:DI point to FCB
+; No other registers affected.
+
+ MOV DI,DX
+ CMP BYTE PTR [DI],-1 ;Check for extended FCB
+ JNZ NORMFCB2
+ ADD DI,7
+NORMFCB2:
+ MOV CX,1
+ MOV AL,[DI.NR]
+ MOV DX,[DI.EXTENT]
+ SHL AL,1
+ SHR DX,1
+ RCR AL,1
+ MOV AH,DL
+ MOV DL,DH
+ MOV DH,0
+ RET
+
+
+ALLOCATE:
+
+; Inputs:
+; DS = CS
+; ES = Segment of FCB
+; BX = Last cluster of file (0 if null file)
+; CX = No. of clusters to allocate
+; DX = Position of cluster BX
+; BP = Base of drive parameters
+; SI = FAT pointer
+; [FCB] = Displacement of FCB within segment
+; Outputs:
+; IF insufficient space
+; THEN
+; Carry set
+; CX = max. no. of records that could be added to file
+; ELSE
+; Carry clear
+; BX = First cluster allocated
+; FAT is fully updated including dirty bit
+; FIRCLUS field of FCB set if file was null
+; SI,BP unchanged. All other registers destroyed.
+
+ PUSH [SI]
+ PUSH DX
+ PUSH CX
+ PUSH BX
+ MOV AX,BX
+ALLOC:
+ MOV DX,BX
+FINDFRE:
+ INC BX
+ CMP BX,[BP.MAXCLUS]
+ JLE TRYOUT
+ CMP AX,1
+ JG TRYIN
+ POP BX
+ MOV DX,0FFFH
+ CALL RELBLKS
+ POP AX ;No. of clusters requested
+ SUB AX,CX ;AX=No. of clusters allocated
+ POP DX
+ POP [SI]
+ INC DX ;Position of first cluster allocated
+ ADD AX,DX ;AX=max no. of cluster in file
+ MOV DL,[BP.CLUSMSK]
+ MOV DH,0
+ INC DX ;DX=records/cluster
+ MUL DX ;AX=max no. of records in file
+ MOV CX,AX
+ SUB CX,WORD PTR [RECPOS] ;CX=max no. of records that could be written
+ JA MAXREC
+ XOR CX,CX ;If CX was negative, zero it
+MAXREC:
+ STC
+RET11: RET
+
+TRYOUT:
+ CALL UNPACK
+ JZ HAVFRE
+TRYIN:
+ DEC AX
+ JLE FINDFRE
+ XCHG AX,BX
+ CALL UNPACK
+ JZ HAVFRE
+ XCHG AX,BX
+ JMP SHORT FINDFRE
+HAVFRE:
+ XCHG BX,DX
+ MOV AX,DX
+ CALL PACK
+ MOV BX,AX
+ LOOP ALLOC
+ MOV DX,0FFFH
+ CALL PACK
+ MOV BYTE PTR [SI-1],1
+ POP BX
+ POP CX ;Don't need this stuff since we're successful
+ POP DX
+ CALL UNPACK
+ POP [SI]
+ XCHG BX,DI
+ OR DI,DI
+ JNZ RET11
+ MOV DI,[FCB]
+ MOV ES:[DI.FIRCLUS],BX
+RET12: RET
+
+
+RELEASE:
+
+; Inputs:
+; DS = CS
+; BX = Cluster in file
+; SI = FAT pointer
+; BP = Base of drive parameters
+; Function:
+; Frees cluster chain starting with [BX]
+; AX,BX,DX,DI all destroyed. Other registers unchanged.
+
+ XOR DX,DX
+RELBLKS:
+; Enter here with DX=0FFFH to put an end-of-file mark
+; in the first cluster and free the rest in the chain.
+ CALL UNPACK
+ JZ RET12
+ MOV AX,DI
+ CALL PACK
+ CMP AX,0FF8H
+ MOV BX,AX
+ JB RELEASE
+RET13: RET
+
+
+GETEOF:
+
+; Inputs:
+; BX = Cluster in a file
+; SI = Base of drive FAT
+; DS = CS
+; Outputs:
+; BX = Last cluster in the file
+; DI destroyed. No other registers affected.
+
+ CALL UNPACK
+ CMP DI,0FF8H
+ JAE RET13
+ MOV BX,DI
+ JMP SHORT GETEOF
+
+
+SRCHFRST: ;System call 17
+ CALL GETFILE
+SAVPLCE:
+; Search-for-next enters here to save place and report
+; findings.
+ JC KILLSRCH
+ OR BH,BH
+ JS SRCHDEV
+ MOV AX,[LASTENT]
+ MOV ES:[DI.FILDIRENT],AX
+ MOV ES:[DI.DRVBP],BP
+;Information in directory entry must be copied into the first
+; 33 bytes starting at the disk transfer address.
+ MOV SI,BX
+ LES DI,DWORD PTR [DMAADD]
+ MOV AX,00FFH
+ CMP AL,[EXTFCB]
+ JNZ NORMFCB
+ STOSW
+ INC AL
+ STOSW
+ STOSW
+ MOV AL,[ATTRIB]
+ STOSB
+NORMFCB:
+ MOV AL,[THISDRV]
+ INC AL
+ STOSB ;Set drive number
+ MOV CX,16
+ REP MOVSW ;Copy remaining 10 characters of name
+ XOR AL,AL
+ RET
+
+KILLSRCH:
+KILLSRCH1 EQU KILLSRCH+1
+;The purpose of the KILLSRCH1 label is to provide a jump label to the following
+; instruction which leaves out the segment override.
+ MOV WORD PTR ES:[DI.FILDIRENT],-1
+ MOV AL,-1
+ RET
+
+SRCHDEV:
+ MOV ES:[DI.FILDIRENT],BX
+ LES DI,DWORD PTR [DMAADD]
+ XOR AX,AX
+ STOSB ;Zero drive byte
+ SUB SI,4 ;Point to device name
+ MOVSW
+ MOVSW
+ MOV AX,2020H
+ STOSB
+ STOSW
+ STOSW
+ STOSW ;Fill with 8 blanks
+ XOR AX,AX
+ MOV CX,10
+ REP STOSW
+ STOSB
+RET14: RET
+
+SRCHNXT: ;System call 18
+ CALL MOVNAME
+ MOV DI,DX
+ JC NEAR PTR KILLSRCH1
+ MOV BP,[DI.DRVBP]
+ MOV AX,[DI.FILDIRENT]
+ OR AX,AX
+ JS NEAR PTR KILLSRCH1
+ PUSH DX
+ PUSH DS
+ PUSH CS
+ POP DS
+ MOV [LASTENT],AX
+ CALL CONTSRCH
+ POP ES
+ POP DI
+ JMP SAVPLCE
+
+
+FILESIZE: ;System call 35
+ CALL GETFILE
+ MOV AL,-1
+ JC RET14
+ ADD DI,33 ;Write size in RR field
+ MOV CX,ES:[DI.RECSIZ-33]
+ OR CX,CX
+ JNZ RECOK
+ MOV CX,128
+RECOK:
+ XOR AX,AX
+ XOR DX,DX ;Intialize size to zero
+ OR BH,BH ;Check for named I/O device
+ JS DEVSIZ
+ INC SI
+ INC SI ;Point to length field
+ MOV AX,[SI+2] ;Get high word of size
+ DIV CX
+ PUSH AX ;Save high part of result
+ LODSW ;Get low word of size
+ DIV CX
+ OR DX,DX ;Check for zero remainder
+ POP DX
+ JZ DEVSIZ
+ INC AX ;Round up for partial record
+ JNZ DEVSIZ ;Propagate carry?
+ INC DX
+DEVSIZ:
+ STOSW
+ MOV AX,DX
+ STOSB
+ MOV AL,0
+ CMP CX,64
+ JAE RET14 ;Only 3-byte field if RECSIZ >= 64
+ MOV ES:[DI],AH
+ RET
+
+
+SETDMA: ;System call 26
+ MOV CS:[DMAADD],DX
+ MOV CS:[DMAADD+2],DS
+ RET
+
+NOSUCHDRV:
+ MOV AL,-1
+ RET
+
+GETFATPT: ;System call 27
+ MOV DL,0 ;Use default drive
+
+GETFATPTDL: ;System call 28
+ PUSH CS
+ POP DS
+ MOV AL,DL
+ CALL GETTHISDRV
+ JC NOSUCHDRV
+ CALL FATREAD
+ MOV BX,[BP.FAT]
+ MOV AL,[BP.CLUSMSK]
+ INC AL
+ MOV DX,[BP.MAXCLUS]
+ DEC DX
+ MOV CX,[BP.SECSIZ]
+ LDS SI,DWORD PTR [SPSAVE]
+ MOV [SI.BXSAVE],BX
+ MOV [SI.DXSAVE],DX
+ MOV [SI.CXSAVE],CX
+ MOV [SI.DSSAVE],CS
+ RET
+
+
+GETDSKPT: ;System call 31
+ PUSH CS
+ POP DS
+ MOV AL,[CURDRV]
+ MOV [THISDRV],AL
+ CALL FATREAD
+ LDS SI,DWORD PTR [SPSAVE]
+ MOV [SI.BXSAVE],BP
+ MOV [SI.DSSAVE],CS
+ RET
+
+
+DSKRESET: ;System call 13
+ PUSH CS
+ POP DS
+WRTFATS:
+; DS=CS. Writes back all dirty FATs. All registers destroyed.
+ XOR AL,AL
+ XCHG AL,[DIRTYBUF]
+ OR AL,AL
+ JZ NOBUF
+ MOV BP,[BUFDRVBP]
+ MOV DX,[BUFSECNO]
+ MOV BX,[BUFFER]
+ MOV CX,1
+ CALL DWRITE
+NOBUF:
+ MOV CL,[NUMIO]
+ MOV CH,0
+ MOV BP,[DRVTAB]
+WRTFAT:
+ PUSH CX
+ CALL CHKFATWRT
+ POP CX
+ ADD BP,DPBSIZ
+ LOOP WRTFAT
+ RET
+
+
+GETDRV: ;System call 25
+ MOV AL,CS:[CURDRV]
+RET15: RET
+
+
+SETRNDREC: ;System call 36
+ CALL GETREC
+ MOV [DI+33],AX
+ MOV [DI+35],DL
+ CMP [DI.RECSIZ],64
+ JAE RET15
+ MOV [DI+36],DH ;Set 4th byte only if record size < 64
+RET16: RET
+
+
+SELDSK: ;System call 14
+ MOV AL,CS:[NUMDRV]
+ CMP DL,AL
+ JNB RET17
+ MOV CS:[CURDRV],DL
+RET17: RET
+
+BUFIN: ;System call 10
+ MOV AX,CS
+ MOV ES,AX
+ MOV SI,DX
+ MOV CH,0
+ LODSW
+ OR AL,AL
+ JZ RET17
+ MOV BL,AH
+ MOV BH,CH
+ CMP AL,BL
+ JBE NOEDIT
+ CMP BYTE PTR [BX+SI],0DH
+ JZ EDITON
+NOEDIT:
+ MOV BL,CH
+EDITON:
+ MOV DL,AL
+ DEC DX
+NEWLIN:
+ MOV AL,CS:[CARPOS]
+ MOV CS:[STARTPOS],AL
+ PUSH SI
+ MOV DI,OFFSET DOSGROUP:INBUF
+ MOV AH,CH
+ MOV BH,CH
+ MOV DH,CH
+GETCH:
+ CALL IN
+ CMP AL,"F"-"@" ;Ignore ^F
+ JZ GETCH
+ CMP AL,CS:ESCCHAR
+ JZ ESC
+ CMP AL,7FH
+ JZ BACKSP
+ CMP AL,8
+ JZ BACKSP
+ CMP AL,13
+ JZ ENDLIN
+ CMP AL,10
+ JZ PHYCRLF
+ CMP AL,CANCEL
+ JZ KILNEW
+SAVCH:
+ CMP DH,DL
+ JAE BUFFUL
+ STOSB
+ INC DH
+ CALL BUFOUT
+ OR AH,AH
+ JNZ GETCH
+ CMP BH,BL
+ JAE GETCH
+ INC SI
+ INC BH
+ JMP SHORT GETCH
+
+BUFFUL:
+ MOV AL,7
+ CALL OUT
+ JMP SHORT GETCH
+
+ESC:
+ CALL IN
+ MOV CL,ESCTABLEN
+ PUSH DI
+ MOV DI,OFFSET DOSGROUP:ESCTAB
+ REPNE SCASB
+ POP DI
+ SHL CX,1
+ MOV BP,CX
+ JMP [BP+OFFSET DOSGROUP:ESCFUNC]
+
+ENDLIN:
+ STOSB
+ CALL OUT
+ POP DI
+ MOV [DI-1],DH
+ INC DH
+COPYNEW:
+ MOV BP,ES
+ MOV BX,DS
+ MOV ES,BX
+ MOV DS,BP
+ MOV SI,OFFSET DOSGROUP:INBUF
+ MOV CL,DH
+ REP MOVSB
+ RET
+CRLF:
+ MOV AL,13
+ CALL OUT
+ MOV AL,10
+ JMP OUT
+
+PHYCRLF:
+ CALL CRLF
+ JMP SHORT GETCH
+
+KILNEW:
+ MOV AL,"\"
+ CALL OUT
+ POP SI
+PUTNEW:
+ CALL CRLF
+ MOV AL,CS:[STARTPOS]
+ CALL TAB
+ JMP NEWLIN
+
+BACKSP:
+ OR DH,DH
+ JZ OLDBAK
+ CALL BACKUP
+ MOV AL,ES:[DI]
+ CMP AL," "
+ JAE OLDBAK
+ CMP AL,9
+ JZ BAKTAB
+ CALL BACKMES
+OLDBAK:
+ OR AH,AH
+ JNZ GETCH1
+ OR BH,BH
+ JZ GETCH1
+ DEC BH
+ DEC SI
+GETCH1:
+ JMP GETCH
+BAKTAB:
+ PUSH DI
+ DEC DI
+ STD
+ MOV CL,DH
+ MOV AL," "
+ PUSH BX
+ MOV BL,7
+ JCXZ FIGTAB
+FNDPOS:
+ SCASB
+ JNA CHKCNT
+ CMP ES:BYTE PTR [DI+1],9
+ JZ HAVTAB
+ DEC BL
+CHKCNT:
+ LOOP FNDPOS
+FIGTAB:
+ SUB BL,CS:[STARTPOS]
+HAVTAB:
+ SUB BL,DH
+ ADD CL,BL
+ AND CL,7
+ CLD
+ POP BX
+ POP DI
+ JZ OLDBAK
+TABBAK:
+ CALL BACKMES
+ LOOP TABBAK
+ JMP SHORT OLDBAK
+BACKUP:
+ DEC DH
+ DEC DI
+BACKMES:
+ MOV AL,8
+ CALL OUT
+ MOV AL," "
+ CALL OUT
+ MOV AL,8
+ JMP OUT
+
+TWOESC:
+ MOV AL,ESCCH
+ JMP SAVCH
+
+COPYLIN:
+ MOV CL,BL
+ SUB CL,BH
+ JMP SHORT COPYEACH
+
+COPYSTR:
+ CALL FINDOLD
+ JMP SHORT COPYEACH
+
+COPYONE:
+ MOV CL,1
+COPYEACH:
+ MOV AH,0
+ CMP DH,DL
+ JZ GETCH2
+ CMP BH,BL
+ JZ GETCH2
+ LODSB
+ STOSB
+ CALL BUFOUT
+ INC BH
+ INC DH
+ LOOP COPYEACH
+GETCH2:
+ JMP GETCH
+
+SKIPONE:
+ CMP BH,BL
+ JZ GETCH2
+ INC BH
+ INC SI
+ JMP GETCH
+
+SKIPSTR:
+ CALL FINDOLD
+ ADD SI,CX
+ ADD BH,CL
+ JMP GETCH
+
+FINDOLD:
+ CALL IN
+ MOV CL,BL
+ SUB CL,BH
+ JZ NOTFND
+ DEC CX
+ JZ NOTFND
+ PUSH ES
+ PUSH DS
+ POP ES
+ PUSH DI
+ MOV DI,SI
+ INC DI
+ REPNE SCASB
+ POP DI
+ POP ES
+ JNZ NOTFND
+ NOT CL
+ ADD CL,BL
+ SUB CL,BH
+RET30: RET
+NOTFND:
+ POP BP
+ JMP GETCH
+
+REEDIT:
+ MOV AL,"@"
+ CALL OUT
+ POP DI
+ PUSH DI
+ PUSH ES
+ PUSH DS
+ CALL COPYNEW
+ POP DS
+ POP ES
+ POP SI
+ MOV BL,DH
+ JMP PUTNEW
+
+ENTERINS:
+ IF TOGLINS
+ NOT AH
+ JMP GETCH
+ ENDIF
+ IF NOT TOGLINS
+ MOV AH,-1
+ JMP GETCH
+
+EXITINS:
+ MOV AH,0
+ JMP GETCH
+ ENDIF
+
+ESCFUNC DW GETCH
+ DW TWOESC
+ IF NOT TOGLINS
+ DW EXITINS
+ ENDIF
+ DW ENTERINS
+ DW BACKSP
+ DW REEDIT
+ DW KILNEW
+ DW COPYLIN
+ DW SKIPSTR
+ DW COPYSTR
+ DW SKIPONE
+ DW COPYONE
+
+ IF IBM
+ DW COPYONE
+ DW CTRLZ
+CTRLZ:
+ MOV AL,"Z"-"@"
+ JMP SAVCH
+ ENDIF
+BUFOUT:
+ CMP AL," "
+ JAE OUT
+ CMP AL,9
+ JZ OUT
+ PUSH AX
+ MOV AL,"^"
+ CALL OUT
+ POP AX
+ OR AL,40H
+ JMP SHORT OUT
+
+NOSTOP:
+ CMP AL,"P"-"@"
+ JZ INCHK
+ IF NOT TOGLPRN
+ CMP AL,"N"-"@"
+ JZ INCHK
+ ENDIF
+ CMP AL,"C"-"@"
+ JZ INCHK
+ RET
+
+CONOUT: ;System call 2
+ MOV AL,DL
+OUT:
+ CMP AL,20H
+ JB CTRLOUT
+ CMP AL,7FH
+ JZ OUTCH
+ INC CS:BYTE PTR [CARPOS]
+OUTCH:
+ PUSH AX
+ CALL STATCHK
+ POP AX
+ CALL FAR PTR BIOSOUT
+ TEST CS:BYTE PTR [PFLAG],-1
+ JZ RET18
+ CALL FAR PTR BIOSPRINT
+RET18: RET
+
+STATCHK:
+ CALL FAR PTR BIOSSTAT
+ JZ RET18
+ CMP AL,'S'-'@'
+ JNZ NOSTOP
+ CALL FAR PTR BIOSIN ;Eat Cntrl-S
+INCHK:
+ CALL FAR PTR BIOSIN
+ CMP AL,'P'-'@'
+ JZ PRINTON
+ IF NOT TOGLPRN
+ CMP AL,'N'-'@'
+ JZ PRINTOFF
+ ENDIF
+ CMP AL,'C'-'@'
+ JNZ RET18
+; Ctrl-C handler.
+; "^C" and CR/LF is printed. Then the user registers are restored and the
+; user CTRL-C handler is executed. At this point the top of the stack has
+; 1) the interrupt return address should the user CTRL-C handler wish to
+; allow processing to continue; 2) the original interrupt return address
+; to the code that performed the function call in the first place. If the
+; user CTRL-C handler wishes to continue, it must leave all registers
+; unchanged and IRET. The function that was interrupted will simply be
+; repeated.
+ MOV AL,3 ;Display "^C"
+ CALL BUFOUT
+ CALL CRLF
+ CLI ;Prepare to play with stack
+ MOV SS,CS:[SSSAVE]
+ MOV SP,CS:[SPSAVE] ;User stack now restored
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ POP SI
+ POP DI
+ POP BP
+ POP DS
+ POP ES ;User registers now restored
+ INT CONTC ;Execute user Ctrl-C handler
+ JMP COMMAND ;Repeat command otherwise
+
+PRINTON:
+ IF TOGLPRN
+ NOT CS:BYTE PTR [PFLAG]
+ RET
+ ENDIF
+ IF NOT TOGLPRN
+ MOV CS:BYTE PTR [PFLAG],1
+ RET
+
+PRINTOFF:
+ MOV CS:BYTE PTR [PFLAG],0
+ RET
+ ENDIF
+
+CTRLOUT:
+ CMP AL,13
+ JZ ZERPOS
+ CMP AL,8
+ JZ BACKPOS
+ CMP AL,9
+ JNZ OUTCHJ
+ MOV AL,CS:[CARPOS]
+ OR AL,0F8H
+ NEG AL
+TAB:
+ PUSH CX
+ MOV CL,AL
+ MOV CH,0
+ JCXZ POPTAB
+TABLP:
+ MOV AL," "
+ CALL OUT
+ LOOP TABLP
+POPTAB:
+ POP CX
+RET19: RET
+
+ZERPOS:
+ MOV CS:BYTE PTR [CARPOS],0
+OUTCHJ: JMP OUTCH
+
+BACKPOS:
+ DEC CS:BYTE PTR [CARPOS]
+ JMP OUTCH
+
+
+CONSTAT: ;System call 11
+ CALL STATCHK
+ MOV AL,0
+ JZ RET19
+ OR AL,-1
+ RET
+
+
+CONIN: ;System call 1
+ CALL IN
+ PUSH AX
+ CALL OUT
+ POP AX
+ RET
+
+
+IN: ;System call 8
+ CALL INCHK
+ JZ IN
+RET29: RET
+
+RAWIO: ;System call 6
+ MOV AL,DL
+ CMP AL,-1
+ JNZ RAWOUT
+ LDS SI,DWORD PTR CS:[SPSAVE] ;Get pointer to register save area
+ CALL FAR PTR BIOSSTAT
+ JNZ RESFLG
+ OR BYTE PTR [SI.FSAVE],40H ;Set user's zero flag
+ XOR AL,AL
+ RET
+
+RESFLG:
+ AND BYTE PTR [SI.FSAVE],0FFH-40H ;Reset user's zero flag
+RAWINP: ;System call 7
+ CALL FAR PTR BIOSIN
+ RET
+RAWOUT:
+ CALL FAR PTR BIOSOUT
+ RET
+
+LIST: ;System call 5
+ MOV AL,DL
+LISTOUT:
+ PUSH AX
+ CALL STATCHK
+ POP AX
+ CALL FAR PTR BIOSPRINT
+RET20: RET
+
+PRTBUF: ;System call 9
+ MOV SI,DX
+OUTSTR:
+ LODSB
+ CMP AL,"$"
+ JZ RET20
+ CALL OUT
+ JMP SHORT OUTSTR
+
+OUTMES: ;String output for internal messages
+ LODS CS:BYTE PTR [SI]
+ CMP AL,"$"
+ JZ RET20
+ CALL OUT
+ JMP SHORT OUTMES
+
+
+MAKEFCB: ;Interrupt call 41
+DRVBIT EQU 2
+NAMBIT EQU 4
+EXTBIT EQU 8
+ MOV DL,0 ;Flag--not ambiguous file name
+ TEST AL,DRVBIT ;Use current drive field if default?
+ JNZ DEFDRV
+ MOV BYTE PTR ES:[DI],0 ;No - use default drive
+DEFDRV:
+ INC DI
+ MOV CX,8
+ TEST AL,NAMBIT ;Use current name fiels as defualt?
+ XCHG AX,BX ;Save bits in BX
+ MOV AL," "
+ JZ FILLB ;If not, go fill with blanks
+ ADD DI,CX
+ XOR CX,CX ;Don't fill any
+FILLB:
+ REP STOSB
+ MOV CL,3
+ TEST BL,EXTBIT ;Use current extension as default
+ JZ FILLB2
+ ADD DI,CX
+ XOR CX,CX
+FILLB2:
+ REP STOSB
+ XCHG AX,CX ;Put zero in AX
+ STOSW
+ STOSW ;Initialize two words after to zero
+ SUB DI,16 ;Point back at start
+ TEST BL,1 ;Scan off separators if not zero
+ JZ SKPSPC
+ CALL SCANB ;Peel off blanks and tabs
+ CALL DELIM ;Is it a one-time-only delimiter?
+ JNZ NOSCAN
+ INC SI ;Skip over the delimiter
+SKPSPC:
+ CALL SCANB ;Always kill preceding blanks and tabs
+NOSCAN:
+ CALL GETLET
+ JBE NODRV ;Quit if termination character
+ CMP BYTE PTR[SI],":" ;Check for potential drive specifier
+ JNZ NODRV
+ INC SI ;Skip over colon
+ SUB AL,"@" ;Convert drive letter to binary drive number
+ JBE BADDRV ;Valid drive numbers are 1-15
+ CMP AL,CS:[NUMDRV]
+ JBE HAVDRV
+BADDRV:
+ MOV DL,-1
+HAVDRV:
+ STOSB ;Put drive specifier in first byte
+ INC SI
+ DEC DI ;Counteract next two instructions
+NODRV:
+ DEC SI ;Back up
+ INC DI ;Skip drive byte
+ MOV CX,8
+ CALL GETWORD ;Get 8-letter file name
+ CMP BYTE PTR [SI],"."
+ JNZ NODOT
+ INC SI ;Skip over dot if present
+ MOV CX,3 ;Get 3-letter extension
+ CALL MUSTGETWORD
+NODOT:
+ LDS BX,CS:DWORD PTR [SPSAVE]
+ MOV [BX.SISAVE],SI
+ MOV AL,DL
+ RET
+
+NONAM:
+ ADD DI,CX
+ DEC SI
+ RET
+
+GETWORD:
+ CALL GETLET
+ JBE NONAM ;Exit if invalid character
+ DEC SI
+MUSTGETWORD:
+ CALL GETLET
+ JBE FILLNAM
+ JCXZ MUSTGETWORD
+ DEC CX
+ CMP AL,"*" ;Check for ambiguous file specifier
+ JNZ NOSTAR
+ MOV AL,"?"
+ REP STOSB
+NOSTAR:
+ STOSB
+ CMP AL,"?"
+ JNZ MUSTGETWORD
+ OR DL,1 ;Flag ambiguous file name
+ JMP MUSTGETWORD
+FILLNAM:
+ MOV AL," "
+ REP STOSB
+ DEC SI
+RET21: RET
+
+SCANB:
+ LODSB
+ CALL SPCHK
+ JZ SCANB
+ DEC SI
+ RET
+
+GETLET:
+;Get a byte from [SI], convert it to upper case, and compare for delimiter.
+;ZF set if a delimiter, CY set if a control character (other than TAB).
+ LODSB
+ AND AL,7FH
+ CMP AL,"a"
+ JB CHK
+ CMP AL,"z"
+ JA CHK
+ SUB AL,20H ;Convert to upper case
+CHK:
+ CMP AL,"."
+ JZ RET21
+ CMP AL,'"'
+ JZ RET21
+ CMP AL,"/"
+ JZ RET21
+ CMP AL,"["
+ JZ RET21
+ CMP AL,"]"
+ JZ RET21
+
+ IF IBM
+DELIM:
+ ENDIF
+ CMP AL,":" ;Allow ":" as separator in IBM version
+ JZ RET21
+ IF NOT IBM
+DELIM:
+ ENDIF
+
+ CMP AL,"+"
+ JZ RET101
+ CMP AL,"="
+ JZ RET101
+ CMP AL,";"
+ JZ RET101
+ CMP AL,","
+ JZ RET101
+SPCHK:
+ CMP AL,9 ;Filter out tabs too
+ JZ RET101
+;WARNING! " " MUST be the last compare
+ CMP AL," "
+RET101: RET
+
+SETVECT: ; Interrupt call 37
+ XOR BX,BX
+ MOV ES,BX
+ MOV BL,AL
+ SHL BX,1
+ SHL BX,1
+ MOV ES:[BX],DX
+ MOV ES:[BX+2],DS
+ RET
+
+
+NEWBASE: ; Interrupt call 38
+ MOV ES,DX
+ LDS SI,CS:DWORD PTR [SPSAVE]
+ MOV DS,[SI.CSSAVE]
+ XOR SI,SI
+ MOV DI,SI
+ MOV AX,DS:[2]
+ MOV CX,80H
+ REP MOVSW
+
+SETMEM:
+
+; Inputs:
+; AX = Size of memory in paragraphs
+; DX = Segment
+; Function:
+; Completely prepares a program base at the
+; specified segment.
+; Outputs:
+; DS = DX
+; ES = DX
+; [0] has INT 20H
+; [2] = First unavailable segment ([ENDMEM])
+; [5] to [9] form a long call to the entry point
+; [10] to [13] have exit address (from INT 22H)
+; [14] to [17] have ctrl-C exit address (from INT 23H)
+; [18] to [21] have fatal error address (from INT 24H)
+; DX,BP unchanged. All other registers destroyed.
+
+ XOR CX,CX
+ MOV DS,CX
+ MOV ES,DX
+ MOV SI,EXIT
+ MOV DI,SAVEXIT
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOV ES:[2],AX
+ SUB AX,DX
+ CMP AX,MAXDIF
+ JBE HAVDIF
+ MOV AX,MAXDIF
+HAVDIF:
+ MOV BX,ENTRYPOINTSEG
+ SUB BX,AX
+ SHL AX,1
+ SHL AX,1
+ SHL AX,1
+ SHL AX,1
+ MOV DS,DX
+ MOV DS:[6],AX
+ MOV DS:[8],BX
+ MOV DS:[0],20CDH ;"INT INTTAB"
+ MOV DS:(BYTE PTR [5]),LONGCALL
+ RET
+
+DATE16:
+ PUSH CX
+ CALL READTIME
+ SHL CL,1 ;Minutes to left part of byte
+ SHL CL,1
+ SHL CX,1 ;Push hours and minutes to left end
+ SHL CX,1
+ SHL CX,1
+ SHR DH,1 ;Count every two seconds
+ OR CL,DH ;Combine seconds with hours and minutes
+ MOV DX,CX
+ POP CX
+ MOV AX,WORD PTR [MONTH] ;Fetch month and year
+ SHL AL,1 ;Push month to left to make room for day
+ SHL AL,1
+ SHL AL,1
+ SHL AL,1
+ SHL AX,1
+ OR AL,[DAY]
+RET22: RET
+
+FOURYEARS EQU 3*365+366
+
+READTIME:
+;Gets time in CX:DX. Figures new date if it has changed.
+;Uses AX, CX, DX.
+ CALL FAR PTR BIOSGETTIME
+ CMP AX,[DAYCNT] ;See if day count is the same
+ JZ RET22
+ CMP AX,FOURYEARS*30 ;Number of days in 120 years
+ JAE RET22 ;Ignore if too large
+ MOV [DAYCNT],AX
+ PUSH SI
+ PUSH CX
+ PUSH DX ;Save time
+ XOR DX,DX
+ MOV CX,FOURYEARS ;Number of days in 4 years
+ DIV CX ;Compute number of 4-year units
+ SHL AX,1
+ SHL AX,1
+ SHL AX,1 ;Multiply by 8 (no. of half-years)
+ MOV CX,AX ;<240 implies AH=0
+ MOV SI,OFFSET DOSGROUP:YRTAB ;Table of days in each year
+ CALL DSLIDE ;Find out which of four years we're in
+ SHR CX,1 ;Convert half-years to whole years
+ JNC SK ;Extra half-year?
+ ADD DX,200
+SK:
+ CALL SETYEAR
+ MOV CL,1 ;At least at first month in year
+ MOV SI,OFFSET DOSGROUP:MONTAB ;Table of days in each month
+ CALL DSLIDE ;Find out which month we're in
+ MOV [MONTH],CL
+ INC DX ;Remainder is day of month (start with one)
+ MOV [DAY],DL
+ CALL WKDAY ;Set day of week
+ POP DX
+ POP CX
+ POP SI
+RET23: RET
+
+DSLIDE:
+ MOV AH,0
+DSLIDE1:
+ LODSB ;Get count of days
+ CMP DX,AX ;See if it will fit
+ JB RET23 ;If not, done
+ SUB DX,AX
+ INC CX ;Count one more month/year
+ JMP SHORT DSLIDE1
+
+SETYEAR:
+;Set year with value in CX. Adjust length of February for this year.
+ MOV BYTE PTR [YEAR],CL
+CHKYR:
+ TEST CL,3 ;Check for leap year
+ MOV AL,28
+ JNZ SAVFEB ;28 days if no leap year
+ INC AL ;Add leap day
+SAVFEB:
+ MOV [MONTAB+1],AL ;Store for February
+ RET
+
+;Days in year
+YRTAB DB 200,166 ;Leap year
+ DB 200,165
+ DB 200,165
+ DB 200,165
+
+;Days of each month
+MONTAB DB 31 ;January
+ DB 28 ;February--reset each time year changes
+ DB 31 ;March
+ DB 30 ;April
+ DB 31 ;May
+ DB 30 ;June
+ DB 31 ;July
+ DB 31 ;August
+ DB 30 ;September
+ DB 31 ;October
+ DB 30 ;November
+ DB 31 ;December
+
+GETDATE: ;Function call 42
+ PUSH CS
+ POP DS
+ CALL READTIME ;Check for rollover to next day
+ MOV AX,[YEAR]
+ MOV BX,WORD PTR [DAY]
+ LDS SI,DWORD PTR [SPSAVE] ;Get pointer to user registers
+ MOV [SI.DXSAVE],BX ;DH=month, DL=day
+ ADD AX,1980 ;Put bias back
+ MOV [SI.CXSAVE],AX ;CX=year
+ MOV AL,CS:[WEEKDAY]
+RET24: RET
+
+SETDATE: ;Function call 43
+ MOV AL,-1 ;Be ready to flag error
+ SUB CX,1980 ;Fix bias in year
+ JC RET24 ;Error if not big enough
+ CMP CX,119 ;Year must be less than 2100
+ JA RET24
+ OR DH,DH
+ JZ RET24
+ OR DL,DL
+ JZ RET24 ;Error if either month or day is 0
+ CMP DH,12 ;Check against max. month
+ JA RET24
+ PUSH CS
+ POP DS
+ CALL CHKYR ;Set Feb. up for new year
+ MOV AL,DH
+ MOV BX,OFFSET DOSGROUP:MONTAB-1
+ XLAT ;Look up days in month
+ CMP AL,DL
+ MOV AL,-1 ;Restore error flag, just in case
+ JB RET24 ;Error if too many days
+ CALL SETYEAR
+ MOV WORD PTR [DAY],DX ;Set both day and month
+ SHR CX,1
+ SHR CX,1
+ MOV AX,FOURYEARS
+ MOV BX,DX
+ MUL CX
+ MOV CL,BYTE PTR [YEAR]
+ AND CL,3
+ MOV SI,OFFSET DOSGROUP:YRTAB
+ MOV DX,AX
+ SHL CX,1 ;Two entries per year, so double count
+ CALL DSUM ;Add up the days in each year
+ MOV CL,BH ;Month of year
+ MOV SI,OFFSET DOSGROUP:MONTAB
+ DEC CX ;Account for months starting with one
+ CALL DSUM ;Add up days in each month
+ MOV CL,BL ;Day of month
+ DEC CX ;Account for days starting with one
+ ADD DX,CX ;Add in to day total
+ XCHG AX,DX ;Get day count in AX
+ MOV [DAYCNT],AX
+ CALL FAR PTR BIOSSETDATE
+WKDAY:
+ MOV AX,[DAYCNT]
+ XOR DX,DX
+ MOV CX,7
+ INC AX
+ INC AX ;First day was Tuesday
+ DIV CX ;Compute day of week
+ MOV [WEEKDAY],DL
+ XOR AL,AL ;Flag OK
+RET25: RET
+
+DSUM:
+ MOV AH,0
+ JCXZ RET25
+DSUM1:
+ LODSB
+ ADD DX,AX
+ LOOP DSUM1
+ RET
+
+GETTIME: ;Function call 44
+ PUSH CS
+ POP DS
+ CALL READTIME
+ LDS SI,DWORD PTR [SPSAVE] ;Get pointer to user registers
+ MOV [SI.DXSAVE],DX
+ MOV [SI.CXSAVE],CX
+ XOR AL,AL
+RET26: RET
+
+SETTIME: ;Function call 45
+;Time is in CX:DX in hours, minutes, seconds, 1/100 sec.
+ MOV AL,-1 ;Flag in case of error
+ CMP CH,24 ;Check hours
+ JAE RET26
+ CMP CL,60 ;Check minutes
+ JAE RET26
+ CMP DH,60 ;Check seconds
+ JAE RET26
+ CMP DL,100 ;Check 1/100's
+ JAE RET26
+ CALL FAR PTR BIOSSETTIME
+ XOR AL,AL
+ RET
+
+
+; Default handler for division overflow trap
+DIVOV:
+ PUSH SI
+ PUSH AX
+ MOV SI,OFFSET DOSGROUP:DIVMES
+ CALL OUTMES
+ POP AX
+ POP SI
+ INT 23H ;Use Ctrl-C abort on divide overflow
+ IRET
+
+CODSIZ EQU $-CODSTRT ;Size of code segment
+CODE ENDS
+
+
+;***** DATA AREA *****
+CONSTANTS SEGMENT BYTE
+ ORG 0
+CONSTRT EQU $ ;Start of constants segment
+
+IONAME:
+ IF NOT IBM
+ DB "PRN ","LST ","NUL ","AUX ","CON "
+ ENDIF
+ IF IBM
+ DB "COM1","PRN ","LPT1","NUL ","AUX ","CON "
+ ENDIF
+DIVMES DB 13,10,"Divide overflow",13,10,"$"
+CARPOS DB 0
+STARTPOS DB 0
+PFLAG DB 0
+DIRTYDIR DB 0 ;Dirty buffer flag
+NUMDRV DB 0 ;Number of drives
+NUMIO DB ? ;Number of disk tables
+VERFLG DB 0 ;Initialize with verify off
+CONTPOS DW 0
+DMAADD DW 80H ;User's disk transfer address (disp/seg)
+ DW ?
+ENDMEM DW ?
+MAXSEC DW 0
+BUFFER DW ?
+BUFSECNO DW 0
+BUFDRVNO DB -1
+DIRTYBUF DB 0
+BUFDRVBP DW ?
+DIRBUFID DW -1
+DAY DB 0
+MONTH DB 0
+YEAR DW 0
+DAYCNT DW -1
+WEEKDAY DB 0
+CURDRV DB 0 ;Default to drive A
+DRVTAB DW 0 ;Address of start of DPBs
+DOSLEN EQU CODSIZ+($-CONSTRT) ;Size of CODE + CONSTANTS segments
+CONSTANTS ENDS
+
+DATA SEGMENT WORD
+; Init code overlaps with data area below
+
+ ORG 0
+INBUF DB 128 DUP (?)
+CONBUF DB 131 DUP (?) ;The rest of INBUF and console buffer
+LASTENT DW ?
+EXITHOLD DB 4 DUP (?)
+FATBASE DW ?
+NAME1 DB 11 DUP (?) ;File name buffer
+ATTRIB DB ?
+NAME2 DB 11 DUP (?)
+NAME3 DB 12 DUP (?)
+EXTFCB DB ?
+;WARNING - the following two items are accessed as a word
+CREATING DB ?
+DELALL DB ?
+TEMP LABEL WORD
+SPSAVE DW ?
+SSSAVE DW ?
+CONTSTK DW ?
+SECCLUSPOS DB ? ;Position of first sector within cluster
+DSKERR DB ?
+TRANS DB ?
+PREREAD DB ? ;0 means preread; 1 means optional
+READOP DB ?
+THISDRV DB ?
+
+ EVEN
+FCB DW ? ;Address of user FCB
+NEXTADD DW ?
+RECPOS DB 4 DUP (?)
+RECCNT DW ?
+LASTPOS DW ?
+CLUSNUM DW ?
+SECPOS DW ? ;Position of first sector accessed
+VALSEC DW ? ;Number of valid (previously written) sectors
+BYTSECPOS DW ? ;Position of first byte within sector
+BYTPOS DB 4 DUP (?) ;Byte position in file of access
+BYTCNT1 DW ? ;No. of bytes in first sector
+BYTCNT2 DW ? ;No. of bytes in last sector
+SECCNT DW ? ;No. of whole sectors
+ENTFREE DW ?
+
+ DB 80H DUP (?) ;Stack space
+IOSTACK LABEL BYTE
+ DB 80H DUP (?)
+DSKSTACK LABEL BYTE
+
+ IF DSKTEST
+NSS DW ?
+NSP DW ?
+ ENDIF
+
+DIRBUF LABEL WORD
+
+;Init code below overlaps with data area above
+
+ ORG 0
+
+MOVFAT:
+;This section of code is safe from being overwritten by block move
+ REP MOVS BYTE PTR [DI],[SI]
+ CLD
+ MOV ES:[DMAADD+2],DX
+ MOV SI,[DRVTAB] ;Address of first DPB
+ MOV AL,-1
+ MOV CL,[NUMIO] ;Number of DPBs
+FLGFAT:
+ MOV DI,ES:[SI.FAT] ;get pointer to FAT
+ DEC DI ;Point to dirty byte
+ STOSB ;Flag as unused
+ ADD SI,DPBSIZ ;Point to next DPB
+ LOOP FLGFAT
+ MOV AX,[ENDMEM]
+ CALL SETMEM ;Set up segment
+
+XXX PROC FAR
+ RET
+XXX ENDP
+
+DOSINIT:
+ CLI
+ CLD
+ PUSH CS
+ POP ES
+ MOV ES:[ENDMEM],DX
+ LODSB ;Get no. of drives & no. of I/O drivers
+ MOV ES:[NUMIO],AL
+ MOV DI,OFFSET DOSGROUP:MEMSTRT
+PERDRV:
+ MOV BP,DI
+ MOV AL,ES:[DRVCNT]
+ STOSB ;DEVNUM
+ LODSB ;Physical unit no.
+ STOSB ;DRVNUM
+ CMP AL,15
+ JA BADINIT
+ CBW ;Index into FAT size table
+ SHL AX,1
+ ADD AX,OFFSET DOSGROUP:FATSIZTAB
+ XCHG BX,AX
+ LODSW ;Pointer to DPT
+ PUSH SI
+ MOV SI,AX
+ LODSW
+ STOSW ;SECSIZ
+ MOV DX,AX
+ CMP AX,ES:[MAXSEC]
+ JBE NOTMAX
+ MOV ES:[MAXSEC],AX
+NOTMAX:
+ LODSB
+ DEC AL
+ STOSB ;CLUSMSK
+ JZ HAVSHFT
+ CBW
+FIGSHFT:
+ INC AH
+ SAR AL,1
+ JNZ FIGSHFT
+ MOV AL,AH
+HAVSHFT:
+ STOSB ;CLUSSHFT
+ MOVSW ;FIRFAT (= number of reserved sectors)
+ MOVSB ;FATCNT
+ MOVSW ;MAXENT
+ MOV AX,DX ;SECSIZ again
+ MOV CL,5
+ SHR AX,CL
+ MOV CX,AX ;Directory entries per sector
+ DEC AX
+ ADD AX,ES:[BP.MAXENT]
+ XOR DX,DX
+ DIV CX
+ STOSW ;DIRSEC (temporarily)
+ MOVSW ;DSKSIZ (temporarily)
+FNDFATSIZ:
+ MOV AL,1
+ MOV DX,1
+GETFATSIZ:
+ PUSH DX
+ CALL FIGFATSIZ
+ POP DX
+ CMP AL,DL ;Compare newly computed FAT size with trial
+ JZ HAVFATSIZ ;Has sequence converged?
+ CMP AL,DH ;Compare with previous trial
+ MOV DH,DL
+ MOV DL,AL ;Shuffle trials
+ JNZ GETFATSIZ ;Continue iterations if not oscillating
+ DEC WORD PTR ES:[BP.DSKSIZ] ;Damp those oscillations
+ JMP SHORT FNDFATSIZ ;Try again
+
+BADINIT:
+ MOV SI,OFFSET DOSGROUP:BADMES
+ CALL OUTMES
+ STI
+ HLT
+
+HAVFATSIZ:
+ STOSB ;FATSIZ
+ MUL ES:BYTE PTR[BP.FATCNT] ;Space occupied by all FATs
+ ADD AX,ES:[BP.FIRFAT]
+ STOSW ;FIRDIR
+ ADD AX,ES:[BP.DIRSEC]
+ MOV ES:[BP.FIRREC],AX ;Destroys DIRSEC
+ CALL FIGMAX
+ MOV ES:[BP.MAXCLUS],CX
+ MOV AX,BX ;Pointer into FAT size table
+ STOSW ;Allocate space for FAT pointer
+ MOV AL,ES:[BP.FATSIZ]
+ XOR AH,AH
+ MUL ES:[BP.SECSIZ]
+ CMP AX,ES:[BX] ;Bigger than already allocated
+ JBE SMFAT
+ MOV ES:[BX],AX
+SMFAT:
+ POP SI ;Restore pointer to init. table
+ MOV AL,ES:[DRVCNT]
+ INC AL
+ MOV ES:[DRVCNT],AL
+ CMP AL,ES:[NUMIO]
+ JAE CONTINIT
+ JMP PERDRV
+
+BADINITJ:
+ JMP BADINIT
+
+CONTINIT:
+ PUSH CS
+ POP DS
+;Calculate true address of buffers, FATs, free space
+ MOV BP,[MAXSEC]
+ MOV AX,OFFSET DOSGROUP:DIRBUF
+ ADD AX,BP
+ MOV [BUFFER],AX ;Start of buffer
+ ADD AX,BP
+ MOV [DRVTAB],AX ;Start of DPBs
+ SHL BP,1 ;Two sectors - directory and buffer
+ ADD BP,DI ;Allocate buffer space
+ ADD BP,ADJFAC ;True address of FATs
+ PUSH BP
+ MOV SI,OFFSET DOSGROUP:FATSIZTAB
+ MOV DI,SI
+ MOV CX,16
+TOTFATSIZ:
+ INC BP ;Add one for Dirty byte
+ INC BP ;Add one for I/O device number
+ LODSW ;Get size of this FAT
+ XCHG AX,BP
+ STOSW ;Save address of this FAT
+ ADD BP,AX ;Compute size of next FAT
+ CMP AX,BP ;If size was zero done
+ LOOPNZ TOTFATSIZ
+ MOV AL,15
+ SUB AL,CL ;Compute number of FATs used
+ MOV [NUMDRV],AL
+ XOR AX,AX ;Set zero flag
+ REPZ SCASW ;Make sure all other entries are zero
+ JNZ BADINITJ
+ ADD BP,15 ;True start of free space
+ MOV CL,4
+ SHR BP,CL ;First free segment
+ MOV DX,CS
+ ADD DX,BP
+ MOV BX,0FH
+ MOV CX,[ENDMEM]
+ CMP CX,1 ;Use memory scan?
+ JNZ SETEND
+ MOV CX,DX ;Start scanning just after DOS
+MEMSCAN:
+ INC CX
+ JZ SETEND
+ MOV DS,CX
+ MOV AL,[BX]
+ NOT AL
+ MOV [BX],AL
+ CMP AL,[BX]
+ NOT AL
+ MOV [BX],AL
+ JZ MEMSCAN
+SETEND:
+ IF HIGHMEM
+ SUB CX,BP
+ MOV BP,CX ;Segment of DOS
+ MOV DX,CS ;Program segment
+ ENDIF
+ IF NOT HIGHMEM
+ MOV BP,CS
+ ENDIF
+; BP has segment of DOS (whether to load high or run in place)
+; DX has program segment (whether after DOS or overlaying DOS)
+; CX has size of memory in paragraphs (reduced by DOS size if HIGHMEM)
+ MOV CS:[ENDMEM],CX
+ IF HIGHMEM
+ MOV ES,BP
+ XOR SI,SI
+ MOV DI,SI
+ MOV CX,(DOSLEN+1)/2
+ PUSH CS
+ POP DS
+ REP MOVSW ;Move DOS to high memory
+ ENDIF
+ XOR AX,AX
+ MOV DS,AX
+ MOV ES,AX
+ MOV DI,INTBASE
+ MOV AX,OFFSET DOSGROUP:QUIT
+ STOSW ;Set abort address--displacement
+ MOV AX,BP
+ MOV BYTE PTR DS:[ENTRYPOINT],LONGJUMP
+ MOV WORD PTR DS:[ENTRYPOINT+1],OFFSET DOSGROUP:ENTRY
+ MOV WORD PTR DS:[ENTRYPOINT+3],AX
+ MOV WORD PTR DS:[0],OFFSET DOSGROUP:DIVOV ;Set default divide trap address
+ MOV DS:[2],AX
+ MOV CX,9
+ REP STOSW ;Set 5 segments (skip 2 between each)
+ MOV WORD PTR DS:[INTBASE+4],OFFSET DOSGROUP:COMMAND
+ MOV WORD PTR DS:[INTBASE+12],OFFSET DOSGROUP:IRET ;Ctrl-C exit
+ MOV WORD PTR DS:[INTBASE+16],OFFSET DOSGROUP:IRET ;Fatal error exit
+ MOV AX,OFFSET BIOSREAD
+ STOSW
+ MOV AX,BIOSSEG
+ STOSW
+ STOSW ;Add 2 to DI
+ STOSW
+ MOV WORD PTR DS:[INTBASE+18H],OFFSET BIOSWRITE
+ MOV WORD PTR DS:[EXIT],100H
+ MOV WORD PTR DS:[EXIT+2],DX
+ IF NOT IBM
+ MOV SI,OFFSET DOSGROUP:HEADER
+ CALL OUTMES
+ ENDIF
+ PUSH CS
+ POP DS
+ PUSH CS
+ POP ES
+;Move the FATs into position
+ MOV AL,[NUMIO]
+ CBW
+ XCHG AX,CX
+ MOV DI,OFFSET DOSGROUP:MEMSTRT.FAT
+FATPOINT:
+ MOV SI,WORD PTR [DI] ;Get address within FAT address table
+ MOVSW ;Set address of this FAT
+ ADD DI,DPBSIZ-2 ;Point to next DPB
+ LOOP FATPOINT
+ POP CX ;True address of first FAT
+ MOV SI,OFFSET DOSGROUP:MEMSTRT ;Place to move DPBs from
+ MOV DI,[DRVTAB] ;Place to move DPBs to
+ SUB CX,DI ;Total length of DPBs
+ CMP DI,SI
+ JBE MOVJMP ;Are we moving to higher or lower memory?
+ DEC CX ;Move backwards to higher memory
+ ADD DI,CX
+ ADD SI,CX
+ INC CX
+ STD
+MOVJMP:
+ MOV ES,BP
+ JMP MOVFAT
+
+FIGFATSIZ:
+ MUL ES:BYTE PTR[BP.FATCNT]
+ ADD AX,ES:[BP.FIRFAT]
+ ADD AX,ES:[BP.DIRSEC]
+FIGMAX:
+;AX has equivalent of FIRREC
+ SUB AX,ES:[BP.DSKSIZ]
+ NEG AX
+ MOV CL,ES:[BP.CLUSSHFT]
+ SHR AX,CL
+ INC AX
+ MOV CX,AX ;MAXCLUS
+ INC AX
+ MOV DX,AX
+ SHR DX,1
+ ADC AX,DX ;Size of FAT in bytes
+ MOV SI,ES:[BP.SECSIZ]
+ ADD AX,SI
+ DEC AX
+ XOR DX,DX
+ DIV SI
+ RET
+
+BADMES:
+ DB 13,10,"INIT TABLE BAD",13,10,"$"
+
+FATSIZTAB:
+ DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+
+DRVCNT DB 0
+
+MEMSTRT LABEL WORD
+ADJFAC EQU DIRBUF-MEMSTRT
+DATA ENDS
+ END
+\1a
\ No newline at end of file
--- /dev/null
+ TITLE MS-DOS version 1.25 by Tim Paterson March 3, 1982
+ PAGE 60,132
+; Use the following booleans to set the switches
+FALSE EQU 0
+TRUE EQU NOT FALSE
+
+; Use the switches below to produce the standard Microsoft version of the IBM
+; version of the operating system
+MSVER EQU TRUE
+IBM EQU FALSE
+
+; Set this switch to cause DOS to move itself to the end of memory
+HIGHMEM EQU FALSE
+
+; Turn on switch below to allow testing disk code with DEBUG. It sets
+; up a different stack for disk I/O (functions > 11) than that used for
+; character I/O which effectively makes the DOS re-entrant.
+
+DSKTEST EQU FALSE
+
+ INCLUDE MSDOS.ASM
+
+\1a
\ No newline at end of file
--- /dev/null
+; Z80 to 8086 Translator version 2.21
+; Runs on the 8086 under 86-DOS
+; by Tim Paterson
+;
+ ORG 100H
+EOF: EQU 1AH ;End of file
+EOL: EQU 0DH
+FCB: EQU 5CH
+SYSTEM: EQU 5
+OPEN: EQU 15
+CLOSE: EQU 16
+SETDMA: EQU 26
+CREATE: EQU 22
+DELETE: EQU 19
+READ: EQU 20
+WRITE: EQU 21
+PRNBUF: EQU 9
+ MOV SP,STACK
+ MOV DX,HEADER
+ MOV CL,9
+ CALL SYSTEM
+ MOV BX,FCB+12
+ XOR AL,AL
+ MOV CH,4
+CLRFCB:
+ MOV [BX],AL
+ INC BX
+ DEC CH
+ JNZ CLRFCB
+ MOV [FCB+32],AL
+ MOV BX,FCB
+ MOV DX,PUTFCB
+ MOV CX,16
+ UP
+ MOV SI,BX
+ MOV DI,DX
+ REP
+ MOVB
+ MOV DX,DI
+ MOV BX,SI
+ MOV [PUTFCB+32],AL
+ MOV BX,"A"+5300H ;"AS"
+ MOV [PUTFCB+9],BX
+ MOV AL,'M'
+ MOV [PUTFCB+11],AL
+ MOV DX,FCB
+ MOV CL,OPEN
+ CALL SYSTEM
+ INC AL
+ MOV DX,NOFILE
+ JZ ABORTJ
+ MOV DX,PUTFCB
+ MOV CL,DELETE
+ CALL SYSTEM
+ MOV DX,PUTFCB
+ MOV CL,CREATE
+ CALL SYSTEM
+ INC AL
+ MOV DX,NOROOM
+ABORTJ:
+ JZ ABORT
+ MOV DX,PUTFCB
+ MOV CL,OPEN
+ CALL SYSTEM
+ MOV BX,PUTBUF
+ MOV [PUTPT],BX
+ MOV BX,GETBUF+80H
+ MOV [GETPT],BX
+TRANLN:
+ XOR AL,AL
+ MOV [OP1],AL
+ MOV [OP2],AL
+ MOV BX,OPCODE
+ CALL LOAD
+ MOV BX,OP1
+ CALL GETOP
+ MOV B,[BX],0
+ MOV BX,OP2
+ CALL GETOP
+DOLIN:
+ MOV B,[BX],0
+ CALL FINDOP
+ENLIN:
+ MOV SP,STACK
+ MOV AL,[CHAR]
+ CMP AL,';'
+ JNZ NOCOM
+ MOV AL,9
+ CALL PUTCH
+ MOV AL,';'
+NOCOM:
+ CALL PUTCH
+PUTLIN:
+ CMP AL,EOF
+ JZ END
+ CALL GETCH
+ CALL PUTCH
+ CMP AL,10
+ JNZ PUTLIN
+ JP TRANLN
+END:
+ MOV CH,127
+ MOV AL,1AH
+FILL:
+ CALL PUTCH
+ DEC CH
+ JNZ FILL
+ MOV DX,PUTFCB
+ MOV CL,CLOSE
+ CALL SYSTEM
+ MOV DX,ENDMES
+ABORT:
+ MOV CL,PRNBUF
+ CALL SYSTEM
+ JMP 0
+DELIM:
+ CALL GETCH
+DELCHK:
+ CMP AL,EOL
+ JZ DOLIN
+ CMP AL,EOF
+ JZ DOLIN
+ CMP AL,';'
+ JZ DOLIN
+ CMP AL,' '
+ JZ RET
+ CMP AL,':'
+ JZ RET
+ CMP AL,','
+ JZ RET
+ CMP AL,9
+ RET
+HEX:
+ AND AL,0FH
+ ADD AL,90H
+ DAA
+ ADC AL,40H
+ DAA
+PUTCH:
+ PUSH BX
+ PUSH DX
+ PUSH CX
+ LAHF
+ XCHG AH,AL
+ PUSH AX
+ XCHG AH,AL
+ AND AL,7FH
+ MOV BX,[PUTPT]
+ MOV [BX],AL
+ INC BX
+ MOV [PUTPT],BX
+ CMP BX,PUTBUF+80H
+ JNZ POPRET
+ MOV DX,PUTBUF
+ MOV [PUTPT],DX
+ MOV CL,SETDMA
+ CALL SYSTEM
+ MOV DX,PUTFCB
+ MOV CL,WRITE
+ CALL SYSTEM
+ OR AL,AL
+ MOV DX,WRTERR
+ JNZ ABORT
+POPRET:
+ POP AX
+ XCHG AH,AL
+ SAHF
+NOTAF:
+ POP CX
+ POP DX
+ POP BX
+ RET
+;
+; Get character from source file.
+;
+GETCH:
+ PUSH BX
+ PUSH DX
+ PUSH CX
+ MOV BX,[GETPT] ; Get buffer pointer.
+ CMP BX,GETBUF+80H ; Past end-of-buffer?
+ JNZ GETIT ; Jump if not.
+ MOV DX,GETBUF ; Set `DMA address'.
+ MOV CL,SETDMA
+ CALL SYSTEM
+ MOV DX,FCB ; Read the next record from source file.
+ MOV CL,READ
+ CALL SYSTEM
+ CMP AL,0 ; Entire record read OK?
+ JE OKRECORD
+ CMP AL,3 ; Partial record read?
+ JE OKRECORD
+ MOV AL,EOF ; Force end-of-file character in case
+ JP TESEND ; there is nothing in the record.
+OKRECORD:
+ MOV BX,GETBUF ; Reset buffer pointer.
+GETIT:
+ MOV AL,[BX] ; Get next character from buffer.
+ INC BX ; Point to next character.
+ MOV [GETPT],BX ; Save new pointer.
+TESEND:
+ MOV [CHAR],AL
+ JP NOTAF ; Pop registers and return.
+LOAD:
+ CALL DELIM
+ JZ LOADOP
+EATLAB:
+ CALL PUTCH
+ CALL DELIM
+ JNZ EATLAB
+ENLAB:
+ MOV AL,':'
+ CALL PUTCH
+LOADOP:
+ MOV BX,OPCODE
+EATEM:
+ CALL DELIM
+ JZ EATEM
+LOADLP:
+ CALL IDCHK
+ JNC $+5
+ JMP OPERR
+ MOV [BX],AL
+ INC BX
+ CALL DELIM
+ JNZ LOADLP
+ MOV B,[BX],0
+ CMP AL,':'
+ JNZ RET
+ MOV BX,OPCODE
+ CALL TRANS
+ JP ENLAB
+GETOP:
+ XOR AL,AL
+ LAHF
+ XCHG AX,BP
+ SAHF
+GETLP:
+ CALL DELIM
+ JZ GETLP
+OPCHAR:
+ CMP AL,'('
+ JNZ NOTLEF
+ LAHF
+ XCHG AX,BP
+ SAHF
+ INC AL
+ LAHF
+ XCHG AX,BP
+ SAHF
+ MOV B,[BX],'['
+ JP NEXCH
+NOTLEF:
+ CMP AL,')'
+ JNZ NOTRIT
+ LAHF
+ XCHG AX,BP
+ SAHF
+ DEC AL
+ LAHF
+ XCHG AX,BP
+ SAHF
+ MOV B,[BX],']'
+ JP NEXCH
+NOTRIT:
+ MOV [BX],AL
+ CMP AL,''''
+ JZ EATQUO
+ CALL IDCHK
+ JNC GETID
+NEXCH:
+ INC BX
+ CALL GETCH
+IDRET:
+ CALL DELCHK
+ JNZ OPCHAR
+ CMP AL,' '
+ JZ OPCHAR
+ RET
+EATQUO:
+ INC BX
+ CALL GETCH
+ MOV [BX],AL
+ CMP AL,';'
+ JZ L0000
+ CALL DELCHK
+L0000:
+ CMP AL,''''
+ JNZ EATQUO
+ JP NEXCH
+IDCHK:
+ CMP AL,'0'
+ JC RET
+ CMP AL,'9'+1
+ CMC
+ JNC RET
+ CMP AL,40H
+ JC RET
+ AND AL,5FH
+ CMP AL,'A'
+ JC RET
+ CMP AL,'Z'+1
+ CMC
+ RET
+GETID:
+ MOV [BX],AL
+ MOV CH,1
+LODID:
+ INC BX
+ CALL GETCH
+ CALL IDCHK
+ JC RWCHK
+ MOV [BX],AL
+ INC CH
+ JP LODID
+RWCHK:
+ LAHF
+ XCHG AH,AL
+ PUSH AX
+ XCHG AH,AL
+ PUSH BX
+ DEC BX
+ DEC CH
+ MOV DL,CH
+ JZ LOOKRW
+ MOV DL,[BX]
+ DEC BX
+ DEC CH
+ JNZ NORW
+LOOKRW:
+ MOV AL,[BX]
+ MOV DH,AL
+ PUSH BX
+ MOV BX,RWTAB
+ MOV CX,LENRW
+RWLK:
+ UP
+ MOV DI,BX
+ REPNZ
+ SCAB
+ MOV BX,DI
+ JNZ NOTRW
+ PUSH BX
+ PUSH CX
+ MOV CX,LENRW-1
+ LAHF
+ ADD BX,CX
+ RCR SI
+ SAHF
+ RCL SI
+ MOV AL,[BX]
+ POP CX
+ POP BX
+ CMP AL,DL
+ JZ HAVRW
+ MOV AL,CL
+ OR AL,AL
+ MOV AL,DH
+ JNZ RWLK
+NOTRW:
+ POP BX
+NORW:
+ POP BX
+ENDRW:
+ POP AX
+ XCHG AH,AL
+ SAHF
+ JMP IDRET
+HAVRW:
+ POP BX
+ INC CL
+ MOV [BX],CL
+ INC BX
+ POP DX
+ PUSH BX
+ MOV AL,CL
+ MOV BX,IXSI
+ CMP AL,RSI
+ JZ IXIY
+ MOV BX,IYDI
+ CMP AL,RDI
+ JNZ NORW
+IXIY:
+ LAHF
+ XCHG AX,BP
+ SAHF
+ JZ NOTENC
+ LAHF
+ XCHG AX,BP
+ SAHF
+ CALL OUTSTR
+ JP NORW
+NOTENC:
+ LAHF
+ XCHG AX,BP
+ SAHF
+ POP BX
+ DEC BX
+ MOV B,[BX],'['
+ INC BX
+ ADD AL,RIX-1
+ MOV [BX],AL
+ INC BX
+ MOV B,[BX],']'
+ INC BX
+ JP ENDRW
+ RET
+FINDOP:
+ MOV BX,OPCODE
+ MOV CX,5
+ XOR AL,AL
+ UP
+ MOV DI,BX
+ REPNZ
+ SCAB
+ MOV BX,DI
+ JNZ OPERR
+ MOV AL,4
+ SUB AL,CL
+ JZ RET
+ DEC AL
+ JZ OPERR
+ MOV CL,AL
+ DEC BX
+ DEC BX
+ OR B,[BX],080H
+ MOV AL,[OPCODE]
+ SUB AL,'A'
+ JC OPERR
+ ADD AL,AL
+ MOV DL,AL
+ MOV DH,0
+ MOV BX,OPTAB
+ LAHF
+ ADD BX,DX
+ RCR SI
+ SAHF
+ RCL SI
+ MOV DL,[BX]
+ INC BX
+ MOV DH,[BX]
+ XCHG DX,BX
+ MOV AL,9
+ CALL PUTCH
+LOOKOP:
+ MOV AL,[BX]
+ OR AL,AL
+ JZ OPERR
+ MOV DX,OPCODE+1
+ MOV CH,CL
+LOOKLP:
+ MOV SI,DX
+ LODB
+ CMP AL,[BX]
+ JNZ NEXOP
+ INC DX
+ INC BX
+ DEC CH
+ JNZ LOOKLP
+ MOV DX,[BX]
+ MOV BX,[BX+2]
+ JMP DX
+NEXOP:
+ RCR SI
+ TEST B,[BX],080H
+ RCL SI
+ LAHF
+ INC BX
+ SAHF
+ JZ NEXOP
+ MOV DX,4
+ LAHF
+ ADD BX,DX
+ RCR SI
+ SAHF
+ RCL SI
+ JP LOOKOP
+OPERR:
+ MOV BX,OPCODE
+ CALL OUTSTR
+ CALL TWOOPS
+ MOV BX,OPCDER
+ CALL OUTSTR
+ JMP ENLIN
+LD:
+ CALL OUTSTR
+ MOV BX,OP1
+ MOV DX,OP2+1
+ CALL LCHECK
+ JNZ $+5
+ JMP LDAX
+ XCHG DX,BX
+ DEC BX
+ INC DX
+ CALL LCHECK
+ JNZ $+5
+ JMP STAX
+;If immediate move, check for byte memory reference
+ MOV AL,[OP2]
+ CMP AL,20H ;Could be immediate?
+ MOV AL,9
+ JC L0001
+ CALL BYTCHK ;Add "B," if memory reference
+L0001:
+ CALL TRAN1
+ JP TRNOP2
+TWOOPS:
+ CALL TRNOP1
+TRNOP2:
+ MOV AL,','
+TRAN2:
+ MOV BX,OP2
+PTRANS:
+ CALL PUTCH
+TRANS:
+ MOV AL,[BX]
+ LAHF
+ INC BX
+ SAHF
+ OR AL,AL
+ JZ RET
+ CALL TRNTOK
+ JP TRANS
+LCHECK:
+ MOV AL,[BX]
+ CMP AL,RAL
+ JNZ RET
+ MOV SI,DX
+ LODB
+ CMP AL,RCX
+ JZ RET
+ CMP AL,RDX
+ RET
+
+ONEOP:
+ CALL OUTSTR
+ MOV AL,9
+ CALL BYTCHK ;If memory reference, add "B," flag
+ JMPS TRAN1
+
+TRNOP1:
+ MOV AL,9
+TRAN1:
+ MOV BX,OP1
+ JP PTRANS
+IN:
+ MOV AL,[OP1]
+ CMP AL,RAL
+ XCHG DX,BX
+ MOV BX,OP2
+ JZ GETPORT
+ MOV BX,SAVEAX
+ CALL OUTSTR
+ CALL OUTSTR
+ MOV BX,OP2
+ CALL GETPORT
+ MOV BX,MOV0
+ CALL ONEOP
+ MOV AL,','
+ CALL PUTCH
+ MOV AL,RAL
+ CALL TRNTOK
+IODONE:
+ MOV BX,RESTAX
+ JMP OUTSTR
+OUT:
+ MOV AL,[OP2]
+ XCHG DX,BX
+ MOV BX,OP1
+ CMP AL,RAL
+ JZ GETOUT
+ MOV BX,SAVEAX
+ CALL OUTSTR
+ MOV BX,MOVAL
+ CALL OUTSTR
+ CALL TRNOP2
+ MOV BX,CRLFTB
+ CALL OUTSTR
+ MOV BX,OP1
+ CALL GETOUT
+ JP IODONE
+GETPORT:
+ MOV AL,[BX]
+ CMP AL,'['
+ JNZ NOBRAK
+ LAHF
+ INC BX
+ SAHF
+ PUSH BX
+ MOV CX,80
+ MOV AL,']'
+ UP
+ MOV DI,BX
+ REPNZ
+ SCAB
+ MOV BX,DI
+ LAHF
+ DEC BX
+ SAHF
+ MOV B,[BX],0
+ POP BX
+NOBRAK:
+ MOV AL,[BX]
+ CMP AL,RGCL
+ JNZ FIXPOR
+ MOV BX,IO1
+ CALL OUTSTR
+ XCHG DX,BX
+ CALL OUTSTR
+ MOV AL,RDX
+ CALL TRNTOK
+ MOV BX,IO2
+ JMP OUTSTR
+GETOUT:
+ CALL GETPORT
+ JNC RET
+ MOV BX,BADIO
+ JMP OUTSTR
+FIXPOR:
+ XCHG DX,BX
+ CALL OUTSTR
+ XCHG DX,BX
+ JMP TRANS
+LDAX:
+ MOV BX,LDAX1
+LSAX:
+ CALL OUTSTR
+ MOV SI,DX
+ LODB
+ CALL TRNTOK
+ JP OUTSTR
+STAX:
+ MOV BX,STAX1
+ JP LSAX
+TRNTOK:
+ CMP AL,' '
+ JC $+5
+ JMP PUTCH
+ PUSH BX
+ PUSH CX
+ MOV CL,AL
+ MOV CH,0
+ MOV BX,TOKTAB-2
+ LAHF
+ ADD BX,CX
+ RCR SI
+ SAHF
+ RCL SI
+ LAHF
+ ADD BX,CX
+ RCR SI
+ SAHF
+ RCL SI
+ MOV AL,[BX]
+ CALL PUTCH
+ INC BX
+ MOV AL,[BX]
+ POP CX
+ POP BX
+ OR AL,AL
+ JZ RET
+ JMP PUTCH
+PUSH:
+ MOV DX,PUSHAF
+ JP AFCHK
+POP:
+ MOV DX,POPAF
+AFCHK:
+ MOV AL,[OP1]
+ CMP AL,RAX
+ JNZ ONEOPJ
+ XCHG DX,BX
+OUTSTR:
+ MOV AL,[BX]
+ OR AL,AL
+ JNZ L0002
+ CALL NEWOP
+L0002:
+ CALL PUTCH
+ INC BX
+ ADD AL,AL
+ JNC OUTSTR
+ RET
+NEWOP:
+ MOV AL,13
+ CALL PUTCH
+ MOV AL,10
+ CALL PUTCH
+ MOV AL,9
+ RET
+LDDR:
+ CALL OUTSTR
+ MOV BX,BLMOVE
+ JP OUTSTR
+CPDR:
+ CALL OUTSTR
+ MOV BX,CMPREP
+ JP OUTSTR
+ADD:
+ MOV AL,[OP1]
+ CMP AL,RBX
+ JZ DAD
+ARITH:
+ CALL OUTSTR
+ MOV AL,[OP2]
+ OR AL,AL
+ JZ $+5
+ JMP TWOOPS
+ MOV AL,9
+ CALL PUTCH
+ MOV AL,RAL
+ CALL TRNTOK
+ MOV AL,','
+ JMP TRAN1
+ACCUM:
+ CALL OUTSTR
+ MOV AL,9
+ CALL PUTCH
+ MOV AL,RAL
+ JMP TRNTOK
+ONEOPJ: JMP ONEOP
+DAD:
+ MOV BX,DAD1
+ CALL OUTSTR
+ CALL TWOOPS
+ MOV BX,DAD2
+ JP OUTSTR
+
+INCDEC:
+ MOV AL,[OP1]
+ CMP AL,RCX+1 ;16-bit?
+ JNC ONEOPJ
+ MOV BX,LAHF
+ CALL OUTSTR
+ XCHG DX,BX
+ MOV BX,OPCODE-1
+ CALL ONEOP
+ XCHG DX,BX
+OUTSTRJ:
+ JMP OUTSTR
+JUMP:
+ MOV AL,[OP1]
+ CMP AL,'['
+ JNZ DIRECT
+ MOV AL,[OP1+1]
+ MOV [OP1],AL
+ XOR AL,AL
+ MOV [OP1+1],AL
+DIRECT:
+ MOV AL,[OP2]
+ OR AL,AL
+ JZ ONEOPJ
+ CALL FIXCON
+ MOV BX,OP2
+OUTCON:
+ MOV CH,AL
+ MOV AL,'J'
+ CALL PUTCH
+ MOV AL,CH
+ CALL TRNTOK
+ MOV AL,9
+ CALL PTRANS
+ MOV AL,CH
+ CMP AL,ODDPAR
+ MOV BX,WARNPA
+ JZ OUTSTRJ
+ CMP AL,EVEPAR
+ JZ OUTSTRJ
+ RET
+FIXCON:
+ MOV AL,[OP1]
+ CMP AL,RGCL
+ JNZ RET
+ MOV AL,CY
+ RET
+RETURN:
+ MOV AL,[OP1]
+ OR AL,AL
+ JZ OUTSTRJ
+ MOV BX,'R'+4500H ;"RE"
+ MOV [OP2],BX
+ MOV BX,'T'
+ MOV [OP2+2],BX
+ JP DIRECT
+ONEOPJ1:
+ JMP ONEOP
+DOCALL:
+ MOV AL,[OP2]
+ OR AL,AL
+ JZ ONEOPJ1
+ CALL FIXCON
+ DEC AL
+ XOR AL,1
+ INC AL
+ MOV BX,LABEL
+ CALL OUTCON
+ MOV BX,OPCODE-1
+ CALL OUTSTR
+ MOV AL,[OP2]
+ OR AL,AL
+ MOV AL,9
+ MOV BX,OP2
+ JZ L0003
+ CALL PTRANS
+L0003:
+ MOV BX,CRLF
+ CALL OUTSTR
+ CALL TRANS
+ CALL OUTSTR
+ MOV BX,LABEL+4
+NEXLAB:
+ INC [BX]
+ MOV AL,[BX]
+ CMP AL,'9'+1
+ JNZ RET
+ MOV B,[BX],'0'
+ LAHF
+ DEC BX
+ SAHF
+ JP NEXLAB
+EX:
+ MOV AL,[OP1]
+ CMP AL,RAX
+ JZ OUTSTRJ1
+ MOV AL,[OP1+1]
+ CMP AL,STP
+ JZ XTHL
+ MOV BX,XCHG
+ CALL OUTSTR
+ JMP TWOOPS
+XTHL:
+ MOV BX,XTHL1
+ CALL OUTSTR
+ CALL TRNOP2
+ MOV BX,XTHL2
+OUTSTRJ1:
+ JMP OUTSTR
+PSEUDO:
+ CALL ONEOP
+ MOV AL,[OP2]
+ OR AL,AL
+ JZ RET
+ JMP TRNOP2
+ RET
+BITSET:
+ MOV CL,0
+ JP SETRES
+RES:
+ MOV CL,-1
+SETRES:
+ CALL OUTSTR
+ PUSH BX
+ MOV AL,[OP2]
+ CMP AL,'['
+ MOV AL,9
+ JNZ L0004
+ CALL BFLAG
+L0004:
+ CALL TRAN2
+ MOV AL,','
+ CALL PUTCH
+ CALL GETBIT
+ MOV BX,BITERR
+ JNC L0005
+ CALL OUTSTR
+L0005:
+ POP BX
+ JMP OUTSTR
+
+BYTCHK:
+;Check if memory reference and add "B," for byte mode
+ CMP B,[OP1],"[" ;Memory reference?
+ JNZ RET
+ CMP B,[OP1+1],RIX ;Referencing IX as a word?
+ JZ RET
+ CMP B,[OP1+1],RIY
+ JZ RET
+BFLAG:
+ CALL PUTCH
+ MOV AL,'B'
+ CALL PUTCH
+ MOV AL,','
+ RET
+
+GETBIT:
+ MOV AL,[OP1+1]
+ OR AL,AL
+ STC
+ JNZ RET
+ MOV AL,[OP1]
+ SUB AL,'0'
+ JC RET
+ CMP AL,8
+ CMC
+ JC RET
+ MOV CH,AL
+ INC CH
+ XOR AL,AL
+ STC
+SHFT:
+ RCL AL
+ DEC CH
+ JNZ SHFT
+ XOR AL,CL
+ MOV CH,AL
+ MOV AL,'0'
+ CALL PUTCH
+ MOV AL,CH
+ RCR AL
+ RCR AL
+ RCR AL
+ RCR AL
+ CALL HEX
+ MOV AL,CH
+ CALL HEX
+ MOV AL,'H'
+ JMP PUTCH
+OPTAB:
+ DW AOPS,BOPS,COPS,DOPS,EOPS
+ DW FOPS,GOPS,HOPS,IOPS,JOPS
+ DW KOPS,LOPS,MOPS,NOPS,OOPS
+ DW POPS,QOPS,ROPS,SOPS,TOPS
+ DW UOPS,VOPS,WOPS,XOPS,YOPS
+ DW ZOPS
+AOPS:
+ DM 'DD'
+ DW ADD,OPCODE
+ DM 'DC'
+ DW ARITH,OPCODE
+ DM 'ND'
+ DW ARITH,OPCODE
+ DB 0
+BOPS:
+ DM 'IT'
+ DW BITSET,TESBIT
+ DB 0
+COPS:
+ DM 'ALL'
+ DW DOCALL,OPCODE
+ DM 'P'
+ DW ARITH,CMP
+ DM 'PL'
+ DW ACCUM,NOT
+ DM 'PIR'
+ DW OUTSTR,CPIR
+ DM 'PDR'
+ DW CPDR,DOWN
+ DM 'CF'
+ DW OUTSTR,CMC
+ DB 0
+DOPS:
+ DM 'EC'
+ DW INCDEC,OPCODE
+ DM 'JNZ'
+ DW ONEOP,DJNZ
+ DM 'AA'
+ DW OUTSTR,OPCODE
+ DM 'I'
+ DW OUTSTR,OPCODE
+ DM 'W'
+ DW PSEUDO,OPCODE
+ DM 'B'
+ DW PSEUDO,OPCODE
+ DM 'M'
+ DW PSEUDO,OPCODE
+ DM 'S'
+ DW ONEOP,OPCODE
+ DB 0
+EOPS:
+ DM 'X'
+ DW EX,EXAF
+ DM 'I'
+ DW OUTSTR,OPCODE
+ DM 'XX'
+ DW OUTSTR,EXX
+ DM 'QU'
+ DW ONEOP,OPCODE
+ DM 'NDIF'
+ DW OUTSTR,OPCODE
+ DB 0
+FOPS:
+ DB 0
+GOPS:
+ DB 0
+HOPS:
+ DM 'ALT'
+ DW OUTSTR,HLT
+ DB 0
+IOPS:
+ DM 'NC'
+ DW INCDEC,OPCODE
+ DM 'N'
+ DW IN,INB
+ DM 'F'
+ DW ONEOP,OPCODE
+ DB 0
+JOPS:
+ DM 'R'
+ DW JUMP,JR
+ DM 'P'
+ DW JUMP,JMP
+ DB 0
+KOPS:
+ DB 0
+LOPS:
+ DM 'D'
+ DW LD,MOV
+ DM 'DIR'
+ DW OUTSTR,UP
+ DM 'DDR'
+ DW LDDR,DOWN
+ DB 0
+MOPS:
+ DB 0
+NOPS:
+ DM 'EG'
+ DW ACCUM,OPCODE
+ DB 0
+OOPS:
+ DM 'R'
+ DW ARITH,OPCODE
+ DM 'UT'
+ DW OUT,OUTB
+ DM 'RG'
+ DW ONEOP,OPCODE
+ DB 0
+POPS:
+ DM 'OP'
+ DW POP,OPCODE
+ DM 'USH'
+ DW PUSH,OPCODE
+ DB 0
+QOPS:
+ DB 0
+ROPS:
+ DM 'ET'
+ DW RETURN,OPCODE
+ DM 'LA'
+ DW ACCUM,RCL
+ DM 'RA'
+ DW ACCUM,RCR
+ DM 'LCA'
+ DW ACCUM,ROL
+ DM 'RCA'
+ DW ACCUM,ROR
+ DM 'L'
+ DW ONEOP,RCL
+ DM 'R'
+ DW ONEOP,RCR
+ DM 'LC'
+ DW ONEOP,ROL
+ DM 'RC'
+ DW ONEOP,ROR
+ DM 'ES'
+ DW RES,RESBIT
+ DM 'ETI'
+ DW OUTSTR,IRET
+ DM 'ETN'
+ DW OUTSTR,IRET
+ DM 'ST'
+ DW ONEOP,CALL
+ DB 0
+SOPS:
+ DM 'UB'
+ DW ARITH,OPCODE
+ DM 'BC'
+ DW ARITH,SBB
+ DM 'LA'
+ DW ONEOP,SAL
+ DM 'RA'
+ DW ONEOP,SAR
+ DM 'RL'
+ DW ONEOP,SHR
+ DM 'CF'
+ DW OUTSTR,STC
+ DM 'ET'
+ DW BITSET,SETBIT
+ DB 0
+TOPS:
+ DB 0
+UOPS:
+ DB 0
+VOPS:
+ DB 0
+WOPS:
+ DB 0
+XOPS:
+ DM 'OR'
+ DW ARITH,OPCODE
+ DB 0
+YOPS:
+ DB 0
+ZOPS:
+ DB 0
+LDAX1: DM 9,'SI,'
+ DM 0,'LODB'
+STAX1: DM 9,'DI,'
+ DM 0,'STOB'
+PUSHAF: DB 'LAHF',0,'XCHG',9,'AH,AL',0,'PUSH',9,'AX',0
+ DM 'XCHG',9,'AH,AL'
+POPAF: DM 'POP',9,'AX',0,'XCHG',9,'AH,AL',0,'SAHF'
+DOWN: DM 'DOWN'
+UP: DB 'UP'
+BLMOVE: DB 0,'MOV',9,'SI,BX',0,'MOV',9,'DI,DX'
+ DB 0,'REP',0,'MOVB',0,'MOV',9,'DX,DI'
+ DM 0,'MOV',9,'BX,SI'
+CPIR: DB 'UP'
+CMPREP: DB 0,'MOV',9,'DI,BX',0,'REPNZ',0,'SCAB'
+ DM 0,'MOV',9,'BX,DI'
+DAD1: DM 'LAHF',0,'ADD'
+DAD2: DM 0,'RCR',9,'SI',0,'SAHF',0,'RCL',9,'SI'
+LAHF: DM 'LAHF'
+ DM 0,'SAHF'
+DJNZ: DB 'DEC',9,'CH',13,10
+ DB '; *** WARNING: DJNZ does not affect flags - DEC does.',0
+ DM 'JNZ'
+WARNPA: DM 13,10,'; *** WARNING: Parity flag not always same as Z80.'
+IO1: DB 'MOV',9,'DI,DX',0,'MOV',9,'DL,CL',0
+ DM 'XOR',9,'DH,DH',13,10,9
+IO2: DM 0,'MOV',9,'DX,DI'
+BADIO: DM 13,10,'; *** WARNING: Flags not same as Z80.'
+BITERR: DM 13,10,' *** ERROR: Cannot determine bit number.'
+SETBIT: DM 'LAHF',0,'OR'
+ DM 0,'SAHF'
+RESBIT: DM 'LAHF',0,'AND'
+ DM 0,'SAHF'
+TESBIT: DM 'RCR',9,'AH',0,'TEST'
+ DM 0,'RCL',9,'AH'
+XTHL1: DM 'POP',9,'SI',0,'XCHG',9,'SI'
+XTHL2: DM 0,'PUSH',9,'SI'
+EXX: DB 'XCHG',9,'BX,[HL]',0,'XCHG',9,'DX,[DE]',0
+ DM 'XCHG',9,'CX,[BC]'
+EXAF: DM 'LAHF',0,'XCHG',9,'AX,BP',0,'SAHF'
+MOVAL: DM 0,'MOV',9,'AL'
+IXSI: DM 9,'MOV',9,'SI,[IX]',13,10
+IYDI: DM 9,'MOV',9,'DI,[IY]',13,10
+RESTAX: DB 0
+SAVEAX: DM 'XCHG',9,'AX,SI'
+CRLFTB: DM 13,10,9
+INB: DM 'INB',9
+OUTB: DM 'OUTB',9
+XCHG: DM 'XCHG'
+JMP: DM 'JMP'
+JR: DM 'JMPS'
+RCL: DM 'RCL'
+RCR: DM 'RCR'
+ROL: DM 'ROL'
+ROR: DM 'ROR'
+SAL: DM 'SAL'
+SAR: DM 'SAR'
+SHR: DM 'SHR'
+STC: DM 'STC'
+IRET: DM 'IRET'
+HLT: DM 'HLT'
+CMC: DM 'CMC'
+NOT: DM 'NOT'
+MOV0: DB 0
+MOV: DM 'MOV'
+CMP: DM 'CMP'
+SBB: DM 'SBB'
+CALL: DM 'CALL'
+TOKTAB:
+ DB 'SIDI'
+ DB 'PEPOS',0,'NSNZZ',0,'NCC',0
+ DB 'AXSPBXDXCX'
+ DB 'BLBHDLDHCLCHALIXIY'
+RWTAB:
+ DB 'ABCDEHLBDHSACNZNPMPPII'
+LENRW: EQU $-RWTAB
+ DB 0,0,0,0,0,0,0,'CELPF',0,'C',0,'Z',0,0,'OEYX'
+HEADER: DB 13,10,'Z80 to 8086 Translator version 2.21',13,10,'$'
+NOROOM: DB 13,10,'File creation error',13,10,'$'
+NOFILE: DB 13,10,'File not found',13,10,'$'
+ENDMES: DB 13,10,'Translation complete',13,10,'$'
+WRTERR: DB 13,10,'Out of disk space',13,10,'$'
+OPCDER: DM 13,10,'*** Opcode Error '
+CRLF: DM 13,10
+LABEL: DB 'L0000',0
+ DM ':',9
+PUTPT: DS 2
+GETPT: DS 2
+CHAR: DS 1
+ DB 0
+OPCODE: DS 80
+OP1: DS 80
+OP2: DS 80
+PUTBUF: DS 128
+GETBUF: DS 128
+PUTFCB: DS 33
+ DS 50
+STACK: EQU $
+ ORG 1 ;This is really just for equates without EQU
+RSI: DS 1
+RDI: DS 1
+ODDPAR: DS 1
+EVEPAR: DS 1
+ DS 5 ;MINUS,PLUS,NOT ZERO,ZERO,NOT CARRY
+CY: DS 1
+RAX: DS 1
+STP: DS 1
+RBX: DS 1
+RDX: DS 1
+RCX: DS 1
+RBL: DS 1
+RBH: DS 1
+RDL: DS 1
+RDH: DS 1
+RGCL: DS 1
+RCH: DS 1
+RAL: DS 1
+RIX: DS 1
+RIY: DS 1
+\1a
\ No newline at end of file