; 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