+; 86-DOS High-performance operating system for the 8086 version 1.25
+; by Tim Paterson
+
+
+; ****************** Revision History *************************
+; >> EVERY change must noted below!! <<
+;
+; 0.34 12/29/80 General release, updating all past customers
+; 0.42 02/25/81 32-byte directory entries added
+; 0.56 03/23/81 Variable record and sector sizes
+; 0.60 03/27/81 Ctrl-C exit changes, including register save on user stack
+; 0.74 04/15/81 Recognize I/O devices with file names
+; 0.75 04/17/81 Improve and correct buffer handling
+; 0.76 04/23/81 Correct directory size when not 2^N entries
+; 0.80 04/27/81 Add console input without echo, Functions 7 & 8
+; 1.00 04/28/81 Renumber for general release
+; 1.01 05/12/81 Fix bug in `STORE'
+; 1.10 07/21/81 Fatal error trapping, NUL device, hidden files, date & time,
+; RENAME fix, general cleanup
+; 1.11 09/03/81 Don't set CURRENT BLOCK to 0 on open; fix SET FILE SIZE
+; 1.12 10/09/81 Zero high half of CURRENT BLOCK after all (CP/M programs don't)
+; 1.13 10/29/81 Fix classic "no write-through" error in buffer handling
+; 1.20 12/31/81 Add time to FCB; separate FAT from DPT; Kill SMALLDIR;
+; Add FLUSH and MAPDEV calls; allow disk mapping in DSKCHG;
+; Lots of smaller improvements
+; 1.21 01/06/82 HIGHMEM switch to run DOS in high memory
+; 1.22 01/12/82 Add VERIFY system call to enable/disable verify after write
+; 1.23 02/11/82 Add defaulting to parser; use variable escape character
+; Don't zero extent field in IBM version (back to 1.01!)
+; 1.24 03/01/82 Restore fcn. 27 to 1.0 level; add fcn. 28
+; 1.25 03/03/82 Put marker (00) at end of directory to speed searches
+;
+; *************************************************************
+
+
+; Interrupt Entry Points:
+
+; INTBASE: ABORT
+; INTBASE+4: COMMAND
+; INTBASE+8: BASE EXIT ADDRESS
+; INTBASE+C: CONTROL-C ABORT
+; INTBASE+10H: FATAL ERROR ABORT
+; INTBASE+14H: BIOS DISK READ
+; INTBASE+18H: BIOS DISK WRITE
+; INTBASE+40H: Long jump to CALL entry point
+
+ IF IBM
+ESCCH EQU 0
+CANCEL EQU 1BH ;Cancel with ESC
+TOGLINS EQU TRUE ;One key toggles insert mode
+TOGLPRN EQU TRUE ;One key toggles printer echo
+NUMDEV EQU 6 ;Include "COM1" as I/O device name
+ZEROEXT EQU TRUE
+ ELSE
+ESCCH EQU 1BH
+CANCEL EQU "X"-"@" ;Cancel with Ctrl-X
+TOGLINS EQU FALSE ;Separate keys for insert mode on and off
+TOGLPRN EQU FALSE ;Separate keys for printer echo on and off
+NUMDEV EQU 5 ;Number of I/O device names
+ZEROEXT EQU FALSE
+ ENDIF
+
+MAXCALL EQU 36
+MAXCOM EQU 46
+INTBASE EQU 80H
+INTTAB EQU 20H
+ENTRYPOINTSEG EQU 0CH
+ENTRYPOINT EQU INTBASE+40H
+CONTC EQU INTTAB+3
+EXIT EQU INTBASE+8
+LONGJUMP EQU 0EAH
+LONGCALL EQU 9AH
+MAXDIF EQU 0FFFH
+SAVEXIT EQU 10
+
+; Field definition for FCBs
+
+FCBLOCK STRUC
+ DB 12 DUP (?) ;Drive code and name
+EXTENT DW ?
+RECSIZ DW ? ;Size of record (user settable)
+FILSIZ DW ? ;Size of file in bytes
+DRVBP DW ? ;BP for SEARCH FIRST and SEARCH NEXT
+FDATE DW ? ;Date of last writing
+FTIME DW ? ;Time of last writing
+DEVID DB ? ;Device ID number, bits 0-5
+ ;bit 7=0 for file, bit 7=1 for I/O device
+ ;If file, bit 6=0 if dirty
+ ;If I/O device, bit 6=0 if EOF (input)
+FIRCLUS DW ? ;First cluster of file
+LSTCLUS DW ? ;Last cluster accessed
+CLUSPOS DW ? ;Position of last cluster accessed
+ DB ? ;Forces NR to offset 32
+NR DB ? ;Next record
+RR DB 3 DUP (?) ;Random record
+FCBLOCK ENDS
+FILDIRENT = FILSIZ ;Used only by SEARCH FIRST and SEARCH NEXT
+
+; Description of 32-byte directory entry (same as returned by SEARCH FIRST
+; and SEARCH NEXT, functions 17 and 18).
+;
+; Location bytes Description
+;
+; 0 11 File name and extension ( 0E5H if empty)
+; 11 1 Attributes. Bits 1 or 2 make file hidden
+; 12 10 Zero field (for expansion)
+; 22 2 Time. Bits 0-4=seconds/2, bits 5-10=minute, 11-15=hour
+; 24 2 Date. Bits 0-4=day, bits 5-8=month, bits 9-15=year-1980
+; 26 2 First allocation unit ( < 4080 )
+; 28 4 File size, in bytes (LSB first, 30 bits max.)
+;
+; The File Allocation Table uses a 12-bit entry for each allocation unit on
+; the disk. These entries are packed, two for every three bytes. The contents
+; of entry number N is found by 1) multiplying N by 1.5; 2) adding the result
+; to the base address of the Allocation Table; 3) fetching the 16-bit word at
+; this address; 4) If N was odd (so that N*1.5 was not an integer), shift the
+; word right four bits; 5) mask to 12 bits (AND with 0FFF hex). Entry number
+; zero is used as an end-of-file trap in the OS and as a flag for directory
+; entry size (if SMALLDIR selected). Entry 1 is reserved for future use. The
+; first available allocation unit is assigned entry number two, and even
+; though it is the first, is called cluster 2. Entries greater than 0FF8H are
+; end of file marks; entries of zero are unallocated. Otherwise, the contents
+; of a FAT entry is the number of the next cluster in the file.
+
+
+; Field definition for Drive Parameter Block
+
+DPBLOCK STRUC
+DEVNUM DB ? ;I/O driver number
+DRVNUM DB ? ;Physical Unit number
+SECSIZ DW ? ;Size of physical sector in bytes
+CLUSMSK DB ? ;Sectors/cluster - 1
+CLUSSHFT DB ? ;Log2 of sectors/cluster
+FIRFAT DW ? ;Starting record of FATs
+FATCNT DB ? ;Number of FATs for this drive
+MAXENT DW ? ;Number of directory entries
+FIRREC DW ? ;First sector of first cluster
+MAXCLUS DW ? ;Number of clusters on drive + 1
+FATSIZ DB ? ;Number of records occupied by FAT
+FIRDIR DW ? ;Starting record of directory
+FAT DW ? ;Pointer to start of FAT
+DPBLOCK ENDS
+
+DPBSIZ EQU 20 ;Size of the structure in bytes
+DIRSEC = FIRREC ;Number of dir. sectors (init temporary)
+DSKSIZ = MAXCLUS ;Size of disk (temp used during init only)
+
+;The following are all of the segments used
+;They are declared in the order that they should be placed in the executable
+
+CODE SEGMENT
+CODE ENDS
+
+CONSTANTS SEGMENT BYTE
+CONSTANTS ENDS
+
+DATA SEGMENT WORD
+DATA ENDS
+
+DOSGROUP GROUP CODE,CONSTANTS,DATA
+
+SEGBIOS SEGMENT
+SEGBIOS ENDS
+
+
+; BOIS entry point definitions
+
+ IF IBM
+BIOSSEG EQU 60H
+ ENDIF
+ IF NOT IBM
+BIOSSEG EQU 40H
+ ENDIF
+
+SEGBIOS SEGMENT AT BIOSSEG
+ ORG 0
+ DB 3 DUP (?) ;Reserve room for jump to init code
+BIOSSTAT DB 3 DUP (?) ;Console input status check
+BIOSIN DB 3 DUP (?) ;Get console character
+BIOSOUT DB 3 DUP (?) ;Output console character
+BIOSPRINT DB 3 DUP (?) ;Output to printer
+BIOSAUXIN DB 3 DUP (?) ;Get byte from auxilliary
+BIOSAUXOUT DB 3 DUP (?) ;Output byte to auxilliary
+BIOSREAD DB 3 DUP (?) ;Disk read
+BIOSWRITE DB 3 DUP (?) ;Disk write
+BIOSDSKCHG DB 3 DUP (?) ;Dsik-change status
+BIOSSETDATE DB 3 DUP (?) ;Set date
+BIOSSETTIME DB 3 DUP (?) ;Set time
+BIOSGETTIME DB 3 DUP (?) ;Get time and date
+BIOSFLUSH DB 3 DUP (?) ;Clear console input buffer
+BIOSMAPDEV DB 3 DUP (?) ;Dynamic disk table mapper
+
+SEGBIOS ENDS
+; Location of user registers relative user stack pointer
+
+STKPTRS STRUC
+AXSAVE DW ?
+BXSAVE DW ?
+CXSAVE DW ?
+DXSAVE DW ?
+SISAVE DW ?
+DISAVE DW ?
+BPSAVE DW ?
+DSSAVE DW ?
+ESSAVE DW ?
+IPSAVE DW ?
+CSSAVE DW ?
+FSAVE DW ?
+STKPTRS ENDS
+
+; Start of code
+
+CODE SEGMENT
+ASSUME CS:DOSGROUP,DS:DOSGROUP,ES:DOSGROUP,SS:DOSGROUP
+
+ ORG 0
+CODSTRT EQU $
+ JMP DOSINIT
+
+ESCCHAR DB ESCCH ;Lead-in character for escape sequences
+ESCTAB:
+ IF NOT IBM
+ DB "S" ;Copy one char
+ DB "V" ;Skip one char
+ DB "T" ;Copy to char
+ DB "W" ;Skip to char
+ DB "U" ;Copy line
+ DB "E" ;Kill line (no change in template)
+ DB "J" ;Reedit line (new template)
+ DB "D" ;Backspace
+ DB "P" ;Enter insert mode
+ DB "Q" ;Exit insert mode
+ DB "R" ;Escape character
+ DB "R" ;End of table
+ ENDIF
+ IF IBM
+ DB 64 ;Crtl-Z - F6
+ DB 77 ;Copy one char - -->
+ DB 59 ;Copy one char - F1
+ DB 83 ;Skip one char - DEL
+ DB 60 ;Copy to char - F2
+ DB 62 ;Skip to char - F4
+ DB 61 ;Copy line - F3
+ DB 61 ;Kill line (no change to template ) - Not used
+ DB 63 ;Reedit line (new template) - F5
+ DB 75 ;Backspace - <--
+ DB 82 ;Enter insert mode - INS (toggle)
+ DB 65 ;Escape character - F7
+ DB 65 ;End of table
+ ENDIF
+
+ESCTABLEN EQU $-ESCTAB
+ IF NOT IBM
+HEADER DB 13,10,"MS-DOS version 1.25"
+ IF HIGHMEM
+ DB "H"
+ ENDIF
+ IF DSKTEST
+ DB "D"
+ ENDIF
+
+ DB 13,10
+ DB "Copyright 1981,82 Microsoft, Inc.",13,10,"$"
+ ENDIF
+
+QUIT:
+ MOV AH,0
+ JMP SHORT SAVREGS
+
+COMMAND: ;Interrupt call entry point
+ CMP AH,MAXCOM
+ JBE SAVREGS
+BADCALL:
+ MOV AL,0
+IRET: IRET
+
+ENTRY: ;System call entry point and dispatcher
+ POP AX ;IP from the long call at 5
+ POP AX ;Segment from the long call at 5
+ POP CS:[TEMP] ;IP from the CALL 5
+ PUSHF ;Start re-ordering the stack
+ CLI
+ PUSH AX ;Save segment
+ PUSH CS:[TEMP] ;Stack now ordered as if INT had been used
+ CMP CL,MAXCALL ;This entry point doesn't get as many calls
+ JA BADCALL
+ MOV AH,CL
+SAVREGS:
+ PUSH ES
+ PUSH DS
+ PUSH BP
+ PUSH DI
+ PUSH SI
+ PUSH DX
+ PUSH CX
+ PUSH BX
+ PUSH AX
+
+ IF DSKTEST
+ MOV AX,CS:[SPSAVE]
+ MOV CS:[NSP],AX
+ MOV AX,CS:[SSSAVE]
+ MOV CS:[NSS],AX
+ POP AX
+ PUSH AX
+ ENDIF
+
+ MOV CS:[SPSAVE],SP
+ MOV CS:[SSSAVE],SS
+ MOV SP,CS
+ MOV SS,SP
+REDISP:
+ MOV SP,OFFSET DOSGROUP:IOSTACK
+ STI ;Stack OK now
+ MOV BL,AH
+ MOV BH,0
+ SHL BX,1
+ CLD
+ CMP AH,12
+ JLE SAMSTK
+ MOV SP,OFFSET DOSGROUP:DSKSTACK
+SAMSTK:
+ CALL CS:[BX+DISPATCH]
+LEAVE:
+ CLI
+ MOV SP,CS:[SPSAVE]
+ MOV SS,CS:[SSSAVE]
+ MOV BP,SP
+ MOV BYTE PTR [BP.AXSAVE],AL
+
+ IF DSKTEST
+ MOV AX,CS:[NSP]
+ MOV CS:[SPSAVE],AX
+ MOV AX,CS:[NSS]
+ MOV CS:[SSSAVE],AX
+ ENDIF
+
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ POP SI
+ POP DI
+ POP BP
+ POP DS
+ POP ES
+ IRET
+; Standard Functions
+DISPATCH DW ABORT ;0
+ DW CONIN
+ DW CONOUT
+ DW READER
+ DW PUNCH
+ DW LIST ;5
+ DW RAWIO
+ DW RAWINP
+ DW IN
+ DW PRTBUF
+ DW BUFIN ;10
+ DW CONSTAT
+ DW FLUSHKB
+ DW DSKRESET
+ DW SELDSK
+ DW OPEN ;15
+ DW CLOSE
+ DW SRCHFRST
+ DW SRCHNXT
+ DW DELETE
+ DW SEQRD ;20
+ DW SEQWRT
+ DW CREATE
+ DW RENAME
+ DW INUSE
+ DW GETDRV ;25
+ DW SETDMA
+ DW GETFATPT
+ DW GETFATPTDL
+ DW GETRDONLY
+ DW SETATTRIB ;30
+ DW GETDSKPT
+ DW USERCODE
+ DW RNDRD
+ DW RNDWRT
+ DW FILESIZE ;35
+ DW SETRNDREC
+; Extended Functions
+ DW SETVECT
+ DW NEWBASE
+ DW BLKRD
+ DW BLKWRT ;40
+ DW MAKEFCB
+ DW GETDATE
+ DW SETDATE
+ DW GETTIME
+ DW SETTIME ;45
+ DW VERIFY
+
+INUSE:
+GETIO:
+SETIO:
+GETRDONLY:
+SETATTRIB:
+USERCODE:
+ MOV AL,0
+ RET
+
+VERIFY:
+ AND AL,1
+ MOV CS:VERFLG,AL
+ RET
+
+FLUSHKB:
+ PUSH AX
+ CALL FAR PTR BIOSFLUSH
+ POP AX
+ MOV AH,AL
+ CMP AL,1
+ JZ REDISPJ
+ CMP AL,6
+ JZ REDISPJ
+ CMP AL,7
+ JZ REDISPJ
+ CMP AL,8
+ JZ REDISPJ
+ CMP AL,10
+ JZ REDISPJ
+ MOV AL,0
+ RET
+
+REDISPJ:JMP REDISP
+
+READER:
+AUXIN:
+ CALL STATCHK
+ CALL FAR PTR BIOSAUXIN
+ RET
+
+PUNCH:
+ MOV AL,DL
+AUXOUT:
+ PUSH AX
+ CALL STATCHK
+ POP AX
+ CALL FAR PTR BIOSAUXOUT
+ RET
+
+
+UNPACK:
+
+; Inputs:
+; DS = CS
+; BX = Cluster number
+; BP = Base of drive parameters
+; SI = Pointer to drive FAT
+; Outputs:
+; DI = Contents of FAT for given cluster
+; Zero set means DI=0 (free cluster)
+; No other registers affected. Fatal error if cluster too big.
+
+ CMP BX,[BP.MAXCLUS]
+ JA HURTFAT
+ LEA DI,[SI+BX]
+ SHR BX,1
+ MOV DI,[DI+BX]
+ JNC HAVCLUS
+ SHR DI,1
+ SHR DI,1
+ SHR DI,1
+ SHR DI,1
+ STC
+HAVCLUS:
+ RCL BX,1
+ AND DI,0FFFH
+ RET
+HURTFAT:
+ PUSH AX
+ MOV AH,80H ;Signal Bad FAT to INT 24H handler
+ MOV DI,0FFFH ;In case INT 24H returns (it shouldn't)
+ CALL FATAL
+ POP AX ;Try to ignore bad FAT
+ RET
+
+
+PACK:
+
+; Inputs:
+; DS = CS
+; BX = Cluster number
+; DX = Data
+; SI = Pointer to drive FAT
+; Outputs:
+; The data is stored in the FAT at the given cluster.
+; BX,DX,DI all destroyed
+; No other registers affected
+
+ MOV DI,BX
+ SHR BX,1
+ ADD BX,SI
+ ADD BX,DI
+ SHR DI,1
+ MOV DI,[BX]
+ JNC ALIGNED
+ SHL DX,1
+ SHL DX,1
+ SHL DX,1
+ SHL DX,1
+ AND DI,0FH
+ JMP SHORT PACKIN
+ALIGNED:
+ AND DI,0F000H
+PACKIN:
+ OR DI,DX
+ MOV [BX],DI
+ RET
+
+DEVNAME:
+ MOV SI,OFFSET DOSGROUP:IONAME ;List of I/O devices with file names
+ MOV BH,NUMDEV ;BH = number of device names
+LOOKIO:
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,4 ;All devices are 4 letters
+ REPE CMPSB ;Check for name in list
+ JZ IOCHK ;If first 3 letters OK, check for the rest
+ ADD SI,CX ;Point to next device name
+ DEC BH
+ JNZ LOOKIO
+CRET:
+ STC ;Not found
+ RET
+
+IOCHK:
+ IF IBM
+ CMP BH,NUMDEV ;Is it the first device?
+ JNZ NOTCOM1
+ MOV BH,2 ;Make it the same as AUX
+NOTCOM1:
+ ENDIF
+ NEG BH
+ MOV CX,2 ;Check rest of name but not extension
+ MOV AX,2020H
+ REPE SCASW ;Make sure rest of name is blanks
+ JNZ CRET
+RET1: RET ;Zero set so CREATE works
+
+GETFILE:
+; Same as GETNAME except ES:DI points to FCB on successful return
+ CALL MOVNAME
+ JC RET1
+ PUSH DX
+ PUSH DS
+ CALL FINDNAME
+ POP ES
+ POP DI
+RET2: RET
+
+
+GETNAME:
+
+; Inputs:
+; DS,DX point to FCB
+; Function:
+; Find file name in disk directory. First byte is
+; drive number (0=current disk). "?" matches any
+; character.
+; Outputs:
+; Carry set if file not found
+; ELSE
+; Zero set if attributes match (always except when creating)
+; BP = Base of drive parameters
+; DS = CS
+; ES = CS
+; BX = Pointer into directory buffer
+; SI = Pointer to First Cluster field in directory entry
+; [DIRBUF] has directory record with match
+; [NAME1] has file name
+; All other registers destroyed.
+
+ CALL MOVNAME
+ JC RET2 ;Bad file name?
+FINDNAME:
+ MOV AX,CS
+ MOV DS,AX
+ CALL DEVNAME
+ JNC RET2
+ CALL STARTSRCH
+CONTSRCH:
+ CALL GETENTRY
+ JC RET2
+SRCH:
+ MOV AH,BYTE PTR [BX]
+ OR AH,AH ;End of directory?
+ JZ FREE
+ CMP AH,[DELALL] ;Free entry?
+ JZ FREE
+ MOV SI,BX
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,11
+WILDCRD:
+ REPE CMPSB
+ JZ FOUND
+ CMP BYTE PTR [DI-1],"?"
+ JZ WILDCRD
+NEXTENT:
+ CALL NEXTENTRY
+ JNC SRCH
+RET3: RET
+
+FREE:
+ CMP [ENTFREE],-1 ;Found a free entry before?
+ JNZ TSTALL ;If so, ignore this one
+ MOV CX,[LASTENT]
+ MOV [ENTFREE],CX
+TSTALL:
+ CMP AH,[DELALL] ;At end of directory?
+ JZ NEXTENT ;No - continue search
+ STC ;Report not found
+ RET
+
+FOUND:
+;Check if attributes allow finding it
+ MOV AH,[ATTRIB] ;Attributes of search
+ NOT AH
+ AND AH,[SI] ;Compare with attributes of file
+ ADD SI,15
+ AND AH,6 ;Only look at bits 1 and 2
+ JZ RET3
+ TEST BYTE PTR [CREATING],-1 ;Pass back mismatch if creating
+ JZ NEXTENT ;Otherwise continue searching
+ RET
+
+
+GETENTRY:
+
+; Inputs:
+; [LASTENT] has previously searched directory entry
+; Function:
+; Locates next sequential directory entry in preparation for search
+; Outputs:
+; Carry set if none
+; ELSE
+; AL = Current directory block
+; BX = Pointer to next directory entry in [DIRBUF]
+; DX = Pointer to first byte after end of DIRBUF
+; [LASTENT] = New directory entry number
+
+ MOV AX,[LASTENT]
+ INC AX ;Start with next entry
+ CMP AX,[BP.MAXENT]
+ JAE NONE
+GETENT:
+ MOV [LASTENT],AX
+ MOV CL,4
+ SHL AX,CL
+ XOR DX,DX
+ SHL AX,1
+ RCL DX,1 ;Account for overflow in last shift
+ MOV BX,[BP.SECSIZ]
+ AND BL,255-31 ;Must be multiple of 32
+ DIV BX
+ MOV BX,DX ;Position within sector
+ MOV AH,[BP.DEVNUM] ;AL=Directory sector no.
+ CMP AX,[DIRBUFID]
+ JZ HAVDIRBUF
+ PUSH BX
+ CALL DIRREAD
+ POP BX
+HAVDIRBUF:
+ MOV DX,OFFSET DOSGROUP:DIRBUF
+ ADD BX,DX
+ ADD DX,[BP.SECSIZ]
+ RET
+
+NEXTENTRY:
+
+; Inputs:
+; Same as outputs of GETENTRY, above
+; Function:
+; Update AL, BX, and [LASTENT] for next directory entry.
+; Carry set if no more.
+
+ MOV DI,[LASTENT]
+ INC DI
+ CMP DI,[BP.MAXENT]
+ JAE NONE
+ MOV [LASTENT],DI
+ ADD BX,32
+ CMP BX,DX
+ JB HAVIT
+ INC AL ;Next directory sector
+ PUSH DX ;Save limit
+ CALL DIRREAD
+ POP DX
+ MOV BX,OFFSET DOSGROUP:DIRBUF
+HAVIT:
+ CLC
+ RET
+
+NONE:
+ CALL CHKDIRWRITE
+ STC
+RET4: RET
+
+
+DELETE: ; System call 19
+ CALL MOVNAME
+ MOV AL,-1
+ JC RET4
+ MOV AL,CS:[ATTRIB]
+ AND AL,6 ;Look only at hidden bits
+ CMP AL,6 ;Both must be set
+ JNZ NOTALL
+ MOV CX,11
+ MOV AL,"?"
+ MOV DI,OFFSET DOSGROUP:NAME1
+ REPE SCASB ;See if name is *.*
+ JNZ NOTALL
+ MOV BYTE PTR CS:[DELALL],0 ;DEL *.* - flag deleting all
+NOTALL:
+ CALL FINDNAME
+ MOV AL,-1
+ JC RET4
+ OR BH,BH ;Check if device name
+ JS RET4 ;Can't delete I/O devices
+DELFILE:
+ MOV BYTE PTR [DIRTYDIR],-1
+ MOV AH,[DELALL]
+ MOV BYTE PTR [BX],AH
+ MOV BX,[SI]
+ MOV SI,[BP.FAT]
+ OR BX,BX
+ JZ DELNXT
+ CMP BX,[BP.MAXCLUS]
+ JA DELNXT
+ CALL RELEASE
+DELNXT:
+ CALL CONTSRCH
+ JNC DELFILE
+ CALL FATWRT
+ CALL CHKDIRWRITE
+ XOR AL,AL
+ RET
+
+
+RENAME: ;System call 23
+ CALL MOVNAME
+ JC ERRET
+ ADD SI,5
+ MOV DI,OFFSET DOSGROUP:NAME2
+ CALL LODNAME
+ JC ERRET ;Report error if second name invalid
+ CALL FINDNAME
+ JC ERRET
+ OR BH,BH ;Check if I/O device name
+ JS ERRET ;If so, can't rename it
+ MOV SI,OFFSET DOSGROUP:NAME1
+ MOV DI,OFFSET DOSGROUP:NAME3
+ MOV CX,6 ;6 words (12 bytes)--include attribute byte
+ REP MOVSW ;Copy name to search for
+RENFIL:
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV SI,OFFSET DOSGROUP:NAME2
+ MOV CX,11
+NEWNAM:
+ LODSB
+ CMP AL,"?"
+ JNZ NOCHG
+ MOV AL,[BX]
+NOCHG:
+ STOSB
+ INC BX
+ LOOP NEWNAM
+ MOV BYTE PTR [DI],6 ;Stop duplicates with any attributes
+ CALL DEVNAME ;Check if giving it a device name
+ JNC RENERR
+ PUSH [LASTENT] ;Save position of match
+ MOV [LASTENT],-1 ;Search entire directory for duplicate
+ CALL CONTSRCH ;See if new name already exists
+ POP AX
+ JNC RENERR ;Error if found
+ CALL GETENT ;Re-read matching entry
+ MOV DI,BX
+ MOV SI,OFFSET DOSGROUP:NAME1
+ MOV CX,5
+ MOVSB
+ REP MOVSW ;Replace old name with new one
+ MOV BYTE PTR [DIRTYDIR],-1 ;Flag change in directory
+ MOV SI,OFFSET DOSGROUP:NAME3
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,6 ;Include attribute byte
+ REP MOVSW ;Copy name back into search buffer
+ CALL CONTSRCH
+ JNC RENFIL
+ CALL CHKDIRWRITE
+ XOR AL,AL
+ RET
+
+RENERR:
+ CALL CHKDIRWRITE
+ERRET:
+ MOV AL,-1
+RET5: RET
+
+
+MOVNAME:
+
+; Inputs:
+; DS, DX point to FCB or extended FCB
+; Outputs:
+; DS:DX point to normal FCB
+; ES = CS
+; If file name OK:
+; BP has base of driver parameters
+; [NAME1] has name in upper case
+; All registers except DX destroyed
+; Carry set if bad file name or drive
+
+ MOV CS:WORD PTR [CREATING],0E500H ;Not creating, not DEL *.*
+ MOV AX,CS
+ MOV ES,AX
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV SI,DX
+ LODSB
+ MOV CS:[EXTFCB],AL ;Set flag if extended FCB in use
+ MOV AH,0 ;Set default attributes
+ CMP AL,-1 ;Is it an extended FCB?
+ JNZ HAVATTRB
+ ADD DX,7 ;Adjust to point to normal FCB
+ ADD SI,6 ;Point to drive select byte
+ MOV AH,[SI-1] ;Get attribute byte
+ LODSB ;Get drive select byte
+HAVATTRB:
+ MOV CS:[ATTRIB],AH ;Save attributes
+ CALL GETTHISDRV
+LODNAME:
+; This entry point copies a file name from DS,SI
+; to ES,DI converting to upper case.
+ CMP BYTE PTR [SI]," " ;Don't allow blank as first letter
+ STC ;In case of error
+ JZ RET5
+ MOV CX,11
+MOVCHK:
+ CALL GETLET
+ JB RET5
+ JNZ STOLET ;Is it a delimiter?
+ CMP AL," " ;This is the only delimiter allowed
+ STC ;In case of error
+ JNZ RET5
+STOLET:
+ STOSB
+ LOOP MOVCHK
+ CLC ;Got through whole name - no error
+RET6: RET
+
+GETTHISDRV:
+ CMP CS:[NUMDRV],AL
+ JC RET6
+ DEC AL
+ JNS PHYDRV
+ MOV AL,CS:[CURDRV]
+PHYDRV:
+ MOV CS:[THISDRV],AL
+ RET
+
+
+OPEN: ;System call 15
+ CALL GETFILE
+DOOPEN:
+; Enter here to perform OPEN on file already found
+; in directory. DS=CS, BX points to directory
+; entry in DIRBUF, SI points to First Cluster field, and
+; ES:DI point to the FCB to be opened. This entry point
+; is used by CREATE.
+ JC ERRET
+ OR BH,BH ;Check if file is I/O device
+ JS OPENDEV ;Special handler if so
+ MOV AL,[THISDRV]
+ INC AX
+ STOSB
+ XOR AX,AX
+ IF ZEROEXT
+ ADD DI,11
+ STOSW ;Zero low byte of extent field if IBM only
+ ENDIF
+ IF NOT ZEROEXT
+ ADD DI,12 ;Point to high half of CURRENT BLOCK field
+ STOSB ;Set it to zero (CP/M programs set low byte)
+ ENDIF
+ MOV AL,128 ;Default record size
+ STOSW ;Set record size
+ LODSW ;Get starting cluster
+ MOV DX,AX ;Save it for the moment
+ MOVSW ;Transfer size to FCB
+ MOVSW
+ MOV AX,[SI-8] ;Get date
+ STOSW ;Save date in FCB
+ MOV AX,[SI-10] ;Get time
+ STOSW ;Save it in FCB
+ MOV AL,[BP.DEVNUM]
+ OR AL,40H
+ STOSB
+ MOV AX,DX ;Restore starting cluster
+ STOSW ; first cluster
+ STOSW ; last cluster accessed
+ XOR AX,AX
+ STOSW ; position of last cluster
+ RET
+
+
+OPENDEV:
+ ADD DI,13 ;point to 2nd half of extent field
+ XOR AX,AX
+ STOSB ;Set it to zero
+ MOV AL,128
+ STOSW ;Set record size to 128
+ XOR AX,AX
+ STOSW
+ STOSW ;Set current size to zero
+ CALL DATE16
+ STOSW ;Date is todays
+ XCHG AX,DX
+ STOSW ;Use current time
+ MOV AL,BH ;Get device number
+ STOSB
+ XOR AL,AL ;No error
+ RET
+FATERR:
+ XCHG AX,DI ;Put error code in DI
+ MOV AH,2 ;While trying to read FAT
+ MOV AL,[THISDRV] ;Tell which drive
+ CALL FATAL1
+ JMP SHORT FATREAD
+STARTSRCH:
+ MOV AX,-1
+ MOV [LASTENT],AX
+ MOV [ENTFREE],AX
+FATREAD:
+
+; Inputs:
+; DS = CS
+; Function:
+; If disk may have been changed, FAT is read in and buffers are
+; flagged invalid. If not, no action is taken.
+; Outputs:
+; BP = Base of drive parameters
+; Carry set if invalid drive returned by MAPDEV
+; All other registers destroyed
+
+ MOV AL,[THISDRV]
+ XOR AH,AH ;Set default response to zero & clear carry
+ CALL FAR PTR BIOSDSKCHG ;See what BIOS has to say
+ JC FATERR
+ CALL GETBP
+ MOV AL,[THISDRV] ;Use physical unit number
+ MOV SI,[BP.FAT]
+ OR AH,[SI-1] ;Dirty byte for FAT
+ JS NEWDSK ;If either say new disk, then it's so
+ JNZ MAPDRV
+ MOV AH,1
+ CMP AX,WORD PTR [BUFDRVNO] ;Does buffer have dirty sector of this drive?
+ JZ MAPDRV
+NEWDSK:
+ CMP AL,[BUFDRVNO] ;See if buffer is for this drive
+ JNZ BUFOK ;If not, don't touch it
+ MOV [BUFSECNO],0 ;Flag buffers invalid
+ MOV WORD PTR [BUFDRVNO],00FFH
+BUFOK:
+ MOV [DIRBUFID],-1
+ CALL FIGFAT
+NEXTFAT:
+ PUSH AX
+ CALL DSKREAD
+ POP AX
+ JC BADFAT
+ SUB AL,[BP.FATCNT]
+ JZ NEWFAT
+ CALL FATWRT
+NEWFAT:
+ MOV SI,[BP.FAT]
+ MOV AL,[BP.DEVNUM]
+ MOV AH,[SI] ;Get first byte of FAT
+ OR AH,0F8H ;Put in range
+ CALL FAR PTR BIOSMAPDEV
+ MOV AH,0
+ MOV [SI-2],AX ;Set device no. and reset dirty bit
+MAPDRV:
+ MOV AL,[SI-2] ;Get device number
+GETBP:
+ MOV BP,[DRVTAB] ;Just in case drive isn't valid
+ AND AL,3FH ;Mask out dirty bit
+ CMP AL,[NUMIO]
+ CMC
+ JC RET7
+ PUSH AX
+ MOV AH,DPBSIZ
+ MUL AH
+ ADD BP,AX
+ POP AX
+RET7: RET
+
+BADFAT:
+ MOV CX,DI
+ ADD DX,CX
+ DEC AL
+ JNZ NEXTFAT
+ CALL FIGFAT ;Reset registers
+ CALL DREAD ;Try first FAT once more
+ JMP SHORT NEWFAT
+
+OKRET1:
+ MOV AL,0
+ RET
+
+CLOSE: ;System call 16
+ MOV DI,DX
+ CMP BYTE PTR [DI],-1 ;Check for extended FCB
+ JNZ NORMFCB3
+ ADD DI,7
+NORMFCB3:
+ TEST BYTE PTR [DI.DEVID],0C0H ;Allow only dirty files
+ JNZ OKRET1 ;can't close if I/O device, or not writen
+ MOV AL,[DI] ;Get physical unit number
+ DEC AL ;Make zero = drive A
+ MOV AH,1 ;Look for dirty buffer
+ CMP AX,CS:WORD PTR [BUFDRVNO]
+ JNZ FNDDIR
+;Write back dirty buffer if on same drive
+ PUSH DX
+ PUSH DS
+ PUSH CS
+ POP DS
+ MOV BYTE PTR [DIRTYBUF],0
+ MOV BX,[BUFFER]
+ MOV CX,1
+ MOV DX,[BUFSECNO]
+ MOV BP,[BUFDRVBP]
+ CALL DWRITE
+ POP DS
+ POP DX
+FNDDIR:
+ CALL GETFILE
+BADCLOSEJ:
+ JC BADCLOSE
+ MOV CX,ES:[DI.FIRCLUS]
+ MOV [SI],CX
+ MOV DX,ES:WORD PTR [DI.FILSIZ]
+ MOV [SI+2],DX
+ MOV DX,ES:WORD PTR [DI.FILSIZ+2]
+ MOV [SI+4],DX
+ MOV DX,ES:[DI.FDATE]
+ MOV [SI-2],DX
+ MOV DX,ES:[DI.FTIME]
+ MOV [SI-4],DX
+ CALL DIRWRITE
+
+CHKFATWRT:
+; Do FATWRT only if FAT is dirty and uses same I/O driver
+ MOV SI,[BP.FAT]
+ MOV AL,[BP.DEVNUM]
+ MOV AH,1
+ CMP [SI-2],AX ;See if FAT dirty and uses same driver
+ JNZ OKRET
+
+FATWRT:
+
+; Inputs:
+; DS = CS
+; BP = Base of drive parameter table
+; Function:
+; Write the FAT back to disk and reset FAT
+; dirty bit.
+; Outputs:
+; AL = 0
+; BP unchanged
+; All other registers destroyed
+
+ CALL FIGFAT
+ MOV BYTE PTR [BX-1],0
+EACHFAT:
+ PUSH DX
+ PUSH CX
+ PUSH BX
+ PUSH AX
+ CALL DWRITE
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ ADD DX,CX
+ DEC AL
+ JNZ EACHFAT
+OKRET:
+ MOV AL,0
+ RET
+
+BADCLOSE:
+ MOV SI,[BP.FAT]
+ MOV BYTE PTR [SI-1],0
+ MOV AL,-1
+ RET
+
+
+FIGFAT:
+; Loads registers with values needed to read or
+; write a FAT.
+ MOV AL,[BP.FATCNT]
+ MOV BX,[BP.FAT]
+ MOV CL,[BP.FATSIZ] ;No. of records occupied by FAT
+ MOV CH,0
+ MOV DX,[BP.FIRFAT] ;Record number of start of FATs
+ RET
+
+
+DIRCOMP:
+; Prepare registers for directory read or write
+ CBW
+ ADD AX,[BP.FIRDIR]
+ MOV DX,AX
+ MOV BX,OFFSET DOSGROUP:DIRBUF
+ MOV CX,1
+ RET
+
+
+CREATE: ;System call 22
+ CALL MOVNAME
+ JC ERRET3
+ MOV DI,OFFSET DOSGROUP:NAME1
+ MOV CX,11
+ MOV AL,"?"
+ REPNE SCASB
+ JZ ERRET3
+ MOV CS:BYTE PTR [CREATING],-1
+ PUSH DX
+ PUSH DS
+ CALL FINDNAME
+ JNC EXISTENT
+ MOV AX,[ENTFREE] ;First free entry found in FINDNAME
+ CMP AX,-1
+ JZ ERRPOP
+ CALL GETENT ;Point at that free entry
+ JMP SHORT FREESPOT
+ERRPOP:
+ POP DS
+ POP DX
+ERRET3:
+ MOV AL,-1
+ RET
+
+EXISTENT:
+ JNZ ERRPOP ;Error if attributes don't match
+ OR BH,BH ;Check if file is I/O device
+ JS OPENJMP ;If so, no action
+ MOV CX,[SI] ;Get pointer to clusters
+ JCXZ FREESPOT
+ CMP CX,[BP.MAXCLUS]
+ JA FREESPOT
+ PUSH BX
+ MOV BX,CX
+ MOV SI,[BP.FAT]
+ CALL RELEASE ;Free any data already allocated
+ CALL FATWRT
+ POP BX
+FREESPOT:
+ MOV DI,BX
+ MOV SI,OFFSET DOSGROUP:NAME1
+ MOV CX,5
+ MOVSB
+ REP MOVSW
+ MOV AL,[ATTRIB]
+ STOSB
+ XOR AX,AX
+ MOV CL,5
+ REP STOSW
+ CALL DATE16
+ XCHG AX,DX
+ STOSW
+ XCHG AX,DX
+ STOSW
+ XOR AX,AX
+ PUSH DI
+ MOV CL,6
+SMALLENT:
+ REP STOSB
+ PUSH BX
+ CALL DIRWRITE
+ POP BX
+ POP SI
+OPENJMP:
+ CLC ;Clear carry so OPEN won't fail
+ POP ES
+ POP DI
+ JMP DOOPEN
+
+
+DIRREAD:
+
+; Inputs:
+; DS = CS
+; AL = Directory block number
+; BP = Base of drive parameters
+; Function:
+; Read the directory block into DIRBUF.
+; Outputs:
+; AX,BP unchanged
+; All other registers destroyed.
+
+ PUSH AX
+ CALL CHKDIRWRITE
+ POP AX
+ PUSH AX
+ MOV AH,[BP.DEVNUM]
+ MOV [DIRBUFID],AX
+ CALL DIRCOMP
+ CALL DREAD
+ POP AX
+RET8: RET
+
+
+DREAD:
+
+; Inputs:
+; BX,DS = Transfer address
+; CX = Number of sectors
+; DX = Absolute record number
+; BP = Base of drive parameters
+; Function:
+; Calls BIOS to perform disk read. If BIOS reports
+; errors, will call HARDERR for further action.
+; BP preserved. All other registers destroyed.
+
+ CALL DSKREAD
+ JNC RET8
+ MOV CS:BYTE PTR [READOP],0
+ CALL HARDERR
+ CMP AL,1 ;Check for retry
+ JZ DREAD
+ RET ;Ignore otherwise
+
+
+HARDERR:
+
+;Hard disk error handler. Entry conditions:
+; DS:BX = Original disk transfer address
+; DX = Original logical sector number
+; CX = Number of sectors to go (first one gave the error)
+; AX = Hardware error code
+; DI = Original sector transfer count
+; BP = Base of drive parameters
+; [READOP] = 0 for read, 1 for write
+
+ XCHG AX,DI ;Error code in DI, count in AX
+ SUB AX,CX ;Number of sectors successfully transferred
+ ADD DX,AX ;First sector number to retry
+ PUSH DX
+ MUL [BP.SECSIZ] ;Number of bytes transferred
+ POP DX
+ ADD BX,AX ;First address for retry
+ MOV AH,0 ;Flag disk section in error
+ CMP DX,[BP.FIRFAT] ;In reserved area?
+ JB ERRINT
+ INC AH ;Flag for FAT
+ CMP DX,[BP.FIRDIR] ;In FAT?
+ JB ERRINT
+ INC AH
+ CMP DX,[BP.FIRREC] ;In directory?
+ JB ERRINT
+ INC AH ;Must be in data area
+ERRINT:
+ SHL AH,1 ;Make room for read/write bit
+ OR AH,CS:[READOP]
+FATAL:
+ MOV AL,[BP.DRVNUM] ;Get drive number
+FATAL1:
+ PUSH BP ;The only thing we preserve
+ MOV CS:[CONTSTK],SP
+ CLI ;Prepare to play with stack
+ MOV SS,CS:[SSSAVE]
+ MOV SP,CS:[SPSAVE] ;User stack pointer restored
+ INT 24H ;Fatal error interrupt vector
+ MOV CS:[SPSAVE],SP
+ MOV CS:[SSSAVE],SS
+ MOV SP,CS
+ MOV SS,SP
+ MOV SP,CS:[CONTSTK]
+ STI
+ POP BP
+ CMP AL,2
+ JZ ERROR
+ RET
+
+DSKREAD:
+ MOV AL,[BP.DEVNUM]
+ PUSH BP
+ PUSH BX
+ PUSH CX
+ PUSH DX
+ CALL FAR PTR BIOSREAD
+ POP DX
+ POP DI
+ POP BX
+ POP BP
+RET9: RET
+
+
+CHKDIRWRITE:
+ TEST BYTE PTR [DIRTYDIR],-1
+ JZ RET9
+
+DIRWRITE:
+
+; Inputs:
+; DS = CS
+; AL = Directory block number
+; BP = Base of drive parameters
+; Function:
+; Write the directory block into DIRBUF.
+; Outputs:
+; BP unchanged
+; All other registers destroyed.
+
+ MOV BYTE PTR [DIRTYDIR],0
+ MOV AL,BYTE PTR [DIRBUFID]
+ CALL DIRCOMP
+
+
+DWRITE:
+
+; Inputs:
+; BX,DS = Transfer address
+; CX = Number of sectors
+; DX = Absolute record number
+; BP = Base of drive parameters
+; Function:
+; Calls BIOS to perform disk write. If BIOS reports
+; errors, will call HARDERR for further action.
+; BP preserved. All other registers destroyed.
+
+ MOV AL,[BP.DEVNUM]
+ MOV AH,CS:VERFLG
+ PUSH BP
+ PUSH BX
+ PUSH CX
+ PUSH DX
+ CALL FAR PTR BIOSWRITE
+ POP DX
+ POP DI
+ POP BX
+ POP BP
+ JNC RET9
+ MOV CS:BYTE PTR [READOP],1
+ CALL HARDERR
+ CMP AL,1 ;Check for retry
+ JZ DWRITE
+ RET
+
+
+ABORT:
+ LDS SI,CS:DWORD PTR [SPSAVE]
+ MOV DS,[SI.CSSAVE]
+ XOR AX,AX
+ MOV ES,AX
+ MOV SI,SAVEXIT
+ MOV DI,EXIT
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ERROR:
+ MOV AX,CS
+ MOV DS,AX
+ MOV ES,AX
+ CALL WRTFATS
+ XOR AX,AX
+ CLI
+ MOV SS,[SSSAVE]
+ MOV SP,[SPSAVE]
+ MOV DS,AX
+ MOV SI,EXIT
+ MOV DI,OFFSET DOSGROUP:EXITHOLD
+ MOVSW
+ MOVSW
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ POP SI
+ POP DI
+ POP BP
+ POP DS
+ POP ES
+ STI ;Stack OK now
+ JMP CS:DWORD PTR [EXITHOLD]
+
+
+SEQRD: ;System call 20
+ CALL GETREC
+ CALL LOAD
+ JMP SHORT FINSEQ
+
+SEQWRT: ;System call 21
+ CALL GETREC
+ CALL STORE
+FINSEQ:
+ JCXZ SETNREX
+ ADD AX,1
+ ADC DX,0
+ JMP SHORT SETNREX
+
+RNDRD: ;System call 33
+ CALL GETRRPOS1
+ CALL LOAD
+ JMP SHORT FINRND
+
+RNDWRT: ;System call 34
+ CALL GETRRPOS1
+ CALL STORE
+ JMP SHORT FINRND
+
+BLKRD: ;System call 39
+ CALL GETRRPOS
+ CALL LOAD
+ JMP SHORT FINBLK
+
+BLKWRT: ;System call 40
+ CALL GETRRPOS
+ CALL STORE
+FINBLK:
+ LDS SI,DWORD PTR [SPSAVE]
+ MOV [SI.CXSAVE],CX
+ JCXZ FINRND
+ ADD AX,1
+ ADC DX,0
+FINRND:
+ MOV ES:WORD PTR [DI.RR],AX
+ MOV ES:[DI.RR+2],DL
+ OR DH,DH
+ JZ SETNREX
+ MOV ES:[DI.RR+3],DH ;Save 4 byte of RECPOS only if significant
+SETNREX:
+ MOV CX,AX
+ AND AL,7FH
+ MOV ES:[DI.NR],AL
+ AND CL,80H
+ SHL CX,1
+ RCL DX,1
+ MOV AL,CH
+ MOV AH,DL
+ MOV ES:[DI.EXTENT],AX
+ MOV AL,CS:[DSKERR]
+ RET
+
+GETRRPOS1:
+ MOV CX,1
+GETRRPOS:
+ MOV DI,DX
+ CMP BYTE PTR [DI],-1
+ JNZ NORMFCB1
+ ADD DI,7
+NORMFCB1:
+ MOV AX,WORD PTR [DI.RR]
+ MOV DX,WORD PTR [DI.RR+2]
+ RET
+
+NOFILERR:
+ XOR CX,CX
+ MOV BYTE PTR [DSKERR],4
+ POP BX
+ RET
+
+SETUP:
+
+; Inputs:
+; DS:DI point to FCB
+; DX:AX = Record position in file of disk transfer
+; CX = Record count
+; Outputs:
+; DS = CS
+; ES:DI point to FCB
+; BL = DEVID from FCB
+; CX = No. of bytes to transfer
+; BP = Base of drive parameters
+; SI = FAT pointer
+; [RECCNT] = Record count
+; [RECPOS] = Record position in file
+; [FCB] = DI
+; [NEXTADD] = Displacement of disk transfer within segment
+; [SECPOS] = Position of first sector
+; [BYTPOS] = Byte position in file
+; [BYTSECPOS] = Byte position in first sector
+; [CLUSNUM] = First cluster
+; [SECCLUSPOS] = Sector within first cluster
+; [DSKERR] = 0 (no errors yet)
+; [TRANS] = 0 (No transfers yet)
+; [THISDRV] = Physical drive unit number
+; If SETUP detects no records will be transfered, it returns 1 level up
+; with CX = 0.
+
+ PUSH AX
+ MOV AL,[DI]
+ DEC AL
+ MOV CS:[THISDRV],AL
+ MOV AL,[DI.DEVID]
+ MOV SI,[DI.RECSIZ]
+ OR SI,SI
+ JNZ HAVRECSIZ
+ MOV SI,128
+ MOV [DI.RECSIZ],SI
+HAVRECSIZ:
+ PUSH DS
+ POP ES ;Set ES to DS
+ PUSH CS
+ POP DS ;Set DS to CS
+ OR AL,AL ;Is it a device?
+ JNS NOTDEVICE
+ MOV AL,0 ;Fake in drive 0 so we can get SP
+NOTDEVICE:
+ CALL GETBP
+ POP AX
+ JC NOFILERR
+ CMP SI,64 ;Check if highest byte of RECPOS is significant
+ JB SMALREC
+ MOV DH,0 ;Ignore MSB if record >= 64 bytes
+SMALREC:
+ MOV [RECCNT],CX
+ MOV WORD PTR [RECPOS],AX
+ MOV WORD PTR [RECPOS+2],DX
+ MOV [FCB],DI
+ MOV BX,[DMAADD]
+ MOV [NEXTADD],BX
+ MOV BYTE PTR [DSKERR],0
+ MOV BYTE PTR [TRANS],0
+ MOV BX,DX
+ MUL SI
+ MOV WORD PTR [BYTPOS],AX
+ PUSH DX
+ MOV AX,BX
+ MUL SI
+ POP BX
+ ADD AX,BX
+ ADC DX,0 ;Ripple carry
+ JNZ EOFERR
+ MOV WORD PTR [BYTPOS+2],AX
+ MOV DX,AX
+ MOV AX,WORD PTR [BYTPOS]
+ MOV BX,[BP.SECSIZ]
+ CMP DX,BX ;See if divide will overflow
+ JNC EOFERR
+ DIV BX
+ MOV [SECPOS],AX
+ MOV [BYTSECPOS],DX
+ MOV DX,AX
+ AND AL,[BP.CLUSMSK]
+ MOV [SECCLUSPOS],AL
+ MOV AX,CX ;Record count
+ MOV CL,[BP.CLUSSHFT]
+ SHR DX,CL
+ MOV [CLUSNUM],DX
+ MUL SI ;Multiply by bytes per record
+ MOV CX,AX
+ ADD AX,[DMAADD] ;See if it will fit in one segment
+ ADC DX,0
+ JZ OK ;Must be less than 64K
+ MOV AX,[DMAADD]
+ NEG AX ;Amount of room left in segment
+ JNZ PARTSEG ;All 64K available?
+ DEC AX ;If so, reduce by one
+PARTSEG:
+ XOR DX,DX
+ DIV SI ;How many records will fit?
+ MOV [RECCNT],AX
+ MUL SI ;Translate that back into bytes
+ MOV BYTE PTR [DSKERR],2 ;Flag that trimming took place
+ MOV CX,AX
+ JCXZ NOROOM
+OK:
+ MOV BL,ES:[DI.DEVID]
+ MOV SI,[BP.FAT]
+ RET
+
+EOFERR:
+ MOV BYTE PTR [DSKERR],1
+ XOR CX,CX
+NOROOM:
+ POP BX ;Kill return address
+ RET
+
+BREAKDOWN:
+
+;Inputs:
+; DS = CS
+; CX = Length of disk transfer in bytes
+; BP = Base of drive parameters
+; [BYTSECPOS] = Byte position witin first sector
+;Outputs:
+; [BYTCNT1] = Bytes to transfer in first sector
+; [SECCNT] = No. of whole sectors to transfer
+; [BYTCNT2] = Bytes to transfer in last sector
+;AX, BX, DX destroyed. No other registers affected.
+
+ MOV AX,[BYTSECPOS]
+ MOV BX,CX
+ OR AX,AX
+ JZ SAVFIR ;Partial first sector?
+ SUB AX,[BP.SECSIZ]
+ NEG AX ;Max number of bytes left in first sector
+ SUB BX,AX ;Subtract from total length
+ JAE SAVFIR
+ ADD AX,BX ;Don't use all of the rest of the sector
+ XOR BX,BX ;And no bytes are left
+SAVFIR:
+ MOV [BYTCNT1],AX
+ MOV AX,BX
+ XOR DX,DX
+ DIV [BP.SECSIZ] ;How many whole sectors?
+ MOV [SECCNT],AX
+ MOV [BYTCNT2],DX ;Bytes remaining for last sector
+RET10: RET
+
+
+FNDCLUS:
+
+; Inputs:
+; DS = CS
+; CX = No. of clusters to skip
+; BP = Base of drive parameters
+; SI = FAT pointer
+; ES:DI point to FCB
+; Outputs:
+; BX = Last cluster skipped to
+; CX = No. of clusters remaining (0 unless EOF)
+; DX = Position of last cluster
+; DI destroyed. No other registers affected.
+
+ MOV BX,ES:[DI.LSTCLUS]
+ MOV DX,ES:[DI.CLUSPOS]
+ OR BX,BX
+ JZ NOCLUS
+ SUB CX,DX
+ JNB FINDIT
+ ADD CX,DX
+ XOR DX,DX
+ MOV BX,ES:[DI.FIRCLUS]
+FINDIT:
+ JCXZ RET10
+SKPCLP:
+ CALL UNPACK
+ CMP DI,0FF8H
+ JAE RET10
+ XCHG BX,DI
+ INC DX
+ LOOP SKPCLP
+ RET
+NOCLUS:
+ INC CX
+ DEC DX
+ RET
+
+
+BUFSEC:
+; Inputs:
+; AL = 0 if buffer must be read, 1 if no pre-read needed
+; BP = Base of drive parameters
+; [CLUSNUM] = Physical cluster number
+; [SECCLUSPOS] = Sector position of transfer within cluster
+; [BYTCNT1] = Size of transfer
+; Function:
+; Insure specified sector is in buffer, flushing buffer before
+; read if necessary.
+; Outputs:
+; SI = Pointer to buffer
+; DI = Pointer to transfer address
+; CX = Number of bytes
+; [NEXTADD] updated
+; [TRANS] set to indicate a transfer will occur
+
+ MOV DX,[CLUSNUM]
+ MOV BL,[SECCLUSPOS]
+ CALL FIGREC
+ MOV [PREREAD],AL
+ CMP DX,[BUFSECNO]
+ JNZ GETSEC
+ MOV AL,[BUFDRVNO]
+ CMP AL,[THISDRV]
+ JZ FINBUF ;Already have it?
+GETSEC:
+ XOR AL,AL
+ XCHG [DIRTYBUF],AL ;Read dirty flag and reset it
+ OR AL,AL
+ JZ RDSEC
+ PUSH DX
+ PUSH BP
+ MOV BP,[BUFDRVBP]
+ MOV BX,[BUFFER]
+ MOV CX,1
+ MOV DX,[BUFSECNO]
+ CALL DWRITE
+ POP BP
+ POP DX
+RDSEC:
+ TEST BYTE PTR [PREREAD],-1
+ JNZ SETBUF
+ XOR AX,AX
+ MOV [BUFSECNO],AX ;Set buffer valid in case of disk error
+ DEC AX
+ MOV [BUFDRVNO],AL
+ MOV BX,[BUFFER]
+ MOV CX,1
+ PUSH DX
+ CALL DREAD
+ POP DX
+SETBUF:
+ MOV [BUFSECNO],DX
+ MOV AL,[THISDRV]
+ MOV [BUFDRVNO],AL
+ MOV [BUFDRVBP],BP
+FINBUF:
+ MOV BYTE PTR [TRANS],1 ;A transfer is taking place
+ MOV DI,[NEXTADD]
+ MOV SI,DI
+ MOV CX,[BYTCNT1]
+ ADD SI,CX
+ MOV [NEXTADD],SI
+ MOV SI,[BUFFER]
+ ADD SI,[BYTSECPOS]
+ RET
+
+BUFRD:
+ XOR AL,AL ;Pre-read necessary
+ CALL BUFSEC
+ PUSH ES
+ MOV ES,[DMAADD+2]
+ SHR CX,1
+ JNC EVENRD
+ MOVSB
+EVENRD:
+ REP MOVSW
+ POP ES
+ RET
+
+BUFWRT:
+ MOV AX,[SECPOS]
+ INC AX ;Set for next sector
+ MOV [SECPOS],AX
+ CMP AX,[VALSEC] ;Has sector been written before?
+ MOV AL,1
+ JA NOREAD ;Skip preread if SECPOS>VALSEC
+ MOV AL,0
+NOREAD:
+ CALL BUFSEC
+ XCHG DI,SI
+ PUSH DS
+ PUSH ES
+ PUSH CS
+ POP ES
+ MOV DS,[DMAADD+2]
+ SHR CX,1
+ JNC EVENWRT
+ MOVSB
+EVENWRT:
+ REP MOVSW
+ POP ES
+ POP DS
+ MOV BYTE PTR [DIRTYBUF],1
+ RET
+
+NEXTSEC:
+ TEST BYTE PTR [TRANS],-1
+ JZ CLRET
+ MOV AL,[SECCLUSPOS]
+ INC AL
+ CMP AL,[BP.CLUSMSK]
+ JBE SAVPOS
+ MOV BX,[CLUSNUM]
+ CMP BX,0FF8H
+ JAE NONEXT
+ MOV SI,[BP.FAT]
+ CALL UNPACK
+ MOV [CLUSNUM],DI
+ INC [LASTPOS]
+ MOV AL,0
+SAVPOS:
+ MOV [SECCLUSPOS],AL
+CLRET:
+ CLC
+ RET
+NONEXT:
+ STC
+ RET
+
+TRANBUF:
+ LODSB
+ STOSB
+ CMP AL,13 ;Check for carriage return
+ JNZ NORMCH
+ MOV BYTE PTR [SI],10
+NORMCH:
+ CMP AL,10
+ LOOPNZ TRANBUF
+ JNZ ENDRDCON
+ CALL OUT ;Transmit linefeed
+ XOR SI,SI
+ OR CX,CX
+ JNZ GETBUF
+ OR AL,1 ;Clear zero flag--not end of file
+ENDRDCON:
+ MOV [CONTPOS],SI
+ENDRDDEV:
+ MOV [NEXTADD],DI
+ POP ES
+ JNZ SETFCBJ ;Zero set if Ctrl-Z found in input
+ MOV DI,[FCB]
+ AND ES:BYTE PTR [DI.DEVID],0FFH-40H ;Mark as no more data available
+SETFCBJ:
+ JMP SETFCB
+
+READDEV:
+ PUSH ES
+ LES DI,DWORD PTR [DMAADD]
+ INC BL
+ JZ READCON
+ INC BL
+ JNZ ENDRDDEV
+READAUX:
+ CALL AUXIN
+ STOSB
+ CMP AL,1AH
+ LOOPNZ READAUX
+ JMP SHORT ENDRDDEV
+
+READCON:
+ PUSH CS
+ POP DS
+ MOV SI,[CONTPOS]
+ OR SI,SI
+ JNZ TRANBUF
+ CMP BYTE PTR [CONBUF],128
+ JZ GETBUF
+ MOV WORD PTR [CONBUF],0FF80H ;Set up 128-byte buffer with no template
+GETBUF:
+ PUSH CX
+ PUSH ES
+ PUSH DI
+ MOV DX,OFFSET DOSGROUP:CONBUF
+ CALL BUFIN ;Get input buffer
+ POP DI
+ POP ES
+ POP CX
+ MOV SI,2 + OFFSET DOSGROUP:CONBUF
+ CMP BYTE PTR [SI],1AH ;Check for Ctrl-Z in first character
+ JNZ TRANBUF
+ MOV AL,1AH
+ STOSB
+ MOV AL,10
+ CALL OUT ;Send linefeed
+ XOR SI,SI
+ JMP SHORT ENDRDCON
+
+RDERR:
+ XOR CX,CX
+ JMP WRTERR
+
+RDLASTJ:JMP RDLAST
+
+LOAD:
+
+; Inputs:
+; DS:DI point to FCB
+; DX:AX = Position in file to read
+; CX = No. of records to read
+; Outputs:
+; DX:AX = Position of last record read
+; CX = No. of bytes read
+; ES:DI point to FCB
+; LSTCLUS, CLUSPOS fields in FCB set
+
+ CALL SETUP
+ OR BL,BL ;Check for named device I/O
+ JS READDEV
+ MOV AX,ES:WORD PTR [DI.FILSIZ]
+ MOV BX,ES:WORD PTR [DI.FILSIZ+2]
+ SUB AX,WORD PTR [BYTPOS]
+ SBB BX,WORD PTR [BYTPOS+2]
+ JB RDERR
+ JNZ ENUF
+ OR AX,AX
+ JZ RDERR
+ CMP AX,CX
+ JAE ENUF
+ MOV CX,AX
+ENUF:
+ CALL BREAKDOWN
+ MOV CX,[CLUSNUM]
+ CALL FNDCLUS
+ OR CX,CX
+ JNZ RDERR
+ MOV [LASTPOS],DX
+ MOV [CLUSNUM],BX
+ CMP [BYTCNT1],0
+ JZ RDMID
+ CALL BUFRD
+RDMID:
+ CMP [SECCNT],0
+ JZ RDLASTJ
+ CALL NEXTSEC
+ JC SETFCB
+ MOV BYTE PTR [TRANS],1 ;A transfer is taking place
+ONSEC:
+ MOV DL,[SECCLUSPOS]
+ MOV CX,[SECCNT]
+ MOV BX,[CLUSNUM]
+RDLP:
+ CALL OPTIMIZE
+ PUSH DI
+ PUSH AX
+ PUSH DS
+ MOV DS,[DMAADD+2]
+ PUSH DX
+ PUSH BX
+ PUSHF ;Save carry flag
+ CALL DREAD
+ POPF ;Restore carry flag
+ POP DI ;Initial transfer address
+ POP AX ;First sector transfered
+ POP DS
+ JC NOTBUFFED ;Was one of those sectors in the buffer?
+ CMP BYTE PTR [DIRTYBUF],0 ;Is buffer dirty?
+ JZ NOTBUFFED ;If not no problem
+;We have transfered in a sector from disk when a dirty copy of it is in the buffer.
+;We must transfer the sector from the buffer to correct memory address
+ SUB AX,[BUFSECNO] ;How many sectors into the transfer?
+ NEG AX
+ MOV CX,[BP.SECSIZ]
+ MUL CX ;How many bytes into the transfer?
+ ADD DI,AX
+ MOV SI,[BUFFER]
+ PUSH ES
+ MOV ES,[DMAADD+2] ;Get disk transfer segment
+ SHR CX,1
+ REP MOVSW
+ JNC EVENMOV
+ MOVSB
+EVENMOV:
+ POP ES
+NOTBUFFED:
+ POP CX
+ POP BX
+ JCXZ RDLAST
+ CMP BX,0FF8H
+ JAE SETFCB
+ MOV DL,0
+ INC [LASTPOS] ;We'll be using next cluster
+ JMP SHORT RDLP
+
+SETFCB:
+ MOV SI,[FCB]
+ MOV AX,[NEXTADD]
+ MOV DI,AX
+ SUB AX,[DMAADD] ;Number of bytes transfered
+ XOR DX,DX
+ MOV CX,ES:[SI.RECSIZ]
+ DIV CX ;Number of records
+ CMP AX,[RECCNT] ;Check if all records transferred
+ JZ FULLREC
+ MOV BYTE PTR [DSKERR],1
+ OR DX,DX
+ JZ FULLREC ;If remainder 0, then full record transfered
+ MOV BYTE PTR [DSKERR],3 ;Flag partial last record
+ SUB CX,DX ;Bytes left in last record
+ PUSH ES
+ MOV ES,[DMAADD+2]
+ XCHG AX,BX ;Save the record count temporarily
+ XOR AX,AX ;Fill with zeros
+ SHR CX,1
+ JNC EVENFIL
+ STOSB
+EVENFIL:
+ REP STOSW
+ XCHG AX,BX ;Restore record count to AX
+ POP ES
+ INC AX ;Add last (partial) record to total
+FULLREC:
+ MOV CX,AX
+ MOV DI,SI ;ES:DI point to FCB
+SETCLUS:
+ MOV AX,[CLUSNUM]
+ MOV ES:[DI.LSTCLUS],AX
+ MOV AX,[LASTPOS]
+ MOV ES:[DI.CLUSPOS],AX
+ADDREC:
+ MOV AX,WORD PTR [RECPOS]
+ MOV DX,WORD PTR [RECPOS+2]
+ JCXZ RET28 ;If no records read, don't change position
+ DEC CX
+ ADD AX,CX ;Update current record position
+ ADC DX,0
+ INC CX
+RET28: RET
+
+RDLAST:
+ MOV AX,[BYTCNT2]
+ OR AX,AX
+ JZ SETFCB
+ MOV [BYTCNT1],AX
+ CALL NEXTSEC
+ JC SETFCB
+ MOV [BYTSECPOS],0
+ CALL BUFRD
+ JMP SHORT SETFCB
+
+WRTDEV:
+ PUSH DS
+ LDS SI,DWORD PTR [DMAADD]
+ OR BL,40H
+ INC BL
+ JZ WRTCON
+ INC BL
+ JZ WRTAUX
+ INC BL
+ JZ ENDWRDEV ;Done if device is NUL
+WRTLST:
+ LODSB
+ CMP AL,1AH
+ JZ ENDWRDEV
+ CALL LISTOUT
+ LOOP WRTLST
+ JMP SHORT ENDWRDEV
+
+WRTAUX:
+ LODSB
+ CALL AUXOUT
+ CMP AL,1AH
+ LOOPNZ WRTAUX
+ JMP SHORT ENDWRDEV
+
+WRTCON:
+ LODSB
+ CMP AL,1AH
+ JZ ENDWRDEV
+ CALL OUT
+ LOOP WRTCON
+ENDWRDEV:
+ POP DS
+ MOV CX,[RECCNT]
+ MOV DI,[FCB]
+ JMP SHORT ADDREC
+
+HAVSTART:
+ MOV CX,AX
+ CALL SKPCLP
+ JCXZ DOWRTJ
+ CALL ALLOCATE
+ JNC DOWRTJ
+WRTERR:
+ MOV BYTE PTR [DSKERR],1
+LVDSK:
+ MOV AX,WORD PTR [RECPOS]
+ MOV DX,WORD PTR [RECPOS+2]
+ MOV DI,[FCB]
+ RET
+
+DOWRTJ: JMP DOWRT
+
+WRTEOFJ:
+ JMP WRTEOF
+
+STORE:
+
+; Inputs:
+; DS:DI point to FCB
+; DX:AX = Position in file of disk transfer
+; CX = Record count
+; Outputs:
+; DX:AX = Position of last record written
+; CX = No. of records written
+; ES:DI point to FCB
+; LSTCLUS, CLUSPOS fields in FCB set
+
+ CALL SETUP
+ CALL DATE16
+ MOV ES:[DI.FDATE],AX
+ MOV ES:[DI.FTIME],DX
+ OR BL,BL
+ JS WRTDEV
+ AND BL,3FH ;Mark file as dirty
+ MOV ES:[DI.DEVID],BL
+ CALL BREAKDOWN
+ MOV AX,WORD PTR [BYTPOS]
+ MOV DX,WORD PTR [BYTPOS+2]
+ JCXZ WRTEOFJ
+ DEC CX
+ ADD AX,CX
+ ADC DX,0 ;AX:DX=last byte accessed
+ DIV [BP.SECSIZ] ;AX=last sector accessed
+ MOV CL,[BP.CLUSSHFT]
+ SHR AX,CL ;Last cluster to be accessed
+ PUSH AX
+ MOV AX,ES:WORD PTR [DI.FILSIZ]
+ MOV DX,ES:WORD PTR [DI.FILSIZ+2]
+ DIV [BP.SECSIZ]
+ OR DX,DX
+ JZ NORNDUP
+ INC AX ;Round up if any remainder
+NORNDUP:
+ MOV [VALSEC],AX ;Number of sectors that have been written
+ POP AX
+ MOV CX,[CLUSNUM] ;First cluster accessed
+ CALL FNDCLUS
+ MOV [CLUSNUM],BX
+ MOV [LASTPOS],DX
+ SUB AX,DX ;Last cluster minus current cluster
+ JZ DOWRT ;If we have last clus, we must have first
+ JCXZ HAVSTART ;See if no more data
+ PUSH CX ;No. of clusters short of first
+ MOV CX,AX
+ CALL ALLOCATE
+ POP AX
+ JC WRTERR
+ MOV CX,AX
+ MOV DX,[LASTPOS]
+ INC DX
+ DEC CX
+ JZ NOSKIP
+ CALL SKPCLP
+NOSKIP:
+ MOV [CLUSNUM],BX
+ MOV [LASTPOS],DX
+DOWRT:
+ CMP [BYTCNT1],0
+ JZ WRTMID
+ MOV BX,[CLUSNUM]
+ CALL BUFWRT
+WRTMID:
+ MOV AX,[SECCNT]
+ OR AX,AX
+ JZ WRTLAST
+ ADD [SECPOS],AX
+ CALL NEXTSEC
+ MOV BYTE PTR [TRANS],1 ;A transfer is taking place
+ MOV DL,[SECCLUSPOS]
+ MOV BX,[CLUSNUM]
+ MOV CX,[SECCNT]
+WRTLP:
+ CALL OPTIMIZE
+ JC NOTINBUF ;Is one of the sectors buffered?
+ MOV [BUFSECNO],0 ;If so, invalidate the buffer since we're
+ MOV WORD PTR [BUFDRVNO],0FFH ; completely rewritting it
+NOTINBUF:
+ PUSH DI
+ PUSH AX
+ PUSH DS
+ MOV DS,[DMAADD+2]
+ CALL DWRITE
+ POP DS
+ POP CX
+ POP BX
+ JCXZ WRTLAST
+ MOV DL,0
+ INC [LASTPOS] ;We'll be using next cluster
+ JMP SHORT WRTLP
+WRTLAST:
+ MOV AX,[BYTCNT2]
+ OR AX,AX
+ JZ FINWRT
+ MOV [BYTCNT1],AX
+ CALL NEXTSEC
+ MOV [BYTSECPOS],0
+ CALL BUFWRT
+FINWRT:
+ MOV AX,[NEXTADD]
+ SUB AX,[DMAADD]
+ ADD AX,WORD PTR [BYTPOS]
+ MOV DX,WORD PTR [BYTPOS+2]
+ ADC DX,0
+ MOV CX,DX
+ MOV DI,[FCB]
+ CMP AX,ES:WORD PTR [DI.FILSIZ]
+ SBB CX,ES:WORD PTR [DI.FILSIZ+2]
+ JB SAMSIZ
+ MOV ES:WORD PTR [DI.FILSIZ],AX
+ MOV ES:WORD PTR [DI.FILSIZ+2],DX
+SAMSIZ:
+ MOV CX,[RECCNT]
+ JMP SETCLUS
+
+
+WRTERRJ:JMP WRTERR
+
+WRTEOF:
+ MOV CX,AX
+ OR CX,DX
+ JZ KILLFIL
+ SUB AX,1
+ SBB DX,0
+ DIV [BP.SECSIZ]
+ MOV CL,[BP.CLUSSHFT]
+ SHR AX,CL
+ MOV CX,AX
+ CALL FNDCLUS
+ JCXZ RELFILE
+ CALL ALLOCATE
+ JC WRTERRJ
+UPDATE:
+ MOV DI,[FCB]
+ MOV AX,WORD PTR [BYTPOS]
+ MOV ES:WORD PTR [DI.FILSIZ],AX
+ MOV AX,WORD PTR [BYTPOS+2]
+ MOV ES:WORD PTR [DI.FILSIZ+2],AX
+ XOR CX,CX
+ JMP ADDREC
+
+RELFILE:
+ MOV DX,0FFFH
+ CALL RELBLKS
+SETDIRT:
+ MOV BYTE PTR [SI-1],1
+ JMP SHORT UPDATE
+
+KILLFIL:
+ XOR BX,BX
+ XCHG BX,ES:[DI.FIRCLUS]
+ OR BX,BX
+ JZ UPDATE
+ CALL RELEASE
+ JMP SHORT SETDIRT
+
+
+OPTIMIZE:
+
+; Inputs:
+; DS = CS
+; BX = Physical cluster
+; CX = No. of records
+; DL = sector within cluster
+; BP = Base of drives parameters
+; [NEXTADD] = transfer address
+; Outputs:
+; AX = No. of records remaining
+; BX = Transfer address
+; CX = No. or records to be transferred
+; DX = Physical sector address
+; DI = Next cluster
+; Carry clear if a sector to transfer is in the buffer
+; Carry set otherwise
+; [CLUSNUM] = Last cluster accessed
+; [NEXTADD] updated
+; BP unchanged. Note that segment of transfer not set.
+
+ PUSH DX
+ PUSH BX
+ MOV AL,[BP.CLUSMSK]
+ INC AL ;Number of sectors per cluster
+ MOV AH,AL
+ SUB AL,DL ;AL = Number of sectors left in first cluster
+ MOV DX,CX
+ MOV SI,[BP.FAT]
+ MOV CX,0
+OPTCLUS:
+;AL has number of sectors available in current cluster
+;AH has number of sectors available in next cluster
+;BX has current physical cluster
+;CX has number of sequential sectors found so far
+;DX has number of sectors left to transfer
+;SI has FAT pointer
+ CALL UNPACK
+ ADD CL,AL
+ ADC CH,0
+ CMP CX,DX
+ JAE BLKDON
+ MOV AL,AH
+ INC BX
+ CMP DI,BX
+ JZ OPTCLUS
+ DEC BX
+FINCLUS:
+ MOV [CLUSNUM],BX ;Last cluster accessed
+ SUB DX,CX ;Number of sectors still needed
+ PUSH DX
+ MOV AX,CX
+ MUL [BP.SECSIZ] ;Number of sectors times sector size
+ MOV SI,[NEXTADD]
+ ADD AX,SI ;Adjust by size of transfer
+ MOV [NEXTADD],AX
+ POP AX ;Number of sectors still needed
+ POP DX ;Starting cluster
+ SUB BX,DX ;Number of new clusters accessed
+ ADD [LASTPOS],BX
+ POP BX ;BL = sector postion within cluster
+ CALL FIGREC
+ MOV BX,SI
+;Now let's see if any of these sectors are already in the buffer
+ CMP [BUFSECNO],DX
+ JC RET100 ;If DX > [BUFSECNO] then not in buffer
+ MOV SI,DX
+ ADD SI,CX ;Last sector + 1
+ CMP [BUFSECNO],SI
+ CMC
+ JC RET100 ;If SI <= [BUFSECNO] then not in buffer
+ PUSH AX
+ MOV AL,[BP.DEVNUM]
+ CMP AL,[BUFDRVNO] ;Is buffer for this drive?
+ POP AX
+ JZ RET100 ;If so, then we match
+ STC ;No match
+RET100: RET
+BLKDON:
+ SUB CX,DX ;Number of sectors in cluster we don't want
+ SUB AH,CL ;Number of sectors in cluster we accepted
+ DEC AH ;Adjust to mean position within cluster
+ MOV [SECCLUSPOS],AH
+ MOV CX,DX ;Anyway, make the total equal to the request
+ JMP SHORT FINCLUS
+
+
+FIGREC:
+
+;Inputs:
+; DX = Physical cluster number
+; BL = Sector postion within cluster
+; BP = Base of drive parameters
+;Outputs:
+; DX = physical sector number
+;No other registers affected.
+
+ PUSH CX
+ MOV CL,[BP.CLUSSHFT]
+ DEC DX
+ DEC DX
+ SHL DX,CL
+ OR DL,BL
+ ADD DX,[BP.FIRREC]
+ POP CX
+ RET
+
+GETREC:
+
+; Inputs:
+; DS:DX point to FCB
+; Outputs:
+; CX = 1
+; DX:AX = Record number determined by EXTENT and NR fields
+; DS:DI point to FCB
+; No other registers affected.
+
+ MOV DI,DX
+ CMP BYTE PTR [DI],-1 ;Check for extended FCB
+ JNZ NORMFCB2
+ ADD DI,7
+NORMFCB2:
+ MOV CX,1
+ MOV AL,[DI.NR]
+ MOV DX,[DI.EXTENT]
+ SHL AL,1
+ SHR DX,1
+ RCR AL,1
+ MOV AH,DL
+ MOV DL,DH
+ MOV DH,0
+ RET
+
+
+ALLOCATE:
+
+; Inputs:
+; DS = CS
+; ES = Segment of FCB
+; BX = Last cluster of file (0 if null file)
+; CX = No. of clusters to allocate
+; DX = Position of cluster BX
+; BP = Base of drive parameters
+; SI = FAT pointer
+; [FCB] = Displacement of FCB within segment
+; Outputs:
+; IF insufficient space
+; THEN
+; Carry set
+; CX = max. no. of records that could be added to file
+; ELSE
+; Carry clear
+; BX = First cluster allocated
+; FAT is fully updated including dirty bit
+; FIRCLUS field of FCB set if file was null
+; SI,BP unchanged. All other registers destroyed.
+
+ PUSH [SI]
+ PUSH DX
+ PUSH CX
+ PUSH BX
+ MOV AX,BX
+ALLOC:
+ MOV DX,BX
+FINDFRE:
+ INC BX
+ CMP BX,[BP.MAXCLUS]
+ JLE TRYOUT
+ CMP AX,1
+ JG TRYIN
+ POP BX
+ MOV DX,0FFFH
+ CALL RELBLKS
+ POP AX ;No. of clusters requested
+ SUB AX,CX ;AX=No. of clusters allocated
+ POP DX
+ POP [SI]
+ INC DX ;Position of first cluster allocated
+ ADD AX,DX ;AX=max no. of cluster in file
+ MOV DL,[BP.CLUSMSK]
+ MOV DH,0
+ INC DX ;DX=records/cluster
+ MUL DX ;AX=max no. of records in file
+ MOV CX,AX
+ SUB CX,WORD PTR [RECPOS] ;CX=max no. of records that could be written
+ JA MAXREC
+ XOR CX,CX ;If CX was negative, zero it
+MAXREC:
+ STC
+RET11: RET
+
+TRYOUT:
+ CALL UNPACK
+ JZ HAVFRE
+TRYIN:
+ DEC AX
+ JLE FINDFRE
+ XCHG AX,BX
+ CALL UNPACK
+ JZ HAVFRE
+ XCHG AX,BX
+ JMP SHORT FINDFRE
+HAVFRE:
+ XCHG BX,DX
+ MOV AX,DX
+ CALL PACK
+ MOV BX,AX
+ LOOP ALLOC
+ MOV DX,0FFFH
+ CALL PACK
+ MOV BYTE PTR [SI-1],1
+ POP BX
+ POP CX ;Don't need this stuff since we're successful
+ POP DX
+ CALL UNPACK
+ POP [SI]
+ XCHG BX,DI
+ OR DI,DI
+ JNZ RET11
+ MOV DI,[FCB]
+ MOV ES:[DI.FIRCLUS],BX
+RET12: RET
+
+
+RELEASE:
+
+; Inputs:
+; DS = CS
+; BX = Cluster in file
+; SI = FAT pointer
+; BP = Base of drive parameters
+; Function:
+; Frees cluster chain starting with [BX]
+; AX,BX,DX,DI all destroyed. Other registers unchanged.
+
+ XOR DX,DX
+RELBLKS:
+; Enter here with DX=0FFFH to put an end-of-file mark
+; in the first cluster and free the rest in the chain.
+ CALL UNPACK
+ JZ RET12
+ MOV AX,DI
+ CALL PACK
+ CMP AX,0FF8H
+ MOV BX,AX
+ JB RELEASE
+RET13: RET
+
+
+GETEOF:
+
+; Inputs:
+; BX = Cluster in a file
+; SI = Base of drive FAT
+; DS = CS
+; Outputs:
+; BX = Last cluster in the file
+; DI destroyed. No other registers affected.
+
+ CALL UNPACK
+ CMP DI,0FF8H
+ JAE RET13
+ MOV BX,DI
+ JMP SHORT GETEOF
+
+
+SRCHFRST: ;System call 17
+ CALL GETFILE
+SAVPLCE:
+; Search-for-next enters here to save place and report
+; findings.
+ JC KILLSRCH
+ OR BH,BH
+ JS SRCHDEV
+ MOV AX,[LASTENT]
+ MOV ES:[DI.FILDIRENT],AX
+ MOV ES:[DI.DRVBP],BP
+;Information in directory entry must be copied into the first
+; 33 bytes starting at the disk transfer address.
+ MOV SI,BX
+ LES DI,DWORD PTR [DMAADD]
+ MOV AX,00FFH
+ CMP AL,[EXTFCB]
+ JNZ NORMFCB
+ STOSW
+ INC AL
+ STOSW
+ STOSW
+ MOV AL,[ATTRIB]
+ STOSB
+NORMFCB:
+ MOV AL,[THISDRV]
+ INC AL
+ STOSB ;Set drive number
+ MOV CX,16
+ REP MOVSW ;Copy remaining 10 characters of name
+ XOR AL,AL
+ RET
+
+KILLSRCH:
+KILLSRCH1 EQU KILLSRCH+1
+;The purpose of the KILLSRCH1 label is to provide a jump label to the following
+; instruction which leaves out the segment override.
+ MOV WORD PTR ES:[DI.FILDIRENT],-1
+ MOV AL,-1
+ RET
+
+SRCHDEV:
+ MOV ES:[DI.FILDIRENT],BX
+ LES DI,DWORD PTR [DMAADD]
+ XOR AX,AX
+ STOSB ;Zero drive byte
+ SUB SI,4 ;Point to device name
+ MOVSW
+ MOVSW
+ MOV AX,2020H
+ STOSB
+ STOSW
+ STOSW
+ STOSW ;Fill with 8 blanks
+ XOR AX,AX
+ MOV CX,10
+ REP STOSW
+ STOSB
+RET14: RET
+
+SRCHNXT: ;System call 18
+ CALL MOVNAME
+ MOV DI,DX
+ JC NEAR PTR KILLSRCH1
+ MOV BP,[DI.DRVBP]
+ MOV AX,[DI.FILDIRENT]
+ OR AX,AX
+ JS NEAR PTR KILLSRCH1
+ PUSH DX
+ PUSH DS
+ PUSH CS
+ POP DS
+ MOV [LASTENT],AX
+ CALL CONTSRCH
+ POP ES
+ POP DI
+ JMP SAVPLCE
+
+
+FILESIZE: ;System call 35
+ CALL GETFILE
+ MOV AL,-1
+ JC RET14
+ ADD DI,33 ;Write size in RR field
+ MOV CX,ES:[DI.RECSIZ-33]
+ OR CX,CX
+ JNZ RECOK
+ MOV CX,128
+RECOK:
+ XOR AX,AX
+ XOR DX,DX ;Intialize size to zero
+ OR BH,BH ;Check for named I/O device
+ JS DEVSIZ
+ INC SI
+ INC SI ;Point to length field
+ MOV AX,[SI+2] ;Get high word of size
+ DIV CX
+ PUSH AX ;Save high part of result
+ LODSW ;Get low word of size
+ DIV CX
+ OR DX,DX ;Check for zero remainder
+ POP DX
+ JZ DEVSIZ
+ INC AX ;Round up for partial record
+ JNZ DEVSIZ ;Propagate carry?
+ INC DX
+DEVSIZ:
+ STOSW
+ MOV AX,DX
+ STOSB
+ MOV AL,0
+ CMP CX,64
+ JAE RET14 ;Only 3-byte field if RECSIZ >= 64
+ MOV ES:[DI],AH
+ RET
+
+
+SETDMA: ;System call 26
+ MOV CS:[DMAADD],DX
+ MOV CS:[DMAADD+2],DS
+ RET
+
+NOSUCHDRV:
+ MOV AL,-1
+ RET
+
+GETFATPT: ;System call 27
+ MOV DL,0 ;Use default drive
+
+GETFATPTDL: ;System call 28
+ PUSH CS
+ POP DS
+ MOV AL,DL
+ CALL GETTHISDRV
+ JC NOSUCHDRV
+ CALL FATREAD
+ MOV BX,[BP.FAT]
+ MOV AL,[BP.CLUSMSK]
+ INC AL
+ MOV DX,[BP.MAXCLUS]
+ DEC DX
+ MOV CX,[BP.SECSIZ]
+ LDS SI,DWORD PTR [SPSAVE]
+ MOV [SI.BXSAVE],BX
+ MOV [SI.DXSAVE],DX
+ MOV [SI.CXSAVE],CX
+ MOV [SI.DSSAVE],CS
+ RET
+
+
+GETDSKPT: ;System call 31
+ PUSH CS
+ POP DS
+ MOV AL,[CURDRV]
+ MOV [THISDRV],AL
+ CALL FATREAD
+ LDS SI,DWORD PTR [SPSAVE]
+ MOV [SI.BXSAVE],BP
+ MOV [SI.DSSAVE],CS
+ RET
+
+
+DSKRESET: ;System call 13
+ PUSH CS
+ POP DS
+WRTFATS:
+; DS=CS. Writes back all dirty FATs. All registers destroyed.
+ XOR AL,AL
+ XCHG AL,[DIRTYBUF]
+ OR AL,AL
+ JZ NOBUF
+ MOV BP,[BUFDRVBP]
+ MOV DX,[BUFSECNO]
+ MOV BX,[BUFFER]
+ MOV CX,1
+ CALL DWRITE
+NOBUF:
+ MOV CL,[NUMIO]
+ MOV CH,0
+ MOV BP,[DRVTAB]
+WRTFAT:
+ PUSH CX
+ CALL CHKFATWRT
+ POP CX
+ ADD BP,DPBSIZ
+ LOOP WRTFAT
+ RET
+
+
+GETDRV: ;System call 25
+ MOV AL,CS:[CURDRV]
+RET15: RET
+
+
+SETRNDREC: ;System call 36
+ CALL GETREC
+ MOV [DI+33],AX
+ MOV [DI+35],DL
+ CMP [DI.RECSIZ],64
+ JAE RET15
+ MOV [DI+36],DH ;Set 4th byte only if record size < 64
+RET16: RET
+
+
+SELDSK: ;System call 14
+ MOV AL,CS:[NUMDRV]
+ CMP DL,AL
+ JNB RET17
+ MOV CS:[CURDRV],DL
+RET17: RET
+
+BUFIN: ;System call 10
+ MOV AX,CS
+ MOV ES,AX
+ MOV SI,DX
+ MOV CH,0
+ LODSW
+ OR AL,AL
+ JZ RET17
+ MOV BL,AH
+ MOV BH,CH
+ CMP AL,BL
+ JBE NOEDIT
+ CMP BYTE PTR [BX+SI],0DH
+ JZ EDITON
+NOEDIT:
+ MOV BL,CH
+EDITON:
+ MOV DL,AL
+ DEC DX
+NEWLIN:
+ MOV AL,CS:[CARPOS]
+ MOV CS:[STARTPOS],AL
+ PUSH SI
+ MOV DI,OFFSET DOSGROUP:INBUF
+ MOV AH,CH
+ MOV BH,CH
+ MOV DH,CH
+GETCH:
+ CALL IN
+ CMP AL,"F"-"@" ;Ignore ^F
+ JZ GETCH
+ CMP AL,CS:ESCCHAR
+ JZ ESC
+ CMP AL,7FH
+ JZ BACKSP
+ CMP AL,8
+ JZ BACKSP
+ CMP AL,13
+ JZ ENDLIN
+ CMP AL,10
+ JZ PHYCRLF
+ CMP AL,CANCEL
+ JZ KILNEW
+SAVCH:
+ CMP DH,DL
+ JAE BUFFUL
+ STOSB
+ INC DH
+ CALL BUFOUT
+ OR AH,AH
+ JNZ GETCH
+ CMP BH,BL
+ JAE GETCH
+ INC SI
+ INC BH
+ JMP SHORT GETCH
+
+BUFFUL:
+ MOV AL,7
+ CALL OUT
+ JMP SHORT GETCH
+
+ESC:
+ CALL IN
+ MOV CL,ESCTABLEN
+ PUSH DI
+ MOV DI,OFFSET DOSGROUP:ESCTAB
+ REPNE SCASB
+ POP DI
+ SHL CX,1
+ MOV BP,CX
+ JMP [BP+OFFSET DOSGROUP:ESCFUNC]
+
+ENDLIN:
+ STOSB
+ CALL OUT
+ POP DI
+ MOV [DI-1],DH
+ INC DH
+COPYNEW:
+ MOV BP,ES
+ MOV BX,DS
+ MOV ES,BX
+ MOV DS,BP
+ MOV SI,OFFSET DOSGROUP:INBUF
+ MOV CL,DH
+ REP MOVSB
+ RET
+CRLF:
+ MOV AL,13
+ CALL OUT
+ MOV AL,10
+ JMP OUT
+
+PHYCRLF:
+ CALL CRLF
+ JMP SHORT GETCH
+
+KILNEW:
+ MOV AL,"\"
+ CALL OUT
+ POP SI
+PUTNEW:
+ CALL CRLF
+ MOV AL,CS:[STARTPOS]
+ CALL TAB
+ JMP NEWLIN
+
+BACKSP:
+ OR DH,DH
+ JZ OLDBAK
+ CALL BACKUP
+ MOV AL,ES:[DI]
+ CMP AL," "
+ JAE OLDBAK
+ CMP AL,9
+ JZ BAKTAB
+ CALL BACKMES
+OLDBAK:
+ OR AH,AH
+ JNZ GETCH1
+ OR BH,BH
+ JZ GETCH1
+ DEC BH
+ DEC SI
+GETCH1:
+ JMP GETCH
+BAKTAB:
+ PUSH DI
+ DEC DI
+ STD
+ MOV CL,DH
+ MOV AL," "
+ PUSH BX
+ MOV BL,7
+ JCXZ FIGTAB
+FNDPOS:
+ SCASB
+ JNA CHKCNT
+ CMP ES:BYTE PTR [DI+1],9
+ JZ HAVTAB
+ DEC BL
+CHKCNT:
+ LOOP FNDPOS
+FIGTAB:
+ SUB BL,CS:[STARTPOS]
+HAVTAB:
+ SUB BL,DH
+ ADD CL,BL
+ AND CL,7
+ CLD
+ POP BX
+ POP DI
+ JZ OLDBAK
+TABBAK:
+ CALL BACKMES
+ LOOP TABBAK
+ JMP SHORT OLDBAK
+BACKUP:
+ DEC DH
+ DEC DI
+BACKMES:
+ MOV AL,8
+ CALL OUT
+ MOV AL," "
+ CALL OUT
+ MOV AL,8
+ JMP OUT
+
+TWOESC:
+ MOV AL,ESCCH
+ JMP SAVCH
+
+COPYLIN:
+ MOV CL,BL
+ SUB CL,BH
+ JMP SHORT COPYEACH
+
+COPYSTR:
+ CALL FINDOLD
+ JMP SHORT COPYEACH
+
+COPYONE:
+ MOV CL,1
+COPYEACH:
+ MOV AH,0
+ CMP DH,DL
+ JZ GETCH2
+ CMP BH,BL
+ JZ GETCH2
+ LODSB
+ STOSB
+ CALL BUFOUT
+ INC BH
+ INC DH
+ LOOP COPYEACH
+GETCH2:
+ JMP GETCH
+
+SKIPONE:
+ CMP BH,BL
+ JZ GETCH2
+ INC BH
+ INC SI
+ JMP GETCH
+
+SKIPSTR:
+ CALL FINDOLD
+ ADD SI,CX
+ ADD BH,CL
+ JMP GETCH
+
+FINDOLD:
+ CALL IN
+ MOV CL,BL
+ SUB CL,BH
+ JZ NOTFND
+ DEC CX
+ JZ NOTFND
+ PUSH ES
+ PUSH DS
+ POP ES
+ PUSH DI
+ MOV DI,SI
+ INC DI
+ REPNE SCASB
+ POP DI
+ POP ES
+ JNZ NOTFND
+ NOT CL
+ ADD CL,BL
+ SUB CL,BH
+RET30: RET
+NOTFND:
+ POP BP
+ JMP GETCH
+
+REEDIT:
+ MOV AL,"@"
+ CALL OUT
+ POP DI
+ PUSH DI
+ PUSH ES
+ PUSH DS
+ CALL COPYNEW
+ POP DS
+ POP ES
+ POP SI
+ MOV BL,DH
+ JMP PUTNEW
+
+ENTERINS:
+ IF TOGLINS
+ NOT AH
+ JMP GETCH
+ ENDIF
+ IF NOT TOGLINS
+ MOV AH,-1
+ JMP GETCH
+
+EXITINS:
+ MOV AH,0
+ JMP GETCH
+ ENDIF
+
+ESCFUNC DW GETCH
+ DW TWOESC
+ IF NOT TOGLINS
+ DW EXITINS
+ ENDIF
+ DW ENTERINS
+ DW BACKSP
+ DW REEDIT
+ DW KILNEW
+ DW COPYLIN
+ DW SKIPSTR
+ DW COPYSTR
+ DW SKIPONE
+ DW COPYONE
+
+ IF IBM
+ DW COPYONE
+ DW CTRLZ
+CTRLZ:
+ MOV AL,"Z"-"@"
+ JMP SAVCH
+ ENDIF
+BUFOUT:
+ CMP AL," "
+ JAE OUT
+ CMP AL,9
+ JZ OUT
+ PUSH AX
+ MOV AL,"^"
+ CALL OUT
+ POP AX
+ OR AL,40H
+ JMP SHORT OUT
+
+NOSTOP:
+ CMP AL,"P"-"@"
+ JZ INCHK
+ IF NOT TOGLPRN
+ CMP AL,"N"-"@"
+ JZ INCHK
+ ENDIF
+ CMP AL,"C"-"@"
+ JZ INCHK
+ RET
+
+CONOUT: ;System call 2
+ MOV AL,DL
+OUT:
+ CMP AL,20H
+ JB CTRLOUT
+ CMP AL,7FH
+ JZ OUTCH
+ INC CS:BYTE PTR [CARPOS]
+OUTCH:
+ PUSH AX
+ CALL STATCHK
+ POP AX
+ CALL FAR PTR BIOSOUT
+ TEST CS:BYTE PTR [PFLAG],-1
+ JZ RET18
+ CALL FAR PTR BIOSPRINT
+RET18: RET
+
+STATCHK:
+ CALL FAR PTR BIOSSTAT
+ JZ RET18
+ CMP AL,'S'-'@'
+ JNZ NOSTOP
+ CALL FAR PTR BIOSIN ;Eat Cntrl-S
+INCHK:
+ CALL FAR PTR BIOSIN
+ CMP AL,'P'-'@'
+ JZ PRINTON
+ IF NOT TOGLPRN
+ CMP AL,'N'-'@'
+ JZ PRINTOFF
+ ENDIF
+ CMP AL,'C'-'@'
+ JNZ RET18
+; Ctrl-C handler.
+; "^C" and CR/LF is printed. Then the user registers are restored and the
+; user CTRL-C handler is executed. At this point the top of the stack has
+; 1) the interrupt return address should the user CTRL-C handler wish to
+; allow processing to continue; 2) the original interrupt return address
+; to the code that performed the function call in the first place. If the
+; user CTRL-C handler wishes to continue, it must leave all registers
+; unchanged and IRET. The function that was interrupted will simply be
+; repeated.
+ MOV AL,3 ;Display "^C"
+ CALL BUFOUT
+ CALL CRLF
+ CLI ;Prepare to play with stack
+ MOV SS,CS:[SSSAVE]
+ MOV SP,CS:[SPSAVE] ;User stack now restored
+ POP AX
+ POP BX
+ POP CX
+ POP DX
+ POP SI
+ POP DI
+ POP BP
+ POP DS
+ POP ES ;User registers now restored
+ INT CONTC ;Execute user Ctrl-C handler
+ JMP COMMAND ;Repeat command otherwise
+
+PRINTON:
+ IF TOGLPRN
+ NOT CS:BYTE PTR [PFLAG]
+ RET
+ ENDIF
+ IF NOT TOGLPRN
+ MOV CS:BYTE PTR [PFLAG],1
+ RET
+
+PRINTOFF:
+ MOV CS:BYTE PTR [PFLAG],0
+ RET
+ ENDIF
+
+CTRLOUT:
+ CMP AL,13
+ JZ ZERPOS
+ CMP AL,8
+ JZ BACKPOS
+ CMP AL,9
+ JNZ OUTCHJ
+ MOV AL,CS:[CARPOS]
+ OR AL,0F8H
+ NEG AL
+TAB:
+ PUSH CX
+ MOV CL,AL
+ MOV CH,0
+ JCXZ POPTAB
+TABLP:
+ MOV AL," "
+ CALL OUT
+ LOOP TABLP
+POPTAB:
+ POP CX
+RET19: RET
+
+ZERPOS:
+ MOV CS:BYTE PTR [CARPOS],0
+OUTCHJ: JMP OUTCH
+
+BACKPOS:
+ DEC CS:BYTE PTR [CARPOS]
+ JMP OUTCH
+
+
+CONSTAT: ;System call 11
+ CALL STATCHK
+ MOV AL,0
+ JZ RET19
+ OR AL,-1
+ RET
+
+
+CONIN: ;System call 1
+ CALL IN
+ PUSH AX
+ CALL OUT
+ POP AX
+ RET
+
+
+IN: ;System call 8
+ CALL INCHK
+ JZ IN
+RET29: RET
+
+RAWIO: ;System call 6
+ MOV AL,DL
+ CMP AL,-1
+ JNZ RAWOUT
+ LDS SI,DWORD PTR CS:[SPSAVE] ;Get pointer to register save area
+ CALL FAR PTR BIOSSTAT
+ JNZ RESFLG
+ OR BYTE PTR [SI.FSAVE],40H ;Set user's zero flag
+ XOR AL,AL
+ RET
+
+RESFLG:
+ AND BYTE PTR [SI.FSAVE],0FFH-40H ;Reset user's zero flag
+RAWINP: ;System call 7
+ CALL FAR PTR BIOSIN
+ RET
+RAWOUT:
+ CALL FAR PTR BIOSOUT
+ RET
+
+LIST: ;System call 5
+ MOV AL,DL
+LISTOUT:
+ PUSH AX
+ CALL STATCHK
+ POP AX
+ CALL FAR PTR BIOSPRINT
+RET20: RET
+
+PRTBUF: ;System call 9
+ MOV SI,DX
+OUTSTR:
+ LODSB
+ CMP AL,"$"
+ JZ RET20
+ CALL OUT
+ JMP SHORT OUTSTR
+
+OUTMES: ;String output for internal messages
+ LODS CS:BYTE PTR [SI]
+ CMP AL,"$"
+ JZ RET20
+ CALL OUT
+ JMP SHORT OUTMES
+
+
+MAKEFCB: ;Interrupt call 41
+DRVBIT EQU 2
+NAMBIT EQU 4
+EXTBIT EQU 8
+ MOV DL,0 ;Flag--not ambiguous file name
+ TEST AL,DRVBIT ;Use current drive field if default?
+ JNZ DEFDRV
+ MOV BYTE PTR ES:[DI],0 ;No - use default drive
+DEFDRV:
+ INC DI
+ MOV CX,8
+ TEST AL,NAMBIT ;Use current name fiels as defualt?
+ XCHG AX,BX ;Save bits in BX
+ MOV AL," "
+ JZ FILLB ;If not, go fill with blanks
+ ADD DI,CX
+ XOR CX,CX ;Don't fill any
+FILLB:
+ REP STOSB
+ MOV CL,3
+ TEST BL,EXTBIT ;Use current extension as default
+ JZ FILLB2
+ ADD DI,CX
+ XOR CX,CX
+FILLB2:
+ REP STOSB
+ XCHG AX,CX ;Put zero in AX
+ STOSW
+ STOSW ;Initialize two words after to zero
+ SUB DI,16 ;Point back at start
+ TEST BL,1 ;Scan off separators if not zero
+ JZ SKPSPC
+ CALL SCANB ;Peel off blanks and tabs
+ CALL DELIM ;Is it a one-time-only delimiter?
+ JNZ NOSCAN
+ INC SI ;Skip over the delimiter
+SKPSPC:
+ CALL SCANB ;Always kill preceding blanks and tabs
+NOSCAN:
+ CALL GETLET
+ JBE NODRV ;Quit if termination character
+ CMP BYTE PTR[SI],":" ;Check for potential drive specifier
+ JNZ NODRV
+ INC SI ;Skip over colon
+ SUB AL,"@" ;Convert drive letter to binary drive number
+ JBE BADDRV ;Valid drive numbers are 1-15
+ CMP AL,CS:[NUMDRV]
+ JBE HAVDRV
+BADDRV:
+ MOV DL,-1
+HAVDRV:
+ STOSB ;Put drive specifier in first byte
+ INC SI
+ DEC DI ;Counteract next two instructions
+NODRV:
+ DEC SI ;Back up
+ INC DI ;Skip drive byte
+ MOV CX,8
+ CALL GETWORD ;Get 8-letter file name
+ CMP BYTE PTR [SI],"."
+ JNZ NODOT
+ INC SI ;Skip over dot if present
+ MOV CX,3 ;Get 3-letter extension
+ CALL MUSTGETWORD
+NODOT:
+ LDS BX,CS:DWORD PTR [SPSAVE]
+ MOV [BX.SISAVE],SI
+ MOV AL,DL
+ RET
+
+NONAM:
+ ADD DI,CX
+ DEC SI
+ RET
+
+GETWORD:
+ CALL GETLET
+ JBE NONAM ;Exit if invalid character
+ DEC SI
+MUSTGETWORD:
+ CALL GETLET
+ JBE FILLNAM
+ JCXZ MUSTGETWORD
+ DEC CX
+ CMP AL,"*" ;Check for ambiguous file specifier
+ JNZ NOSTAR
+ MOV AL,"?"
+ REP STOSB
+NOSTAR:
+ STOSB
+ CMP AL,"?"
+ JNZ MUSTGETWORD
+ OR DL,1 ;Flag ambiguous file name
+ JMP MUSTGETWORD
+FILLNAM:
+ MOV AL," "
+ REP STOSB
+ DEC SI
+RET21: RET
+
+SCANB:
+ LODSB
+ CALL SPCHK
+ JZ SCANB
+ DEC SI
+ RET
+
+GETLET:
+;Get a byte from [SI], convert it to upper case, and compare for delimiter.
+;ZF set if a delimiter, CY set if a control character (other than TAB).
+ LODSB
+ AND AL,7FH
+ CMP AL,"a"
+ JB CHK
+ CMP AL,"z"
+ JA CHK
+ SUB AL,20H ;Convert to upper case
+CHK:
+ CMP AL,"."
+ JZ RET21
+ CMP AL,'"'
+ JZ RET21
+ CMP AL,"/"
+ JZ RET21
+ CMP AL,"["
+ JZ RET21
+ CMP AL,"]"
+ JZ RET21
+
+ IF IBM
+DELIM:
+ ENDIF
+ CMP AL,":" ;Allow ":" as separator in IBM version
+ JZ RET21
+ IF NOT IBM
+DELIM:
+ ENDIF
+
+ CMP AL,"+"
+ JZ RET101
+ CMP AL,"="
+ JZ RET101
+ CMP AL,";"
+ JZ RET101
+ CMP AL,","
+ JZ RET101
+SPCHK:
+ CMP AL,9 ;Filter out tabs too
+ JZ RET101
+;WARNING! " " MUST be the last compare
+ CMP AL," "
+RET101: RET
+
+SETVECT: ; Interrupt call 37
+ XOR BX,BX
+ MOV ES,BX
+ MOV BL,AL
+ SHL BX,1
+ SHL BX,1
+ MOV ES:[BX],DX
+ MOV ES:[BX+2],DS
+ RET
+
+
+NEWBASE: ; Interrupt call 38
+ MOV ES,DX
+ LDS SI,CS:DWORD PTR [SPSAVE]
+ MOV DS,[SI.CSSAVE]
+ XOR SI,SI
+ MOV DI,SI
+ MOV AX,DS:[2]
+ MOV CX,80H
+ REP MOVSW
+
+SETMEM:
+
+; Inputs:
+; AX = Size of memory in paragraphs
+; DX = Segment
+; Function:
+; Completely prepares a program base at the
+; specified segment.
+; Outputs:
+; DS = DX
+; ES = DX
+; [0] has INT 20H
+; [2] = First unavailable segment ([ENDMEM])
+; [5] to [9] form a long call to the entry point
+; [10] to [13] have exit address (from INT 22H)
+; [14] to [17] have ctrl-C exit address (from INT 23H)
+; [18] to [21] have fatal error address (from INT 24H)
+; DX,BP unchanged. All other registers destroyed.
+
+ XOR CX,CX
+ MOV DS,CX
+ MOV ES,DX
+ MOV SI,EXIT
+ MOV DI,SAVEXIT
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOVSW
+ MOV ES:[2],AX
+ SUB AX,DX
+ CMP AX,MAXDIF
+ JBE HAVDIF
+ MOV AX,MAXDIF
+HAVDIF:
+ MOV BX,ENTRYPOINTSEG
+ SUB BX,AX
+ SHL AX,1
+ SHL AX,1
+ SHL AX,1
+ SHL AX,1
+ MOV DS,DX
+ MOV DS:[6],AX
+ MOV DS:[8],BX
+ MOV DS:[0],20CDH ;"INT INTTAB"
+ MOV DS:(BYTE PTR [5]),LONGCALL
+ RET
+
+DATE16:
+ PUSH CX
+ CALL READTIME
+ SHL CL,1 ;Minutes to left part of byte
+ SHL CL,1
+ SHL CX,1 ;Push hours and minutes to left end
+ SHL CX,1
+ SHL CX,1
+ SHR DH,1 ;Count every two seconds
+ OR CL,DH ;Combine seconds with hours and minutes
+ MOV DX,CX
+ POP CX
+ MOV AX,WORD PTR [MONTH] ;Fetch month and year
+ SHL AL,1 ;Push month to left to make room for day
+ SHL AL,1
+ SHL AL,1
+ SHL AL,1
+ SHL AX,1
+ OR AL,[DAY]
+RET22: RET
+
+FOURYEARS EQU 3*365+366
+
+READTIME:
+;Gets time in CX:DX. Figures new date if it has changed.
+;Uses AX, CX, DX.
+ CALL FAR PTR BIOSGETTIME
+ CMP AX,[DAYCNT] ;See if day count is the same
+ JZ RET22
+ CMP AX,FOURYEARS*30 ;Number of days in 120 years
+ JAE RET22 ;Ignore if too large
+ MOV [DAYCNT],AX
+ PUSH SI
+ PUSH CX
+ PUSH DX ;Save time
+ XOR DX,DX
+ MOV CX,FOURYEARS ;Number of days in 4 years
+ DIV CX ;Compute number of 4-year units
+ SHL AX,1
+ SHL AX,1
+ SHL AX,1 ;Multiply by 8 (no. of half-years)
+ MOV CX,AX ;<240 implies AH=0
+ MOV SI,OFFSET DOSGROUP:YRTAB ;Table of days in each year
+ CALL DSLIDE ;Find out which of four years we're in
+ SHR CX,1 ;Convert half-years to whole years
+ JNC SK ;Extra half-year?
+ ADD DX,200
+SK:
+ CALL SETYEAR
+ MOV CL,1 ;At least at first month in year
+ MOV SI,OFFSET DOSGROUP:MONTAB ;Table of days in each month
+ CALL DSLIDE ;Find out which month we're in
+ MOV [MONTH],CL
+ INC DX ;Remainder is day of month (start with one)
+ MOV [DAY],DL
+ CALL WKDAY ;Set day of week
+ POP DX
+ POP CX
+ POP SI
+RET23: RET
+
+DSLIDE:
+ MOV AH,0
+DSLIDE1:
+ LODSB ;Get count of days
+ CMP DX,AX ;See if it will fit
+ JB RET23 ;If not, done
+ SUB DX,AX
+ INC CX ;Count one more month/year
+ JMP SHORT DSLIDE1
+
+SETYEAR:
+;Set year with value in CX. Adjust length of February for this year.
+ MOV BYTE PTR [YEAR],CL
+CHKYR:
+ TEST CL,3 ;Check for leap year
+ MOV AL,28
+ JNZ SAVFEB ;28 days if no leap year
+ INC AL ;Add leap day
+SAVFEB:
+ MOV [MONTAB+1],AL ;Store for February
+ RET
+
+;Days in year
+YRTAB DB 200,166 ;Leap year
+ DB 200,165
+ DB 200,165
+ DB 200,165
+
+;Days of each month
+MONTAB DB 31 ;January
+ DB 28 ;February--reset each time year changes
+ DB 31 ;March
+ DB 30 ;April
+ DB 31 ;May
+ DB 30 ;June
+ DB 31 ;July
+ DB 31 ;August
+ DB 30 ;September
+ DB 31 ;October
+ DB 30 ;November
+ DB 31 ;December
+
+GETDATE: ;Function call 42
+ PUSH CS
+ POP DS
+ CALL READTIME ;Check for rollover to next day
+ MOV AX,[YEAR]
+ MOV BX,WORD PTR [DAY]
+ LDS SI,DWORD PTR [SPSAVE] ;Get pointer to user registers
+ MOV [SI.DXSAVE],BX ;DH=month, DL=day
+ ADD AX,1980 ;Put bias back
+ MOV [SI.CXSAVE],AX ;CX=year
+ MOV AL,CS:[WEEKDAY]
+RET24: RET
+
+SETDATE: ;Function call 43
+ MOV AL,-1 ;Be ready to flag error
+ SUB CX,1980 ;Fix bias in year
+ JC RET24 ;Error if not big enough
+ CMP CX,119 ;Year must be less than 2100
+ JA RET24
+ OR DH,DH
+ JZ RET24
+ OR DL,DL
+ JZ RET24 ;Error if either month or day is 0
+ CMP DH,12 ;Check against max. month
+ JA RET24
+ PUSH CS
+ POP DS
+ CALL CHKYR ;Set Feb. up for new year
+ MOV AL,DH
+ MOV BX,OFFSET DOSGROUP:MONTAB-1
+ XLAT ;Look up days in month
+ CMP AL,DL
+ MOV AL,-1 ;Restore error flag, just in case
+ JB RET24 ;Error if too many days
+ CALL SETYEAR
+ MOV WORD PTR [DAY],DX ;Set both day and month
+ SHR CX,1
+ SHR CX,1
+ MOV AX,FOURYEARS
+ MOV BX,DX
+ MUL CX
+ MOV CL,BYTE PTR [YEAR]
+ AND CL,3
+ MOV SI,OFFSET DOSGROUP:YRTAB
+ MOV DX,AX
+ SHL CX,1 ;Two entries per year, so double count
+ CALL DSUM ;Add up the days in each year
+ MOV CL,BH ;Month of year
+ MOV SI,OFFSET DOSGROUP:MONTAB
+ DEC CX ;Account for months starting with one
+ CALL DSUM ;Add up days in each month
+ MOV CL,BL ;Day of month
+ DEC CX ;Account for days starting with one
+ ADD DX,CX ;Add in to day total
+ XCHG AX,DX ;Get day count in AX
+ MOV [DAYCNT],AX
+ CALL FAR PTR BIOSSETDATE
+WKDAY:
+ MOV AX,[DAYCNT]
+ XOR DX,DX
+ MOV CX,7
+ INC AX
+ INC AX ;First day was Tuesday
+ DIV CX ;Compute day of week
+ MOV [WEEKDAY],DL
+ XOR AL,AL ;Flag OK
+RET25: RET
+
+DSUM:
+ MOV AH,0
+ JCXZ RET25
+DSUM1:
+ LODSB
+ ADD DX,AX
+ LOOP DSUM1
+ RET
+
+GETTIME: ;Function call 44
+ PUSH CS
+ POP DS
+ CALL READTIME
+ LDS SI,DWORD PTR [SPSAVE] ;Get pointer to user registers
+ MOV [SI.DXSAVE],DX
+ MOV [SI.CXSAVE],CX
+ XOR AL,AL
+RET26: RET
+
+SETTIME: ;Function call 45
+;Time is in CX:DX in hours, minutes, seconds, 1/100 sec.
+ MOV AL,-1 ;Flag in case of error
+ CMP CH,24 ;Check hours
+ JAE RET26
+ CMP CL,60 ;Check minutes
+ JAE RET26
+ CMP DH,60 ;Check seconds
+ JAE RET26
+ CMP DL,100 ;Check 1/100's
+ JAE RET26
+ CALL FAR PTR BIOSSETTIME
+ XOR AL,AL
+ RET
+
+
+; Default handler for division overflow trap
+DIVOV:
+ PUSH SI
+ PUSH AX
+ MOV SI,OFFSET DOSGROUP:DIVMES
+ CALL OUTMES
+ POP AX
+ POP SI
+ INT 23H ;Use Ctrl-C abort on divide overflow
+ IRET
+
+CODSIZ EQU $-CODSTRT ;Size of code segment
+CODE ENDS
+
+
+;***** DATA AREA *****
+CONSTANTS SEGMENT BYTE
+ ORG 0
+CONSTRT EQU $ ;Start of constants segment
+
+IONAME:
+ IF NOT IBM
+ DB "PRN ","LST ","NUL ","AUX ","CON "
+ ENDIF
+ IF IBM
+ DB "COM1","PRN ","LPT1","NUL ","AUX ","CON "
+ ENDIF
+DIVMES DB 13,10,"Divide overflow",13,10,"$"
+CARPOS DB 0
+STARTPOS DB 0
+PFLAG DB 0
+DIRTYDIR DB 0 ;Dirty buffer flag
+NUMDRV DB 0 ;Number of drives
+NUMIO DB ? ;Number of disk tables
+VERFLG DB 0 ;Initialize with verify off
+CONTPOS DW 0
+DMAADD DW 80H ;User's disk transfer address (disp/seg)
+ DW ?
+ENDMEM DW ?
+MAXSEC DW 0
+BUFFER DW ?
+BUFSECNO DW 0
+BUFDRVNO DB -1
+DIRTYBUF DB 0
+BUFDRVBP DW ?
+DIRBUFID DW -1
+DAY DB 0
+MONTH DB 0
+YEAR DW 0
+DAYCNT DW -1
+WEEKDAY DB 0
+CURDRV DB 0 ;Default to drive A
+DRVTAB DW 0 ;Address of start of DPBs
+DOSLEN EQU CODSIZ+($-CONSTRT) ;Size of CODE + CONSTANTS segments
+CONSTANTS ENDS
+
+DATA SEGMENT WORD
+; Init code overlaps with data area below
+
+ ORG 0
+INBUF DB 128 DUP (?)
+CONBUF DB 131 DUP (?) ;The rest of INBUF and console buffer
+LASTENT DW ?
+EXITHOLD DB 4 DUP (?)
+FATBASE DW ?
+NAME1 DB 11 DUP (?) ;File name buffer
+ATTRIB DB ?
+NAME2 DB 11 DUP (?)
+NAME3 DB 12 DUP (?)
+EXTFCB DB ?
+;WARNING - the following two items are accessed as a word
+CREATING DB ?
+DELALL DB ?
+TEMP LABEL WORD
+SPSAVE DW ?
+SSSAVE DW ?
+CONTSTK DW ?
+SECCLUSPOS DB ? ;Position of first sector within cluster
+DSKERR DB ?
+TRANS DB ?
+PREREAD DB ? ;0 means preread; 1 means optional
+READOP DB ?
+THISDRV DB ?
+
+ EVEN
+FCB DW ? ;Address of user FCB
+NEXTADD DW ?
+RECPOS DB 4 DUP (?)
+RECCNT DW ?
+LASTPOS DW ?
+CLUSNUM DW ?
+SECPOS DW ? ;Position of first sector accessed
+VALSEC DW ? ;Number of valid (previously written) sectors
+BYTSECPOS DW ? ;Position of first byte within sector
+BYTPOS DB 4 DUP (?) ;Byte position in file of access
+BYTCNT1 DW ? ;No. of bytes in first sector
+BYTCNT2 DW ? ;No. of bytes in last sector
+SECCNT DW ? ;No. of whole sectors
+ENTFREE DW ?
+
+ DB 80H DUP (?) ;Stack space
+IOSTACK LABEL BYTE
+ DB 80H DUP (?)
+DSKSTACK LABEL BYTE
+
+ IF DSKTEST
+NSS DW ?
+NSP DW ?
+ ENDIF
+
+DIRBUF LABEL WORD
+
+;Init code below overlaps with data area above
+
+ ORG 0
+
+MOVFAT:
+;This section of code is safe from being overwritten by block move
+ REP MOVS BYTE PTR [DI],[SI]
+ CLD
+ MOV ES:[DMAADD+2],DX
+ MOV SI,[DRVTAB] ;Address of first DPB
+ MOV AL,-1
+ MOV CL,[NUMIO] ;Number of DPBs
+FLGFAT:
+ MOV DI,ES:[SI.FAT] ;get pointer to FAT
+ DEC DI ;Point to dirty byte
+ STOSB ;Flag as unused
+ ADD SI,DPBSIZ ;Point to next DPB
+ LOOP FLGFAT
+ MOV AX,[ENDMEM]
+ CALL SETMEM ;Set up segment
+
+XXX PROC FAR
+ RET
+XXX ENDP
+
+DOSINIT:
+ CLI
+ CLD
+ PUSH CS
+ POP ES
+ MOV ES:[ENDMEM],DX
+ LODSB ;Get no. of drives & no. of I/O drivers
+ MOV ES:[NUMIO],AL
+ MOV DI,OFFSET DOSGROUP:MEMSTRT
+PERDRV:
+ MOV BP,DI
+ MOV AL,ES:[DRVCNT]
+ STOSB ;DEVNUM
+ LODSB ;Physical unit no.
+ STOSB ;DRVNUM
+ CMP AL,15
+ JA BADINIT
+ CBW ;Index into FAT size table
+ SHL AX,1
+ ADD AX,OFFSET DOSGROUP:FATSIZTAB
+ XCHG BX,AX
+ LODSW ;Pointer to DPT
+ PUSH SI
+ MOV SI,AX
+ LODSW
+ STOSW ;SECSIZ
+ MOV DX,AX
+ CMP AX,ES:[MAXSEC]
+ JBE NOTMAX
+ MOV ES:[MAXSEC],AX
+NOTMAX:
+ LODSB
+ DEC AL
+ STOSB ;CLUSMSK
+ JZ HAVSHFT
+ CBW
+FIGSHFT:
+ INC AH
+ SAR AL,1
+ JNZ FIGSHFT
+ MOV AL,AH
+HAVSHFT:
+ STOSB ;CLUSSHFT
+ MOVSW ;FIRFAT (= number of reserved sectors)
+ MOVSB ;FATCNT
+ MOVSW ;MAXENT
+ MOV AX,DX ;SECSIZ again
+ MOV CL,5
+ SHR AX,CL
+ MOV CX,AX ;Directory entries per sector
+ DEC AX
+ ADD AX,ES:[BP.MAXENT]
+ XOR DX,DX
+ DIV CX
+ STOSW ;DIRSEC (temporarily)
+ MOVSW ;DSKSIZ (temporarily)
+FNDFATSIZ:
+ MOV AL,1
+ MOV DX,1
+GETFATSIZ:
+ PUSH DX
+ CALL FIGFATSIZ
+ POP DX
+ CMP AL,DL ;Compare newly computed FAT size with trial
+ JZ HAVFATSIZ ;Has sequence converged?
+ CMP AL,DH ;Compare with previous trial
+ MOV DH,DL
+ MOV DL,AL ;Shuffle trials
+ JNZ GETFATSIZ ;Continue iterations if not oscillating
+ DEC WORD PTR ES:[BP.DSKSIZ] ;Damp those oscillations
+ JMP SHORT FNDFATSIZ ;Try again
+
+BADINIT:
+ MOV SI,OFFSET DOSGROUP:BADMES
+ CALL OUTMES
+ STI
+ HLT
+
+HAVFATSIZ:
+ STOSB ;FATSIZ
+ MUL ES:BYTE PTR[BP.FATCNT] ;Space occupied by all FATs
+ ADD AX,ES:[BP.FIRFAT]
+ STOSW ;FIRDIR
+ ADD AX,ES:[BP.DIRSEC]
+ MOV ES:[BP.FIRREC],AX ;Destroys DIRSEC
+ CALL FIGMAX
+ MOV ES:[BP.MAXCLUS],CX
+ MOV AX,BX ;Pointer into FAT size table
+ STOSW ;Allocate space for FAT pointer
+ MOV AL,ES:[BP.FATSIZ]
+ XOR AH,AH
+ MUL ES:[BP.SECSIZ]
+ CMP AX,ES:[BX] ;Bigger than already allocated
+ JBE SMFAT
+ MOV ES:[BX],AX
+SMFAT:
+ POP SI ;Restore pointer to init. table
+ MOV AL,ES:[DRVCNT]
+ INC AL
+ MOV ES:[DRVCNT],AL
+ CMP AL,ES:[NUMIO]
+ JAE CONTINIT
+ JMP PERDRV
+
+BADINITJ:
+ JMP BADINIT
+
+CONTINIT:
+ PUSH CS
+ POP DS
+;Calculate true address of buffers, FATs, free space
+ MOV BP,[MAXSEC]
+ MOV AX,OFFSET DOSGROUP:DIRBUF
+ ADD AX,BP
+ MOV [BUFFER],AX ;Start of buffer
+ ADD AX,BP
+ MOV [DRVTAB],AX ;Start of DPBs
+ SHL BP,1 ;Two sectors - directory and buffer
+ ADD BP,DI ;Allocate buffer space
+ ADD BP,ADJFAC ;True address of FATs
+ PUSH BP
+ MOV SI,OFFSET DOSGROUP:FATSIZTAB
+ MOV DI,SI
+ MOV CX,16
+TOTFATSIZ:
+ INC BP ;Add one for Dirty byte
+ INC BP ;Add one for I/O device number
+ LODSW ;Get size of this FAT
+ XCHG AX,BP
+ STOSW ;Save address of this FAT
+ ADD BP,AX ;Compute size of next FAT
+ CMP AX,BP ;If size was zero done
+ LOOPNZ TOTFATSIZ
+ MOV AL,15
+ SUB AL,CL ;Compute number of FATs used
+ MOV [NUMDRV],AL
+ XOR AX,AX ;Set zero flag
+ REPZ SCASW ;Make sure all other entries are zero
+ JNZ BADINITJ
+ ADD BP,15 ;True start of free space
+ MOV CL,4
+ SHR BP,CL ;First free segment
+ MOV DX,CS
+ ADD DX,BP
+ MOV BX,0FH
+ MOV CX,[ENDMEM]
+ CMP CX,1 ;Use memory scan?
+ JNZ SETEND
+ MOV CX,DX ;Start scanning just after DOS
+MEMSCAN:
+ INC CX
+ JZ SETEND
+ MOV DS,CX
+ MOV AL,[BX]
+ NOT AL
+ MOV [BX],AL
+ CMP AL,[BX]
+ NOT AL
+ MOV [BX],AL
+ JZ MEMSCAN
+SETEND:
+ IF HIGHMEM
+ SUB CX,BP
+ MOV BP,CX ;Segment of DOS
+ MOV DX,CS ;Program segment
+ ENDIF
+ IF NOT HIGHMEM
+ MOV BP,CS
+ ENDIF
+; BP has segment of DOS (whether to load high or run in place)
+; DX has program segment (whether after DOS or overlaying DOS)
+; CX has size of memory in paragraphs (reduced by DOS size if HIGHMEM)
+ MOV CS:[ENDMEM],CX
+ IF HIGHMEM
+ MOV ES,BP
+ XOR SI,SI
+ MOV DI,SI
+ MOV CX,(DOSLEN+1)/2
+ PUSH CS
+ POP DS
+ REP MOVSW ;Move DOS to high memory
+ ENDIF
+ XOR AX,AX
+ MOV DS,AX
+ MOV ES,AX
+ MOV DI,INTBASE
+ MOV AX,OFFSET DOSGROUP:QUIT
+ STOSW ;Set abort address--displacement
+ MOV AX,BP
+ MOV BYTE PTR DS:[ENTRYPOINT],LONGJUMP
+ MOV WORD PTR DS:[ENTRYPOINT+1],OFFSET DOSGROUP:ENTRY
+ MOV WORD PTR DS:[ENTRYPOINT+3],AX
+ MOV WORD PTR DS:[0],OFFSET DOSGROUP:DIVOV ;Set default divide trap address
+ MOV DS:[2],AX
+ MOV CX,9
+ REP STOSW ;Set 5 segments (skip 2 between each)
+ MOV WORD PTR DS:[INTBASE+4],OFFSET DOSGROUP:COMMAND
+ MOV WORD PTR DS:[INTBASE+12],OFFSET DOSGROUP:IRET ;Ctrl-C exit
+ MOV WORD PTR DS:[INTBASE+16],OFFSET DOSGROUP:IRET ;Fatal error exit
+ MOV AX,OFFSET BIOSREAD
+ STOSW
+ MOV AX,BIOSSEG
+ STOSW
+ STOSW ;Add 2 to DI
+ STOSW
+ MOV WORD PTR DS:[INTBASE+18H],OFFSET BIOSWRITE
+ MOV WORD PTR DS:[EXIT],100H
+ MOV WORD PTR DS:[EXIT+2],DX
+ IF NOT IBM
+ MOV SI,OFFSET DOSGROUP:HEADER
+ CALL OUTMES
+ ENDIF
+ PUSH CS
+ POP DS
+ PUSH CS
+ POP ES
+;Move the FATs into position
+ MOV AL,[NUMIO]
+ CBW
+ XCHG AX,CX
+ MOV DI,OFFSET DOSGROUP:MEMSTRT.FAT
+FATPOINT:
+ MOV SI,WORD PTR [DI] ;Get address within FAT address table
+ MOVSW ;Set address of this FAT
+ ADD DI,DPBSIZ-2 ;Point to next DPB
+ LOOP FATPOINT
+ POP CX ;True address of first FAT
+ MOV SI,OFFSET DOSGROUP:MEMSTRT ;Place to move DPBs from
+ MOV DI,[DRVTAB] ;Place to move DPBs to
+ SUB CX,DI ;Total length of DPBs
+ CMP DI,SI
+ JBE MOVJMP ;Are we moving to higher or lower memory?
+ DEC CX ;Move backwards to higher memory
+ ADD DI,CX
+ ADD SI,CX
+ INC CX
+ STD
+MOVJMP:
+ MOV ES,BP
+ JMP MOVFAT
+
+FIGFATSIZ:
+ MUL ES:BYTE PTR[BP.FATCNT]
+ ADD AX,ES:[BP.FIRFAT]
+ ADD AX,ES:[BP.DIRSEC]
+FIGMAX:
+;AX has equivalent of FIRREC
+ SUB AX,ES:[BP.DSKSIZ]
+ NEG AX
+ MOV CL,ES:[BP.CLUSSHFT]
+ SHR AX,CL
+ INC AX
+ MOV CX,AX ;MAXCLUS
+ INC AX
+ MOV DX,AX
+ SHR DX,1
+ ADC AX,DX ;Size of FAT in bytes
+ MOV SI,ES:[BP.SECSIZ]
+ ADD AX,SI
+ DEC AX
+ XOR DX,DX
+ DIV SI
+ RET
+
+BADMES:
+ DB 13,10,"INIT TABLE BAD",13,10,"$"
+
+FATSIZTAB:
+ DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+
+DRVCNT DB 0
+
+MEMSTRT LABEL WORD
+ADJFAC EQU DIRBUF-MEMSTRT
+DATA ENDS
+ END
+\1a
\ No newline at end of file