diff --git a/v1.25/Tim_Paterson_16Dec2013_email.txt b/v1.25/Tim_Paterson_16Dec2013_email.txt new file mode 100644 index 0000000..45c4055 --- /dev/null +++ b/v1.25/Tim_Paterson_16Dec2013_email.txt @@ -0,0 +1,23 @@ +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” stack of printouts of assembly listings for some of these and probably other related programs. + +Tim Paterson +Paterson Technology +http://www.patersontech.com/ + + diff --git a/v1.25/bin/ART.BAS b/v1.25/bin/ART.BAS new file mode 100644 index 0000000..2de324a Binary files /dev/null and b/v1.25/bin/ART.BAS differ diff --git a/v1.25/bin/BALL.BAS b/v1.25/bin/BALL.BAS new file mode 100644 index 0000000..132087a Binary files /dev/null and b/v1.25/bin/BALL.BAS differ diff --git a/v1.25/bin/BASIC.COM b/v1.25/bin/BASIC.COM new file mode 100644 index 0000000..d6b32cf Binary files /dev/null and b/v1.25/bin/BASIC.COM differ diff --git a/v1.25/bin/BASICA.COM b/v1.25/bin/BASICA.COM new file mode 100644 index 0000000..dc08578 Binary files /dev/null and b/v1.25/bin/BASICA.COM differ diff --git a/v1.25/bin/CALENDAR.BAS b/v1.25/bin/CALENDAR.BAS new file mode 100644 index 0000000..34860ec Binary files /dev/null and b/v1.25/bin/CALENDAR.BAS differ diff --git a/v1.25/bin/CHKDSK.COM b/v1.25/bin/CHKDSK.COM new file mode 100644 index 0000000..bf88ed2 Binary files /dev/null and b/v1.25/bin/CHKDSK.COM differ diff --git a/v1.25/bin/CIRCLE.BAS b/v1.25/bin/CIRCLE.BAS new file mode 100644 index 0000000..611fc0d Binary files /dev/null and b/v1.25/bin/CIRCLE.BAS differ diff --git a/v1.25/bin/COLORBAR.BAS b/v1.25/bin/COLORBAR.BAS new file mode 100644 index 0000000..067792c Binary files /dev/null and b/v1.25/bin/COLORBAR.BAS differ diff --git a/v1.25/bin/COMM.BAS b/v1.25/bin/COMM.BAS new file mode 100644 index 0000000..ef3439a Binary files /dev/null and b/v1.25/bin/COMM.BAS differ diff --git a/v1.25/bin/COMMAND.COM b/v1.25/bin/COMMAND.COM new file mode 100644 index 0000000..751326f Binary files /dev/null and b/v1.25/bin/COMMAND.COM differ diff --git a/v1.25/bin/COMP.COM b/v1.25/bin/COMP.COM new file mode 100644 index 0000000..0d16119 Binary files /dev/null and b/v1.25/bin/COMP.COM differ diff --git a/v1.25/bin/DEBUG.COM b/v1.25/bin/DEBUG.COM new file mode 100644 index 0000000..b502552 Binary files /dev/null and b/v1.25/bin/DEBUG.COM differ diff --git a/v1.25/bin/DISKCOMP.COM b/v1.25/bin/DISKCOMP.COM new file mode 100644 index 0000000..08c595b Binary files /dev/null and b/v1.25/bin/DISKCOMP.COM differ diff --git a/v1.25/bin/DISKCOPY.COM b/v1.25/bin/DISKCOPY.COM new file mode 100644 index 0000000..e2436d1 Binary files /dev/null and b/v1.25/bin/DISKCOPY.COM differ diff --git a/v1.25/bin/DONKEY.BAS b/v1.25/bin/DONKEY.BAS new file mode 100644 index 0000000..8b6ac8a Binary files /dev/null and b/v1.25/bin/DONKEY.BAS differ diff --git a/v1.25/bin/EDLIN.COM b/v1.25/bin/EDLIN.COM new file mode 100644 index 0000000..5ed5148 Binary files /dev/null and b/v1.25/bin/EDLIN.COM differ diff --git a/v1.25/bin/EXE2BIN.EXE b/v1.25/bin/EXE2BIN.EXE new file mode 100644 index 0000000..d43a3f2 Binary files /dev/null and b/v1.25/bin/EXE2BIN.EXE differ diff --git a/v1.25/bin/FORMAT.COM b/v1.25/bin/FORMAT.COM new file mode 100644 index 0000000..c1cc45e Binary files /dev/null and b/v1.25/bin/FORMAT.COM differ diff --git a/v1.25/bin/LINK.EXE b/v1.25/bin/LINK.EXE new file mode 100644 index 0000000..499b7b2 Binary files /dev/null and b/v1.25/bin/LINK.EXE differ diff --git a/v1.25/bin/MODE.COM b/v1.25/bin/MODE.COM new file mode 100644 index 0000000..b9f69e3 Binary files /dev/null and b/v1.25/bin/MODE.COM differ diff --git a/v1.25/bin/MORTGAGE.BAS b/v1.25/bin/MORTGAGE.BAS new file mode 100644 index 0000000..80c062e Binary files /dev/null and b/v1.25/bin/MORTGAGE.BAS differ diff --git a/v1.25/bin/MUSIC.BAS b/v1.25/bin/MUSIC.BAS new file mode 100644 index 0000000..bd0ae0d Binary files /dev/null and b/v1.25/bin/MUSIC.BAS differ diff --git a/v1.25/bin/PIECHART.BAS b/v1.25/bin/PIECHART.BAS new file mode 100644 index 0000000..943b398 Binary files /dev/null and b/v1.25/bin/PIECHART.BAS differ diff --git a/v1.25/bin/SAMPLES.BAS b/v1.25/bin/SAMPLES.BAS new file mode 100644 index 0000000..2d2b2aa Binary files /dev/null and b/v1.25/bin/SAMPLES.BAS differ diff --git a/v1.25/bin/SETCLOCK.COM b/v1.25/bin/SETCLOCK.COM new file mode 100644 index 0000000..3bf8501 Binary files /dev/null and b/v1.25/bin/SETCLOCK.COM differ diff --git a/v1.25/bin/SPACE.BAS b/v1.25/bin/SPACE.BAS new file mode 100644 index 0000000..52ba147 Binary files /dev/null and b/v1.25/bin/SPACE.BAS differ diff --git a/v1.25/bin/SYS.COM b/v1.25/bin/SYS.COM new file mode 100644 index 0000000..7f478fa Binary files /dev/null and b/v1.25/bin/SYS.COM differ diff --git a/v1.25/source/ASM.ASM b/v1.25/source/ASM.ASM new file mode 100644 index 0000000..d870d97 --- /dev/null +++ b/v1.25/source/ASM.ASM @@ -0,0 +1,4006 @@ +; 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 $ + \ No newline at end of file diff --git a/v1.25/source/COMMAND.ASM b/v1.25/source/COMMAND.ASM new file mode 100644 index 0000000..830bf73 --- /dev/null +++ b/v1.25/source/COMMAND.ASM @@ -0,0 +1,2166 @@ +; 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 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 + \ No newline at end of file diff --git a/v1.25/source/HEX2BIN.ASM b/v1.25/source/HEX2BIN.ASM new file mode 100644 index 0000000..d7ba742 --- /dev/null +++ b/v1.25/source/HEX2BIN.ASM @@ -0,0 +1,214 @@ +; 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 + \ No newline at end of file diff --git a/v1.25/source/IO.ASM b/v1.25/source/IO.ASM new file mode 100644 index 0000000..3b9f181 --- /dev/null +++ b/v1.25/source/IO.ASM @@ -0,0 +1,1934 @@ +; 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 + \ No newline at end of file diff --git a/v1.25/source/MSDOS.ASM b/v1.25/source/MSDOS.ASM new file mode 100644 index 0000000..8538b76 --- /dev/null +++ b/v1.25/source/MSDOS.ASM @@ -0,0 +1,4031 @@ +; 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 + \ No newline at end of file diff --git a/v1.25/source/STDDOS.ASM b/v1.25/source/STDDOS.ASM new file mode 100644 index 0000000..1216b4c --- /dev/null +++ b/v1.25/source/STDDOS.ASM @@ -0,0 +1,23 @@ + 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 + + \ No newline at end of file diff --git a/v1.25/source/TRANS.ASM b/v1.25/source/TRANS.ASM new file mode 100644 index 0000000..6b06619 --- /dev/null +++ b/v1.25/source/TRANS.ASM @@ -0,0 +1,1213 @@ +; 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 + \ No newline at end of file