; 86-DOS High-performance operating system for the 8086 version 1.00 04/28/81 ; 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 ; ; ************************************************************* ; Use the switch below to generate code to accept the old 16-byte ; directory entry as well as the new 32-byte entry. SMALLDIR: EQU 1 ;1 to enable, 0 to disable ; 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 0 ;1 to enable, 0 to disable ; 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 MAXCALL:EQU 36 MAXCOM: EQU 41 ESCCH: EQU 1BH 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 ORG 0 DS 12 ;Drive code and name EXTENT: DS 2 RECSIZ: DS 2 ;Size of record (user settable) FILSIZ: DS 4 ;Size of file in bytes FDATE: DS 2 ;Date of last writing FILDIRBLK:DS 2 ;Location in directory FIRCLUS:DS 2 ;First cluster of file LSTCLUS:DS 2 ;Last cluster accessed CLUSPOS:DS 2 ;Position of last cluster accessed DIRTYFIL:DS 1 ;File has been written to if <>0 ORG 32 NR: DS 1 ;Next record RR: DS 3 ;Random record ; 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 13 Zero field (for expansion) ; 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 ORG 0 DRVNUM: DS 1 ;Drive number SECSIZ: DS 2 ;Size of physical sector in bytes CLUSMSK:DS 1 ;Sectors/cluster - 1 CLUSSHFT:DS 1 ;Log2 of sectors/cluster FIRFAT: DS 2 ;Starting record of FATs FATCNT: DS 1 ;Number of FATs for this drive MAXENT: DS 2 ;Number of directory entries DIRSEC: ;Number of dir. sectors (init temporary) FIRREC: DS 2 ;First sector of first cluster DSKSIZ: ;Size of disk (temp used during init only) MAXCLUS:DS 2 ;Number of clusters on drive + 1 FATSIZ: DS 1 ;Number of records occupied by FAT FIRDIR: DS 2 ;Starting record of directory IF SMALLDIR FIRREC1:DS 2 ;First data sector with 16-byte dir. entries MAXCLUS1:DS 2 ;No. of clusters + 1 with 16-byte dir. entries FIRREC2:DS 2 ;First data sector with 32-byte dir. entries MAXCLUS2:DS 2 ;No. of clusters + 1 with 32-byte dir entries ENDIF DIRTYFAT:DS 1 ;1=FAT has been changed, -1=never been read FAT: ;Start of FAT DIRSIZ: ;-1=small dir. entry, else large ; BIOS entry point defintions BIOSSEG: EQU 40H ORG 0 DS 3 BIOSSTAT: DS 3 BIOSIN: DS 3 BIOSOUT: DS 3 BIOSPRINT: DS 3 BIOSAUXIN: DS 3 BIOSAUXOUT: DS 3 BIOSREAD: DS 3 BIOSWRITE: DS 3 BIOSDSKCHG: DS 3 ; Location of user registers relative user stack pointer ORG 0 AXSAVE: DS 2 BXSAVE: DS 2 CXSAVE: DS 2 DXSAVE: DS 2 SISAVE: DS 2 DISAVE: DS 2 BPSAVE: DS 2 DSSAVE: DS 2 ESSAVE: DS 2 IPSAVE: DS 2 CSSAVE: DS 2 FSAVE: DS 2 ; Start of code ORG 0 PUT 100H JMP DOSINIT ESCTAB: DB "SC" ;Copy one char DB "VN" ;Skip one char DB "TA" ;Copy to char DB "WB" ;Skip to char DB "UH" ;Copy line DB "HH" ;Kill line (no change in template) DB "RM" ;Reedit line (new template) DB "DD" ;Backspace DB "P@" ;Enter insert mode DB "QL" ;Exit insert mode DB ESCCH,ESCCH ;Escape character DB ESCCH,ESCCH ;End of table ESCTABLEN:EQU $-ESCTAB HEADER: DB 13,10,"86-DOS version 1.00" IF DSKTEST DB "D" ENDIF DB 13,10 DB "Copyright 1980,81 Seattle Computer Products, Inc.",13,10,"$" QUIT: MOV AH,0 JP 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 SEG CS POP [TEMP] ;IP from the CALL 5 PUSHF ;Start re-ordering the stack DI PUSH AX ;Save segment SEG CS PUSH [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 SEG CS MOV AX,[SPSAVE] SEG CS MOV [NSP],AX SEG CS MOV AX,[SSSAVE] SEG CS MOV [NSS],AX POP AX PUSH AX ENDIF SEG CS MOV [SPSAVE],SP SEG CS MOV [SSSAVE],SS MOV BP,SP MOV BX,[BP+CSSAVE] SEG CS MOV [CSLOC],BX MOV SP,CS MOV SS,SP MOV SP,STACK EI ;Stack OK now SEG CS MOV [FUNC],AH MOV BL,AH MOV BH,0 SHL BX UP IF DSKTEST CMP AH,12 JL SAMSTK MOV SP,TESTSTK SAMSTK: ENDIF SEG CS CALL [BX+DISPATCH] LEAVE: DI SEG CS MOV SP,[SPSAVE] SEG CS MOV SS,[SSSAVE] MOV BP,SP MOV [BP+AXSAVE],AL IF DSKTEST SEG CS MOV AX,[NSP] SEG CS MOV [SPSAVE],AX SEG CS MOV AX,[NSS] SEG CS MOV [SSSAVE],AX ENDIF POP AX POP BX POP CX POP DX POP SI POP DI POP BP POP DS POP ES IRET DISPATCH: ; Standard Functions 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 VERSION 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 CURDRV ;25 DW SETDMA DW GETFATPT DW WRTPROT 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 VERSION: GETIO: SETIO: WRTPROT: GETRDONLY: SETATTRIB: USERCODE: MOV AL,0 RET READER: CALL BIOSAUXIN,BIOSSEG RET PUNCH: MOV AL,DL CALL BIOSAUXOUT,BIOSSEG 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 MOV DI,[DI+BX] JNC HAVCLUS SHR DI SHR DI SHR DI SHR DI STC HAVCLUS: RCL BX AND DI,0FFFH RET HURTFAT: MOV SI,BADMES CALL OUTMES JMP ERROR 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 ADD BX,SI ADD BX,DI SHR DI MOV DI,[BX] JNC ALIGNED SHL DX SHL DX SHL DX SHL DX AND DI,0FH JP PACKIN ALIGNED: AND DI,0F000H PACKIN: OR DI,DX MOV [BX],DI RET IOCHK: MOV CX,5 ;Check rest of name but not extension CMP B,[DI],":" JNZ NOCOL INC DI ;Skip over colon DEC CX NOCOL: MOV AL," " REPE SCAB ;Make sure rest of name is blanks JNZ FILSRCH DEC BL RET GETFILE: ; 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 ; 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 RET ;Bad file name? FINDNAME: MOV AX,CS MOV DS,AX MOV SI,IONAME ;List of I/O devices with file names MOV BX,0FF04H ;BL = number of devices LOOKIO: MOV DI,NAME1 MOV CX,3 ;All device names are 3 letters REPE CMPB ;Check for name in list JZ IOCHK ;If first 3 letters OK, check the rest ADD SI,CX ;Point to next device name DEC BL JNZ LOOKIO FILSRCH: ;Not a device name CALL STARTSRCH CONTSRCH: CALL GETENTRY JC RET SRCH: CMP B,[BX],0E5H JZ NEXTENT MOV SI,BX MOV DI,NAME1 MOV CX,11 WILDCRD: REPE CMPB JZ FOUND CMP B,[DI-1],"?" JZ WILDCRD NEXTENT: CALL NEXTENTRY JNC SRCH RET FOUND: IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JZ RET ENDIF ADD SI,15 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 MOV [LASTENT],AX MOV CL,4 SHL AX,CL XOR DX,DX IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JZ SMALENT1 ENDIF SHL AX RCL DX ;Account for overflow in last shift SMALENT1: MOV BX,[BP+SECSIZ] AND BL,255-31 ;Must be multiple of 32 DIV AX,BX MOV BX,DX ;Position within sector MOV AH,[BP+DRVNUM] ;AL=Directory sector no. CMP AX,[DIRBUFID] JZ HAVDIRBUF PUSH BX CALL DIRREAD POP BX HAVDIRBUF: MOV DX,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 IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JNZ BIGENT3 SUB BX,16 BIGENT3: ENDIF CMP BX,DX JB HAVIT INC AL ;Next directory sector PUSH DX ;Save limit CALL DIRREAD POP DX MOV BX,DIRBUF HAVIT: CLC RET NONE: CALL CHKDIRWRITE STC RET DELETE: ; System call 19 CALL GETFILE JC ERRET CMP BH,-1 ;Check if device name JZ ERRET ;Can't delete I/O devices DELFILE: MOV B,[DIRTYDIR],-1 MOV B,[BX],0E5H MOV BX,[SI] LEA 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 CMP BH,-1 ;Check if I/O device name JZ ERRET ;If so, can't rename it ADD SI,5 MOV DI,NAME2 CALL LODNAME CALL FINDNAME JC ERRET RENFIL: MOV B,[DIRTYDIR],-1 MOV DI,BX MOV SI,NAME2 MOV CX,11 NEWNAM: LODB CMP AL,"?" JZ NOCHG MOV [DI],AL NOCHG: INC DI LOOP NEWNAM CALL CONTSRCH JNC RENFIL CALL CHKDIRWRITE XOR AL,AL RET ERRET: MOV AL,-1 RET MOVNAME: ; Inputs: ; DS, DX point to FCB ; Outputs: ; 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 AX,CS MOV ES,AX MOV DI,NAME1 MOV SI,DX LODB CALL GETBP JB RET LODNAME: ; This entry point copies a file name from DS,SI ; to ES,DI converting to upper case. MOV CX,11 MOVCHK: LODB AND AL,7FH CMP AL,60H JLE CASEOK AND AL,5FH CASEOK: CMP AL,20H JC RET STOB LOOP MOVCHK RET GETBP: SEG CS CMP [NUMDRV],AL JC RET CBW XCHG BP,AX SHL BP MOV BP,[BP+CURDRVPT] RET OPEN: ;System call 15 PUSH DX PUSH DS CALL GETFILE DOOPEN: ; Enter here to perform OPEN on file already found ; in directory. DS=ES=CS, BX points to directory ; entry in DIRBUF, SI points to First Cluster field, and ; the top of the stack has the address and segment ; of the FCB to be opened. This entry point is used ; by CREATE. POP ES POP DI JC ERRET CMP BH,-1 ;Check if file is I/O device JZ OPENDEV ;Special handler if so MOV AL,[BP+DRVNUM] INC AL STOB ADD DI,11 ;Point to extent field XOR AX,AX STOW ;Set extent field to 0 MOV AX,128 ;Default record size STOW ;Set record size LODW ;Get starting cluster MOV DX,AX ;Save it for the moment MOVW ;Transfer size to FCB MOVW MOV AX,[SI-8] ;Get date IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JNZ BIGENT10 SEG ES MOV [DI-1],0 XOR AX,AX ;Date not available in small entry BIGENT10: ENDIF STOW ;Save date in FCB MOV AX,[LASTENT] STOW ;directory location MOV AX,DX ;Restore starting cluster STOW ; first cluster STOW ; last cluster accessed XOR AX,AX STOW ; position of last cluster STOB ; dirty flag RET OPENDEV: SEG ES MOV [DI+FILDIRBLK],BX ;Use dir. entry number as flag XOR AL,AL RET STARTSRCH: MOV [LASTENT],-1 FATREAD: ; Inputs: ; DS = CS ; BP = Base of drive parameters ; Function: ; If disk may have been changed, FAT is read in and buffers are ; flagged invalid. If not, no action is taken. ; Outputs: ; AL = 0 ; BP unchanged ; All other registers destroyed MOV AL,[BP+DRVNUM] CALL BIOSDSKCHG,BIOSSEG ;See what BIOS has to say MOV AL,[BP+DRVNUM] OR AH,[BP+DIRTYFAT] JS NEWDSK ;If either say new disk, then it's so DEC AH ;Check for AH=1 (disk not changed) JZ RET MOV AH,1 CMP AX,[BUFDRVNO] ;Does buffer have dirty sector of this drive? JZ RET ;If so, disk has not been changed 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 [BUFDRVNO],00FFH BUFOK: MOV [DIRBUFID],-1 CALL FIGFAT NEXTFAT: PUSH DX PUSH CX PUSH BX PUSH AX CALL DREAD OR AL,AL POP AX POP BX POP CX POP DX JNZ BADFAT IF SMALLDIR MOV DL,AL LEA SI,[BP+FIRREC1] ;FIRREC and MAXCLUS for 16-byte dir entries CMP B,[BP+DIRSIZ],-1 JZ SETSIZ ADD SI,4 ;FIRREC and MAXCLUS for 32-byte entries SETSIZ: LODW MOV [BP+FIRREC],AX LODW MOV [BP+MAXCLUS],AX MOV AL,DL ENDIF SUB AL,[BP+FATCNT] JZ RET NEG AL ;{Insert error code here. AL=number of bad fats. ;Since one good FAT was read, should include option ;to rewrite all FATs.} JMP FATWRT BADFAT: ADD DX,CX DEC AL JNZ NEXTFAT POP BP ;{Insert error code here. All FATs on drive are bad.} MOV SI,BADFATMES CALL HARDERR JP FATREAD OKRET1: MOV AL,0 RET CLOSE: ;System call 16 MOV DI,DX CMP B,[DI+FILDIRBLK],-1 ;Check for I/O device JZ OKRET1 ;Can't close I/O device TEST B,[DI+DIRTYFIL],-1 JZ OKRET1 ;If not written to, do nothing MOV AL,[DI] ;Get drive number CALL GETBP ;Get base of drive parameters JC BADCLOSEJ MOV AL,[BP+DRVNUM] MOV AH,1 ;Look for dirty buffer SEG CS CMP AX,[BUFDRVNO] JNZ FNDDIR ;Write back dirty buffer if on same drive PUSH DX PUSH DS PUSH CS POP DS MOV B,[DIRTYBUF],0 MOV BX,[BUFFER] MOV CX,1 MOV DX,[BUFSECNO] CALL DWRITE POP DS POP DX FNDDIR: PUSH DX PUSH DS CALL GETFILE POP ES POP DI BADCLOSEJ: JC BADCLOSE MOV AX,[LASTENT] SEG ES CMP AX,[DI+FILDIRBLK] JNZ BADCLOSE SEG ES MOV CX,[DI+FIRCLUS] MOV [SI],CX SEG ES MOV DX,[DI+FILSIZ] MOV [SI+2],DX SEG ES MOV DX,[DI+FILSIZ+2] IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JNZ BIGENT11 MOV [SI+4],DL JP SMALLENT2 BIGENT11: ENDIF MOV [SI+4],DX SEG ES MOV DX,[DI+FDATE] MOV [SI-2],DX SMALLENT2: CALL DIRWRITE CHKFATWRT: ; Do FATWRT only if FAT is dirty CMP B,[BP+DIRTYFAT],1 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 MOV B,[BP+DIRTYFAT],0 CALL FIGFAT 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 B,[BP+DIRTYFAT],0 MOV AL,-1 RET FIGFAT: ; Loads registers with values needed to read or ; write a FAT. MOV AL,[BP+FATCNT] LEA 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,DIRBUF MOV CX,1 RET CREATE: ;System call 22 CALL MOVNAME JC ERRET3 MOV DI,NAME1 MOV CX,11 MOV AL,"?" REPNE SCAB JZ ERRET3 PUSH DX PUSH DS CALL FINDNAME JNC EXISTENT CALL STARTSRCH CALL GETENTRY LOOKFRE: CMP B,[BX],0E5H JZ FREESPOT CALL NEXTENTRY JNC LOOKFRE POP DS POP DX ERRET3: MOV AL,-1 RET EXISTENT: CMP BH,-1 ;Check if file is I/O device JZ OPENJMP ;If so, no action XOR CX,CX MOV [SI+2],CX MOV AX,[DATE] IF SMALLDIR MOV [SI+4],CL CMP B,[BP+DIRSIZ],-1 JZ SMLENT MOV [SI+5],CL MOV [SI-2],AX SMLENT: ENDIF IF 1-SMALLDIR MOV [SI+4],CX MOV [SI-2],AX ENDIF XCHG CX,[SI] PUSH SI PUSH BX JCXZ WRTBACK CMP CX,[BP+MAXCLUS] JA WRTBACK MOV BX,CX LEA SI,[BP+FAT] CALL RELEASE CALL FATWRT JP WRTBACK FREESPOT: MOV DI,BX MOV SI,NAME1 MOV CX,5 MOVB REP MOVW XOR AX,AX IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JNZ BIGENT4 PUSH DI MOV CL,5 JP SMALLENT BIGENT4: ENDIF MOV CL,13 REP STOB MOV AX,[DATE] STOW XOR AX,AX PUSH DI MOV CL,6 SMALLENT: REP STOB PUSH BX WRTBACK: CALL DIRWRITE POP BX POP SI OPENJMP: CLC ;Clear carry so OPEN won't fail 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+DRVNUM] MOV [DIRBUFID],AX CALL DIRCOMP CALL DREAD POP AX 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. ; Outputs: ; AL = 0 if no error, otherwise non-zero ; BP preserved. All other registers destroyed. MOV AL,[BP+DRVNUM] PUSH BP PUSH BX PUSH CX PUSH DX CALL BIOSREAD,BIOSSEG POP DX POP DI POP BX POP BP JC HARDREAD XOR AL,AL RET HARDREAD: MOV SI,RDERRMES CALL HARDERR JP DREAD CHKDIRWRITE: TEST B,[DIRTYDIR],-1 JZ RET 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 B,[DIRTYDIR],0 MOV AL,[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. ; Outputs: ; AL = 0 if no error, otherwise non-zero ; BP preserved. All other registers destroyed. MOV AL,[BP+DRVNUM] WRTDRV: PUSH AX PUSH BP PUSH BX PUSH CX PUSH DX CALL BIOSWRITE,BIOSSEG POP DX POP DI POP BX POP BP POP AX JC HARDWRITE XOR AL,AL RET HARDWRITE: MOV SI,WRTERRMES CALL HARDERR JP WRTDRV HARDERR: SUB DI,CX ADD DX,DI CALL SHFTDI7 ADD BX,DI MOV AH,AL ;Save drive number CALL OUTMES GETINSTR: CALL IN OR AL,20H CMP AL,"a" JZ ERROR CMP AL,"r" JZ RETRY CMP AL,"i" JZ IGNORE CMP AL,"c" JNZ GETINSTR CONTINUE: POP AX MOV AL,1 RET IGNORE: POP AX MOV AL,0 RET RETRY: MOV AL,AH ;Restore drive number RET ABORT: SEG CS MOV DS,[CSLOC] XOR AX,AX MOV ES,AX MOV SI,SAVEXIT MOV DI,EXIT MOVW MOVW MOVW MOVW ERROR: MOV AX,CS MOV DS,AX MOV ES,AX CALL WRTFATS XOR AX,AX DI MOV SS,[SSSAVE] MOV SP,[SPSAVE] MOV DS,AX MOV SI,EXIT MOV DI,EXITHOLD MOVW MOVW POP AX POP BX POP CX POP DX POP SI POP DI POP BP POP DS POP ES EI ;Stack OK now SEG CS JMP L,[EXITHOLD] SEQRD: ;System call 20 CALL GETREC CALL LOAD JP FINSEQ SEQWRT: ;System call 21 CALL GETREC CALL STORE FINSEQ: JCXZ SETNREX ADD AX,1 ADC DX,0 JP SETNREX RNDRD: ;System call 33 CALL GETRRPOS1 CALL LOAD JP FINRND RNDWRT: ;System call 34 CALL GETRRPOS1 CALL STORE JP FINRND BLKRD: ;System call 39 CALL GETRRPOS CALL LOAD JP FINBLK BLKWRT: ;System call 40 CALL GETRRPOS CALL STORE FINBLK: LDS SI,[SPSAVE] MOV [SI+CXSAVE],CX JCXZ FINRND ADD AX,1 ADC DX,0 FINRND: SEG ES MOV [DI+RR],AX SEG ES MOV [DI+RR+2],DL OR DH,DH JZ SETNREX SEG ES MOV [DI+RR+3],DH ;Save 4 byte of RECPOS only if significant SETNREX: MOV CX,AX AND AL,7FH SEG ES MOV [DI+NR],AL AND CL,80H SHL CX RCL DX MOV AL,CH MOV AH,DL SEG ES MOV [DI+EXTENT],AX SEG CS MOV AL,[DSKERR] RET GETRRPOS1: MOV CX,1 GETRRPOS: MOV DI,DX MOV AX,[DI+RR] MOV DX,[DI+RR+2] RET NOFILERR: XOR CX,CX MOV B,[DSKERR],-2 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 ; 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) ; If SETUP detects no records will be transfered, it returns 1 level up ; with CX = 0. PUSH AX MOV AL,[DI] MOV SI,[DI+RECSIZ] OR SI,SI JNZ HAVRECSIZ MOV SI,128 MOV [DI+RECSIZ],SI HAVRECSIZ: MOV BX,DS MOV ES,BX MOV BX,CS MOV DS,BX 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 [RECPOS],AX MOV [RECPOS+2],DX MOV [FCB],DI MOV BX,[DMAADD] MOV [NEXTADD],BX MOV B,[DSKERR],0 MOV B,[TRANS],0 MOV BX,DX MUL AX,SI MOV [BYTPOS],AX PUSH DX MOV AX,BX MUL AX,SI POP BX ADD AX,BX ADC DX,0 ;Ripple carry JNZ EOFERR MOV [BYTPOS+2],AX MOV DX,AX MOV AX,[BYTPOS] DIV AX,[BP+SECSIZ] 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 AX,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 stay within 64K MOV AX,[DMAADD] NEG AX ;Amount of room left in segment XOR DX,DX DIV AX,SI ;How many records will fit? MUL AX,SI ;Translate that back into bytes MOV B,[DSKERR],2 ;Flag that trimming took place MOV CX,AX JCXZ NOROOM OK: LEA SI,[BP+FAT] RET EOFERR: MOV B,[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 AX,[BP+SECSIZ] ;How many whole sectors? MOV [SECCNT],AX MOV [BYTCNT2],DX ;Bytes remaining for last sector 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. SEG ES MOV BX,[DI+LSTCLUS] SEG ES MOV DX,[DI+CLUSPOS] OR BX,BX JZ NOCLUS SUB CX,DX JNB FINDIT ADD CX,DX XOR DX,DX SEG ES MOV BX,[DI+FIRCLUS] FINDIT: JCXZ RET SKPCLP: CALL UNPACK CMP DI,0FF8H JAE RET 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 OR AL,AL JNZ SETBUF CMP DX,[BUFSECNO] JNZ GETSEC MOV AL,[BUFDRVNO] CMP AL,[BP+DRVNUM] JZ FINBUF ;Already have it? GETSEC: TEST B,[DIRTYBUF],-1 JZ RDSEC PUSH DX MOV AL,[BUFDRVNO] MOV BX,[BUFFER] MOV CX,1 MOV DX,[BUFSECNO] CALL WRTDRV POP DX RDSEC: MOV BX,[BUFFER] MOV CX,1 PUSH DX CALL DREAD POP DX SETBUF: MOV [BUFSECNO],DX MOV AL,[BP+DRVNUM] MOV AH,0 MOV [BUFDRVNO],AX FINBUF: MOV B,[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 JNC EVENRD MOVB EVENRD: REP MOVW 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 JNC EVENWRT MOVB EVENWRT: REP MOVW POP ES POP DS MOV B,[DIRTYBUF],1 RET NEXTSEC: TEST B,[TRANS],-1 JZ CLRET MOV AL,[SECCLUSPOS] INC AL CMP AL,[BP+CLUSMSK] JBE SAVPOS MOV BX,[CLUSNUM] CMP BX,0FF8H JAE NONEXT LEA SI,[BP+FAT] CALL UNPACK MOV [CLUSNUM],DI INC [LASTPOS] MOV AL,0 SAVPOS: MOV [SECCLUSPOS],AL CLRET: CLC RET NONEXT: STC RET TRANBUF: LODB STOB CMP AL,13 ;Check for carriage return JNZ NORMCH MOV [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] SEG ES OR B,[DI+FILDIRBLK],80H ;Mark as no more data available SETFCBJ: JMP SETFCB READDEV: PUSH ES LES DI,[DMAADD] OR BL,BL JZ READCON DEC BL JNZ ENDRDDEV READAUX: CALL BIOSAUXIN,BIOSSEG STOB CMP AL,1AH LOOPNZ READAUX JP ENDRDDEV READCON: PUSH CS POP DS MOV SI,[CONTPOS] OR SI,SI JNZ TRANBUF CMP B,[CONBUF],128 JZ GETBUF MOV [CONBUF],0FF80H ;Set up 128-byte buffer with no template GETBUF: PUSH CX PUSH ES PUSH DI MOV DX,CONBUF CALL BUFIN ;Get input buffer POP DI POP ES POP CX MOV SI,CONBUF+2 CMP B,[SI],1AH ;Check for Ctrl-Z in first character JNZ TRANBUF MOV AL,1AH STOB MOV AL,10 CALL OUT ;Send linefeed XOR SI,SI JP 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 SEG ES MOV BX,[DI+FILDIRBLK] CMP BH,-1 ;Check for named device I/O JZ READDEV SEG ES MOV AX,[DI+FILSIZ] SEG ES MOV BX,[DI+FILSIZ+2] SUB AX,[BYTPOS] SBC BX,[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 B,[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] CALL DREAD POP DS POP CX POP BX JCXZ RDLAST CMP BX,0FF8H JAE SETFCB MOV DL,0 INC [LASTPOS] ;We'll be using next cluster JP RDLP SETFCB: MOV SI,[FCB] MOV AX,[NEXTADD] MOV DI,AX SUB AX,[DMAADD] ;Number of bytes transfered XOR DX,DX SEG ES MOV CX,[SI+RECSIZ] DIV AX,CX ;Number of records CMP AX,[RECCNT] ;Check if all records transferred JZ FULLREC MOV B,[DSKERR],1 OR DX,DX JZ FULLREC ;If remainder 0, then full record transfered MOV B,[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 JNC EVENFIL STOB EVENFIL: REP STOW 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] SEG ES MOV [DI+LSTCLUS],AX MOV AX,[LASTPOS] SEG ES MOV [DI+CLUSPOS],AX ADDREC: MOV AX,[RECPOS] MOV DX,[RECPOS+2] DEC CX ADD AX,CX ;Update current record position ADC DX,0 INC CX RET RDLAST: MOV AX,[BYTCNT2] OR AX,AX JZ SETFCB MOV [BYTCNT1],AX CALL NEXTSEC JC SETFCB MOV [BYTSECPOS],0 CALL BUFRD JP SETFCB WRTDEV: PUSH DS LDS SI,[DMAADD] AND BL,7FH OR BL,BL JZ WRTCON DEC BL JZ WRTAUX WRTLST: LODB CMP AL,1AH JZ ENDWRDEV CALL BIOSPRINT,BIOSSEG LOOP WRTLST JP ENDWRDEV WRTAUX: LODB CALL BIOSAUXOUT,BIOSSEG CMP AL,1AH LOOPNZ WRTAUX JP ENDWRDEV WRTCON: LODB CMP AL,1AH JZ ENDWRDEV CALL OUT LOOP WRTCON ENDWRDEV: POP DS MOV CX,[RECCNT] MOV DI,[FCB] JP ADDREC HAVSTART: MOV CX,AX CALL SKPCLP JCXZ DOWRTJ CALL ALLOCATE JNC DOWRTJ WRTERR: MOV B,[DSKERR],1 LVDSK: MOV AX,[RECPOS] MOV DX,[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 MOV B,[DI+DIRTYFIL],1 SEG CS MOV BX,[DATE] MOV [DI+FDATE],BX CALL SETUP SEG ES MOV BX,[DI+FILDIRBLK] CMP BH,-1 JZ WRTDEV CALL BREAKDOWN MOV AX,[BYTPOS] MOV DX,[BYTPOS+2] JCXZ WRTEOFJ DEC CX ADD AX,CX ADC DX,0 ;AX:DX=last byte accessed DIV AX,[BP+SECSIZ] ;AX=last sector accessed MOV CL,[BP+CLUSSHFT] SHR AX,CL ;Last cluster to be accessed PUSH AX SEG ES MOV AX,[DI+FILSIZ] SEG ES MOV DX,[DI+FILSIZ] DIV AX,[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 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 B,[TRANS],1 ;A transfer is taking place MOV DL,[SECCLUSPOS] MOV BX,[CLUSNUM] MOV CX,[SECCNT] WRTLP: CALL OPTIMIZE 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 JP 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,[BYTPOS] MOV DX,[BYTPOS+2] ADC DX,0 MOV CX,DX MOV DI,[FCB] SEG ES CMP AX,[DI+FILSIZ] SEG ES SBB CX,[DI+FILSIZ+2] JB SAMSIZ SEG ES MOV [DI+FILSIZ],AX SEG ES MOV [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 SBC DX,0 DIV AX,[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,[BYTPOS] SEG ES MOV [DI+FILSIZ],AX MOV AX,[BYTPOS+2] SEG ES MOV [DI+FILSIZ+2],AX XOR CX,CX RET RELFILE: MOV DX,0FFFH CALL RELBLKS SETDIRT: MOV B,[BP+DIRTYFAT],1 JP UPDATE KILLFIL: XOR BX,BX SEG ES XCHG BX,[DI+FIRCLUS] OR BX,BX JZ UPDATE CALL RELEASE JP 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 ; [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 LEA 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 AX,[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 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 JP 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 MOV CX,1 MOV AL,[DI+NR] MOV DX,[DI+EXTENT] SHL AL SHR DX RCR AL 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 AX,DX ;AX=max no. of records in file MOV CX,AX SUB CX,[RECPOS] ;CX=max no. of records that could be written JA MAXREC XOR CX,CX ;If CX was negative, zero it MAXREC: STC RET TRYOUT: CALL UNPACK JZ HAVFRE TRYIN: DEC AX JLE FINDFRE XCHG AX,BX CALL UNPACK JZ HAVFRE XCHG AX,BX JP FINDFRE HAVFRE: XCHG BX,DX MOV AX,DX CALL PACK MOV BX,AX LOOP ALLOC MOV DX,0FFFH CALL PACK MOV B,[BP+DIRTYFAT],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 RET MOV DI,[FCB] SEG ES MOV [DI+FIRCLUS],BX 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 RET MOV AX,DI CALL PACK CMP AX,0FF8H MOV BX,AX JB RELEASE 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 RET MOV BX,DI JP GETEOF SRCHFRST: ;System call 17 PUSH DX PUSH DS CALL GETFILE SAVPLCE: ; Search-for-next enters here to save place and report ; findings. POP ES POP DI JC KILLSRCH CMP BH,-1 JZ SRCHDEV MOV AX,[LASTENT] SEG ES MOV [DI+FILDIRBLK],AX ;Information in directory entry must be copied into the first ; 33 bytes starting at the disk transfer address. MOV SI,BX LES DI,[DMAADD] MOV AL,[BP+DRVNUM] INC AL STOB ;Set drive number MOVB ;Copy first character of name MOV CX,5 REP MOVW ;Copy remaining 10 characters of name XOR AX,AX IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JNZ BIGENT5 STOB ;Zero out unused portion MOV CX,7 REP STOW ;Zero a total of 15 bytes MOVW ;Copy first cluster pointer MOVW ;Copy low word of length MOVB ;Copy 3rd byte of length STOB ;4th byte of length must be zero RET BIGENT5: ENDIF MOV CX,10 REP MOVW MOVB RET KILLSRCH: SEG ES KILLSRCH1: MOV [DI+FILDIRBLK],-2 MOV AL,-1 RET SRCHDEV: SEG ES MOV [DI+FILDIRBLK],BX LES DI,[DMAADD] XOR AX,AX STOB ;Zero drive byte SUB SI,3 ;Point to device name MOVW MOVB MOV AX,2020H MOV CX,4 REP STOW ;Fill with 8 blanks XOR AX,AX MOV CX,10 REP STOW STOB RET SRCHNXT: ;System call 18 CALL MOVNAME MOV DI,DX JC KILLSRCH1 PUSH DX PUSH DS MOV AX,[DI+FILDIRBLK] PUSH CS POP DS MOV [LASTENT],AX CALL CONTSRCH JMP SAVPLCE FILESIZE: ;System call 35 PUSH DS PUSH DX CALL GETFILE POP DI POP ES MOV AL,-1 JC RET ADD DI,33 ;Write size in RR field SEG ES MOV CX,[DI-33+RECSIZ] OR CX,CX JNZ RECOK MOV CX,128 RECOK: XOR AX,AX XOR DX,DX ;Intialize size to zero CMP BH,-1 ;Check for named I/O device JZ DEVSIZ INC SI INC SI ;Point to length field MOV AX,[SI+2] ;Get high word of size IF SMALLDIR CMP B,[BP+DIRSIZ],-1 JNZ BIGSIZ MOV AH,0 BIGSIZ: ENDIF DIV AX,CX PUSH AX ;Save high part of result LODW ;Get low word of size DIV AX,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: STOW MOV AX,DX STOB MOV AL,0 CMP CX,64 JAE RET ;Only 3-byte field if RECSIZ >= 64 SEG ES MOV [DI],AH RET SETDMA: ;System call 26 SEG CS MOV [DMAADD],DX SEG CS MOV [DMAADD+2],DS RET GETFATPT: ;System call 27 MOV AX,CS MOV DS,AX MOV BP,[CURDRVPT] CALL FATREAD LEA BX,[BP+FAT] MOV AL,[BP+CLUSMSK] INC AL MOV DX,[BP+MAXCLUS] DEC DX MOV B,[BP+DIRTYFAT],1 MOV CX,[BP+SECSIZ] LDS SI,[SPSAVE] MOV [SI+BXSAVE],BX MOV [SI+DXSAVE],DX MOV [SI+CXSAVE],CX MOV [SI+DSSAVE],CS RET GETDSKPT: ;System call 31 SEG CS MOV BX,[CURDRVPT] SEG CS LDS SI,[SPSAVE] MOV [SI+BXSAVE],BX MOV [SI+DSSAVE],CS RET DSKRESET: ;System call 13 SEG CS MOV [DMAADD+2],DS MOV AX,CS MOV DS,AX MOV [DMAADD],80H MOV AX,[CURDRVPT+2] MOV [CURDRVPT],AX WRTFATS: ; DS=CS. Writes back all dirty FATs. All registers destroyed. MOV CL,[NUMDRV] MOV CH,0 MOV SI,CURDRVPT+2 WRTFAT: LODW PUSH CX PUSH SI MOV BP,AX CALL CHKFATWRT POP SI POP CX LOOP WRTFAT MOV AX,[BUFDRVNO] OR AH,AH JZ RET CBW MOV BX,AX SHL BX MOV BP,[BX+DRVTAB] MOV B,[DIRTYBUF],0 MOV DX,[BUFSECNO] MOV BX,BUFFER MOV CX,1 JMP DWRITE CURDRV: ;System call 25 SEG CS MOV BP,[CURDRVPT] MOV AL,[BP+DRVNUM] RET INUSE: ;System call 24 MOV AX,CS MOV DS,AX MOV CL,[NUMDRV] MOV CH,0 MOV SI,CX SHL SI ADD SI,CURDRVPT MOV BX,0 DOWN CHKUSE: LODW MOV BP,AX CMP B,[BP+DIRTYFAT],-1 ;Carry set if not equal RCL BX LOOP CHKUSE MOV AL,BL RET SETRNDREC: ;System call 36 CALL GETREC MOV [DI+33],AX MOV [DI+35],DL RET SELDSK: ;System call 14 MOV DH,0 MOV BX,DX PUSH CS POP DS MOV AL,[NUMDRV] CMP BL,AL JNB RET SHL BX MOV DX,[BX+CURDRVPT+2] MOV [CURDRVPT],DX RET BUFIN: ;System call 10 MOV AX,CS MOV ES,AX MOV SI,DX MOV CH,0 LODW OR AL,AL JZ RET MOV BL,AH MOV BH,CH CMP AL,BL JBE NOEDIT CMP B,[BX+SI],0DH JZ EDITON NOEDIT: MOV BL,CH EDITON: MOV DL,AL DEC DX NEWLIN: SEG CS MOV AL,[CARPOS] SEG CS MOV [STARTPOS],AL PUSH SI MOV DI,INBUF MOV AH,CH MOV BH,CH MOV DH,CH GETCH: CALL IN CMP AL,7FH JZ BACKSP CMP AL,8 JZ BACKSP CMP AL,13 JZ ENDLIN CMP AL,10 JZ PHYCRLF CMP AL,"X"-"@" JZ KILNEW CMP AL,ESCCH JZ ESC SAVCH: CMP DH,DL JAE GETCH STOB INC DH CALL BUFOUT OR AH,AH JNZ GETCH CMP BH,BL JAE GETCH INC SI INC BH JP GETCH ESC: CALL IN MOV CL,ESCTABLEN PUSH DI MOV DI,ESCTAB REPNE SCAB POP DI AND CL,0FEH MOV BP,CX SEG CS ;To work in init JMP [BP+ESCFUNC] ENDLIN: STOB 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,INBUF MOV CL,DH REP MOVB RET CRLF: MOV AL,13 CALL OUT MOV AL,10 JMP OUT PHYCRLF: CALL CRLF JP GETCH KILNEW: MOV AL,"\" CALL OUT POP SI PUTNEW: CALL CRLF SEG CS MOV AL,[STARTPOS] CALL TAB JMP NEWLIN BACKSP: OR DH,DH JZ OLDBAK CALL BACKUP SEG ES MOV AL,[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 DOWN MOV CL,DH MOV AL," " PUSH BX MOV BL,7 JCXZ FIGTAB FNDPOS: SCAB JNA CHKCNT SEG ES CMP B,[DI+1],9 JZ HAVTAB DEC BL CHKCNT: LOOP FNDPOS FIGTAB: SEG CS SUB BL,[STARTPOS] HAVTAB: SUB BL,DH ADD CL,BL AND CL,7 UP POP BX POP DI JZ OLDBAK TABBAK: CALL BACKMES LOOP TABBAK JP 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 JP COPYEACH COPYSTR: CALL FINDOLD JP COPYEACH COPYONE: MOV CL,1 COPYEACH: CMP DH,DL JZ GETCH2 CMP BH,BL JZ GETCH2 LODB STOB 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 SCAB POP DI POP ES JNZ NOTFND NOT CL ADD CL,BL SUB CL,BH 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: MOV AH,-1 JMP GETCH EXITINS: MOV AH,0 JMP GETCH ESCFUNC: DW GETCH DW TWOESC DW EXITINS DW ENTERINS DW BACKSP DW REEDIT DW KILNEW DW COPYLIN DW SKIPSTR DW COPYSTR DW SKIPONE DW COPYONE BUFOUT: CMP AL," " JAE OUT CMP AL,9 JZ OUT PUSH AX MOV AL,"^" CALL OUT POP AX OR AL,40H JP OUT CONOUT: ;System call 2 MOV AL,DL OUT: CMP AL,20H JB CTRLOUT CMP AL,7FH JZ OUTCH SEG CS INC B,[CARPOS] OUTCH: CALL BIOSOUT,BIOSSEG SEG CS TEST B,[PFLAG],-1 JZ STATCHK CALL BIOSPRINT,BIOSSEG STATCHK: CALL BIOSSTAT,BIOSSEG JZ RET INCHK: CALL BIOSIN,BIOSSEG CMP AL,'S'-'@' JNZ NOSTOP CALL BIOSIN,BIOSSEG NOSTOP: CMP AL,'P'-'@' JZ PRINTON CMP AL,'N'-'@' JZ PRINTOFF CMP AL,'C'-'@' JNZ RET ; Ctrl-C handler. ; If function 9 or 10 was in progress, a backslash, CR, and LF are printed ; to show line is canceled. 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, except for console output (function 2), which has already output ; its character before the interrupt. SEG CS MOV AL,[FUNC] ;Get currently executing function CMP AL,10 ;Check if buffered line input JZ CANLIN CMP AL,9 ;Check if buffered line output JNZ RESTREG CANLIN: MOV AL,"\" ;Display cancel line symbol CALL OUT CALL CRLF RESTREG: DI ;Prepare to play with stack SEG CS MOV SS,[SSSAVE] SEG CS MOV SP,[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 CMP AH,2 ;Check if function was console output JZ SETAL JMP COMMAND ;Repeat command otherwise SETAL: MOV AL,DL IRET ;Return to user program PRINTON: SEG CS MOV B,[PFLAG],1 RET PRINTOFF: SEG CS MOV B,[PFLAG],0 RET CTRLOUT: CMP AL,13 JZ ZERPOS CMP AL,8 JZ BACKPOS CMP AL,9 JNZ OUTCHJ SEG CS MOV AL,[CARPOS] OR AL,0F8H NEG AL TAB: PUSH CX MOV CL,AL MOV CH,0 TABLP: MOV AL," " CALL OUT LOOP TABLP POP CX RET ZERPOS: SEG CS MOV B,[CARPOS],0 OUTCHJ: JMP OUTCH BACKPOS: SEG CS DEC B,[CARPOS] JMP OUTCH CONSTAT: ;System call 11 CALL BIOSSTAT,BIOSSEG JZ RET OR AL,-1 RET CONIN: ;System call 1 CALL IN PUSH AX CALL OUT POP AX RET IN: ;Internal input routine CALL INCHK JZ IN RET RAWIO: ;System call 6 MOV AL,DL CMP AL,-1 JNZ RAWOUT CALL BIOSSTAT,BIOSSEG JZ RET RAWINP: ;System call 7 CALL BIOSIN,BIOSSEG RET RAWOUT: CALL BIOSOUT,BIOSSEG RET LIST: ;System call 5 MOV AL,DL CALL BIOSPRINT,BIOSSEG RET PRTBUF: ;System call 9 MOV SI,DX OUTSTR: LODB CMP AL,"$" JZ RET CALL OUT JP OUTSTR OUTMES: ;String output for internal messages SEG CS LODB CMP AL,"$" JZ RET CALL OUT JP OUTMES MAKEFCB: ;Interrupt call 41 MOV DL,0 ;Flag--not ambiguous file name OR AL,AL ;Scan off separators if not zero JZ NOSCAN SCAN: CALL GETLET JZ SCAN ;Get rid of leading separators (e.g., blanks) DEC SI ;Point back to first non-separator NOSCAN: CMP B,[SI+1],":" ;Check for potential drive specifier JNZ DEFAULT CALL GETLET SUB AL,"@" ;Convert drive letter to binary drive number JZ NODRV ;Valid drive numbers are 1-15 INC SI CMP AL,15 JBE HAVDRV DEC SI NODRV: DEC SI ;Invalid drive specifier--back up pointer DEFAULT: XOR AL,AL HAVDRV: STOB ;Put drive specifier in first byte MOV CX,8 CALL GETWORD ;Get 8-letter file name CMP B,[SI],"." JNZ NODOT INC SI ;Skip over dot if present NODOT: MOV CX,3 ;Get 3-letter extension CALL GETWORD SEG CS LDS BX,[SPSAVE] MOV [BX+SISAVE],SI XOR AX,AX STOW STOW MOV AL,DL RET GETWORD: CALL GETLET JZ FILLNAM ;Exit if invalid character CMP AL," " JBE FILLNAM CMP AL,"*" ;Check for ambiguous file specifier JNZ NOSTAR MOV AL,"?" DEC CX REP STOB ;Fill rest of word with "?" INC CX NOSTAR: STOB CMP AL,"?" JNZ NOTQ MOV DL,1 ;Flag ambiguous file name NOTQ: LOOP GETWORD INC SI ;Point to "termination" character FILLNAM: MOV AL," " REP STOB DEC SI RET GETLET: LODB CMP AL,"a" JB CHK CMP AL,"z" JA CHK SUB AL,20H ;Convert to upper case CHK: CMP AL," " JZ RET CMP AL,"=" JZ RET CMP AL,"," JZ RET CMP AL,";" JZ RET CMP AL,"." JZ RET CMP AL,":" JZ RET CMP AL,9 ;Filter out TABs too RET SETVECT: ; Interrupt call 37 XOR BX,BX MOV ES,BX MOV BL,AL SHL BX SHL BX SEG ES MOV [BX],DX SEG ES MOV [BX+2],DS RET NEWBASE: ; Interrupt call 38 MOV ES,DX SEG CS MOV DS,[CSLOC] XOR SI,SI MOV DI,SI MOV CX,80H REP MOVW SETMEM: ; Inputs: ; 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) ; AX,DX,BP unchanged. All other registers destroyed. XOR CX,CX MOV DS,CX MOV ES,DX MOV SI,EXIT MOV DI,SAVEXIT MOVW MOVW MOVW MOVW SEG CS MOV CX,[ENDMEM] SEG ES MOV [2],CX SUB CX,DX CMP CX,MAXDIF JBE HAVDIF MOV CX,MAXDIF HAVDIF: MOV BX,ENTRYPOINTSEG SUB BX,CX SHL CX SHL CX SHL CX SHL CX MOV DS,DX MOV [6],CX MOV [8],BX MOV [0],20CDH ;"INT INTTAB" MOV B,[5],LONGCALL RET SHFTDI7: SHL DI SHL DI SHL DI SHL DI SHL DI SHL DI SHL DI RET ; Default handler for division overflow trap DIVOV: XOR DX,DX ;Say no remainder MOV AX,-1 ;But large quotient IRET ;***** DATA AREA ***** BADMES: DB 13,10,"Bad FAT",13,10,"$" BADFATMES:DB 13,10,"All FATs on disk are bad",13,10,"$" RDERRMES:DB 13,10,"Disk read error",13,10,"$" WRTERRMES:DB 13,10,"Disk write error",13,10,"$" IONAME: DB "PRN","LST","AUX","CON" CARPOS: DB 0 STARTPOS:DB 0 PFLAG: DB 0 DIRTYDIR:DB 0 ;Dirty buffer flag NUMDRV: DS 1 ;Number of drives CONTPOS:DW 0 DMAADD: DW 80H ;User's disk transfer address (disp/seg) DS 2 ENDMEM: DS 2 MAXSEC: DW 0 BUFFER: DS 2 BUFSECNO:DW 0 BUFDRVNO:DB -1 DIRTYBUF:DB 0 DIRBUFID:DW -1 DATE: DS 2 CURDRVPT:DS 2 DRVTAB: DS 30 ;Enough for 15 drives INBUF: DS 15 ;Only the first part ; Init code overlaps with data area below INITCODE: DS 128-15 ;More of INBUF CONBUF: DS 130 ;The rest of INBUF and console buffer FUNC: DS 1 ;Currently executing function code LASTENT:DS 2 EXITHOLD:DS 4 FATBASE:DS 2 NAME1: DS 11 ;File name buffer NAME2: DS 11 TEMP: CSLOC: DS 2 SPSAVE: DS 2 SSSAVE: DS 2 SECCLUSPOS:DS 1 ;Position of first sector within cluster DSKERR: DS 1 TRANS: DS 1 ALIGN FCB: DS 2 ;Address of user FCB NEXTADD:DS 2 RECPOS: DS 4 RECCNT: DS 2 LASTPOS:DS 2 CLUSNUM:DS 2 SECPOS: DS 2 ;Position of first sector accessed VALSEC: DS 2 ;Number of valid (previously written) sectors BYTSECPOS:DS 2 ;Position of first byte within sector BYTPOS: DS 4 ;Byte position in file of access BYTCNT1:DS 2 ;No. of bytes in first sector BYTCNT2:DS 2 ;No. of bytes in last sector SECCNT: DS 2 ;No. of whole sectors DS 60 ;Stack space STACK: IF DSKTEST NSS: DS 2 NSP: DS 2 DS 60 TESTSTK: ENDIF DIRBUF: ;Init code below overlaps with data area above ORG INITCODE PUT $+100H MOVFAT: ;This section of code is safe from being overwritten by block move SEG ES REP MOVB UP FININIT: CALL SETMEM ;Set up segment RET L DOSINIT: DI UP PUSH CS POP ES LODB SEG ES MOV [NUMDRV],AL MOV BX,DRVTAB MOV DI,MEMSTRT PERDRV: SEG ES MOV [BX],DI MOV BP,DI INC BX INC BX SEG ES MOV AL,[DRVCNT] STOB ;DRVNUM LODW ;Pointer to DPT PUSH SI MOV SI,AX LODW STOW ;SECSIZ MOV DX,AX SEG ES CMP AX,[MAXSEC] JBE NOTMAX SEG ES MOV [MAXSEC],AX NOTMAX: LODB DEC AL STOB ;CLUSMSK JZ HAVSHFT CBW FIGSHFT: INC AH SAR AL JNZ FIGSHFT MOV AL,AH HAVSHFT: STOB ;CLUSSHFT MOVW ;FIRFAT (= number of reserved sectors) MOVB ;FATCNT MOVW ;MAXENT MOV AX,DX ;SECSIZ again MOV CL,5 SHR AX,CL MOV CX,AX ;Directory entries per sector DEC AX SEG ES ADD AX,[BP+MAXENT] XOR DX,DX DIV AX,CX STOW ;DIRSEC (temporarily) SHR AX ;Divide by two ADC AX,0 ;Round up SEG ES MOV [SDIRSEC],AX ;Number of directory records for small entries MOVW ;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 SEG ES DEC [BP+DSKSIZ] ;Damp those oscillations JP FNDFATSIZ ;Try again HAVFATSIZ: STOB ;FATSIZ SEG ES MUL AL,[BP+FATCNT] ;Space occupied by all FATs SEG ES ADD AX,[BP+FIRFAT] STOW ;FIRDIR IF SMALLDIR-1 SEG ES ADD AX,[BP+DIRSEC] SEG ES MOV [BP+FIRREC],AX ;Destroys DIRSEC CALL FIGMAX SEG ES MOV [BP+MAXCLUS],CX ENDIF IF SMALLDIR MOV DX,AX ;Save FIRDIR momentarily SEG ES ADD AX,[SDIRSEC] ;Add number of dir. sectors for 16-byte entry STOW ;FIRREC1 XCHG AX,CX ;Previously computed MAXCLUS STOW ;MAXCLUS1 XCHG DX,AX SEG ES ADD AX,[BP+DIRSEC] ;Add number of dir. sectores for 32-byte entry STOW ;FIRREC2 CALL FIGMAX XCHG AX,CX STOW ;MAXCLUS2 ENDIF MOV AX,0FFH STOB ;DIRTYFAT SEG ES MOV AL,[BP+FATSIZ] SEG ES MUL AX,[BP+SECSIZ] ADD DI,AX ;Allocate FAT POP SI ;Restore pointer to init. table SEG ES MOV AL,[DRVCNT] INC AL SEG ES MOV [DRVCNT],AL SEG ES CMP AL,[NUMDRV] JAE CONTINIT JMP PERDRV CONTINIT: LODW ;Max. buffer size SEG ES MOV BX,[MAXSEC] MOV AX,DIRBUF ADD AX,BX SEG ES MOV [BUFFER],AX ;Start of buffer PUSH DI ;Save ending location ADD DI,BX ;Allocate directory buffer ADD DI,BX ;Allocate buffer space ADD DI,ADJFAC+15 ;True start of free space MOV CL,4 SHR DI,CL ;First free segment MOV BP,DI XOR AX,AX MOV DS,AX MOV ES,AX MOV DI,INTBASE MOV AX,QUIT STOW ;Set abort address--displacement MOV AX,CS MOV B,[ENTRYPOINT],LONGJUMP MOV [ENTRYPOINT+1],ENTRY MOV [ENTRYPOINT+3],AX MOV [0],DIVOV ;Set default divide trap address MOV [2],AX MOV CX,9 REP STOW ;Set 5 segments (skip 2 between each) MOV [INTBASE+4],COMMAND MOV [INTBASE+12],IRET ;CTRL-C exit MOV [INTBASE+16],IRET ;Fatal error exit MOV AX,BIOSREAD STOW MOV AX,BIOSSEG STOW STOW ;Add 2 to DI STOW MOV [INTBASE+18H],BIOSWRITE MOV DX,CS MOV DS,DX ADD DX,BP MOV [DMAADD],80H MOV [DMAADD+2],DX MOV AX,[DRVTAB] MOV [CURDRVPT],AX MOV CX,DX MOV BX,0FH MEMSCAN: INC CX JZ HAVMEM MOV DS,CX MOV AL,[BX] NOT AL MOV [BX],AL CMP AL,[BX] NOT AL MOV [BX],AL JZ MEMSCAN HAVMEM: SEG CS MOV [ENDMEM],CX XOR CX,CX MOV DS,CX MOV [EXIT],100H MOV [EXIT+2],DX MOV SI,HEADER CALL OUTMES PUSH CS POP DS PUSH CS POP ES PUSH DX GETDAT: MOV SI,DATMES CALL OUTMES MOV DX,DATBUF CALL BUFIN CALL CRLF MOV SI,DATBUF+2 MOV DX,12 CALL MYD JC GETDAT MOV CL,5 SHL AX,CL MOV [DATE],AX MOV DX,31 CALL MYD JC GETDAT OR [DATE],AL MOV DX,2100 CALL MYD JC GETDAT SUB AX,80 JC GETDAT CMP AX,19 JBE SAVYR SUB AX,1900 JC GETDAT SAVYR: SHL AL OR [DATE+1],AL ;Move the FATs into position and adjust the drive pointer table POP DX POP SI MOV AX,[MAXSEC] SHL AX ;Allocate two buffers ADD AX,ADJFAC JZ FINJMP MOV DI,DRVTAB MOV CX,15 ADJLP: ADD [DI],AX INC DI INC DI LOOP ADJLP MOV CX,SI MOV SI,MEMSTRT ;Place to move FATs from SUB CX,SI ;Total length of FATs MOV DI,AX ADD DI,SI ;Place to move FATs to OR AX,AX JS MOVJMP DEC CX ADD DI,CX ADD SI,CX INC CX DOWN MOVJMP: JMP MOVFAT FINJMP: JMP FININIT FIGFATSIZ: SEG ES MUL AL,[BP+FATCNT] SEG ES ADD AX,[BP+FIRFAT] SEG ES ADD AX,[SDIRSEC] ;Use small dir. entry to figure FAT size FIGMAX: ;AX has equivalent of FIRREC SEG ES SUB AX,[BP+DSKSIZ] NEG AX SEG ES MOV CL,[BP+CLUSSHFT] SHR AX,CL INC AX MOV CX,AX ;MAXCLUS INC AX MOV DX,AX SHR DX ADC AX,DX ;Size of FAT in bytes SEG ES MOV SI,[BP+SECSIZ] ADD AX,SI DEC AX XOR DX,DX DIV AX,SI RET MYD: ;SI points to input buffer. Get decimal number < DX and return it ;in AX. Carry set on error. XOR BX,BX MOV AH,0 GETDIG: LODB SUB AL,"0" JC CHKRET CMP AL,10 JNC CHKRET SHL BX MOV CX,BX SHL BX SHL BX ADD BX,CX ADD BX,AX JP GETDIG CHKRET: MOV AX,BX OR AX,AX STC JZ RET CMP DX,AX RET DRVCNT: DB 0 DATMES: DB "Enter today's date (m-d-y): $" DATBUF: DB 12,8,"04-28-81",13 DS 5 SDIRSEC:DS 2 MEMSTRT: ADJFAC: EQU DIRBUF-MEMSTRT