; 86-DOS High-performance operating system for the 8086 version 0.11 ; by Tim Paterson ; 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 ESCCH EQU 1BH CANCEL EQU "X"-"@" ;Cancel with Ctrl-X MAXCALL EQU 36 MAXCOM EQU 40 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 SAVEXIT EQU 10 ; Field definition for FCBs FNAME EQU 0 ;Drive code and name EXTENT EQU 12 ENTPOS EQU 16 ;Position of entry in directory DRVBP EQU 18 ;BP for SEARCH FIRST and SEARCH NEXT FIRCLUS EQU 20 ;First cluster of file LSTCLUS EQU 22 ;Last cluster accessed FILSIZ EQU 24 ;Size of file in records CLUSPOS EQU 26 ;Position of last cluster accessed FLSFLG EQU 28 ;Flush flag SIZCHG EQU 29 ;Size change flag NR EQU 32 ;Next record RR EQU 33 ;Random record ; Description of 16-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 2 First allocation unit ( < 4080 ) ; 13 3 File size, in bytes (LSB first, 24 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. 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 of 0FFFH ; 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 DEVNUM EQU 0 ;I/O driver number SECSIZ EQU 1 ;Size of physical sector in records CLUSMSK EQU 2 ;Records/cluster - 1 CLUSSHFT EQU 3 ;Log2 of records/cluster FIRFAT EQU 4 ;Starting record of FATs FATSIZ EQU 6 ;Number of records occupied by FAT FATCNT EQU 7 ;Number of FATs for this drive FIRDIR EQU 8 ;Starting record of directory DIRSIZ EQU 10 ;Number of records occupied by directory FIRREC EQU 11 ;First record of first cluster MAXCLUS EQU 13 ;Number of clusters on drive + 1 DIRTY EQU 15 ;Whether FAT is dirty FAT EQU 16 ;Pointer to start of FAT DPBSIZ EQU 18 ;Size of the structure in bytes ; BOIS entry point definitions BIOSSEG EQU 40H BIOSINIT EQU 0 ;Reserve room for jump to init code BIOSSTAT EQU 3 ;Console input status check BIOSIN EQU 6 ;Get console character BIOSOUT EQU 9 ;Output console character BIOSPRINT EQU 12 ;Output to printer BIOSAUXIN EQU 15 ;Get byte from auxilliary BIOSAUXOUT EQU 18 ;Output byte to auxilliary BIOSREAD EQU 21 ;Disk read BIOSWRITE EQU 24 ;Disk write BIOSFLUSH EQU 27 ;Disk buffer flush ; Start of code ORG 0 JMP DOSINIT ESCTAB: DB "SC" ;Copy one character from template DB "VN" ;Skip over one character in template DB "TA" ;Copy up to specified character DB "WB" ;Skip up to specified character DB "UH" ;Copy rest of template DB "HH" ;Kill line with no change in template (Ctrl-X) DB "RM" ;Cancel line and update template DB "DD" ;Backspace (same as Ctrl-H) DB "P@" ;Enter Insert mode DB "QL" ;Exit Insert mode DB 1BH,1BH ;Escape sequence to represent escape character DB ESCCH,ESCCH ESCTABLEN EQU $-ESCTAB HEADER DB 13,10,"86-DOS version 0.11" DB 13,10 DB "Copyright 1980 Seattle Computer Products, Inc.",13,10,"$" DOSINIT: CLI CLD MOV AX,CS MOV ES,AX LODB ;Get no. of drives & no. of I/O drivers CBW MOV CX,AX SEG CS MOV [NUMIO],AL MOV DI,AX SHL DI MOV AH,DPBSIZ ;Size of DPB MUL AX,AH MOV BX,DRVTAB ADD DI,BX ;Point to first DPB ADD AX,DI MOV BP,AX ;Point to first FAT SEG CS MOV [FATBASE],AX PERDEV: SEG CS MOV [BX],DI ;Store DPB pointer to table entry INC BX INC BX MOV AL,CH STOB ;DEVNUM LODW ;Get pointer to DPT PUSH SI ;Save INITTAB pointer MOV SI,AX ;Load DPT MOVB ;SECSIZ LODB DEC AL STOB ;CLUSMSK CBW FIGSHFT: INC AH SAR AL JNZ FIGSHFT MOV AL,AH STOB ;CLUSSHFT LODW STOW ;FIRFAT MOV DX,AX LODB STOB ;FATSIZ MOV AH,AL PUSH AX LODB STOB ;FATCNT MUL AX,AH ADD AX,DX STOW ;FIRDIR MOV DX,AX LODB STOB ;DIRSIZ CBW ADD AX,DX STOW ;FIRREC POP DX LODW INC AX STOW ;MAXCLUS XOR AL,AL STOB POP SI LODW ;Allocation table displacement SEG CS ADD AX,[FATBASE] STOW ;FAT MOV DL,0 SHR DX ;FAT size in bytes ADD AX,DX CMP AX,BP JBE SMFAT MOV BP,AX SMFAT: INC CH ;Next I/O device number DEC CL JNZ PERDEV ADD BP,15 ;True start of free space MOV CL,4 SHR BP,CL ;First free segment 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 W,[ENTRYPOINT+1],ENTRY MOV W,[ENTRYPOINT+3],CS STOW STOW STOW MOV W,[INTBASE+4],COMMAND MOV DI,INTBASE+14H MOV AX,BIOSREAD STOW MOV AX,BIOSSEG STOW STOW ;Add 2 to DI STOW MOV W,[INTBASE+18H],BIOSWRITE MOV DX,CS MOV DS,DX ADD DX,BP ;Next free segment MOV W,[DMAADD],128 MOV W,[DMAADD+2],DX MOV AX,[DRVTAB] MOV [CURDRV],AX MOV CX,DX ;Start scanning just after DOS MOV BX,0FH 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: SEG CS MOV [ENDMEM],CX XOR CX,CX MOV DS,CX MOV W,[EXIT],100H MOV W,[EXIT+2],DX MOV W,[INTBASE+0CH],100H MOV W,[INTBASE+0EH],DX CALL SETMEM MOV SI,HEADER CALL OUTMES RET L 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 CLI 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: SEG CS MOV [SPSAVE],SP SEG CS MOV [SSSAVE],SS INC SP INC SP SEG CS POP [TEMP] MOV SP,CS MOV SS,SP MOV SP,SSSAVE PUSH ES PUSH DS PUSH BP PUSH DI PUSH SI PUSH DX PUSH CX PUSH BX PUSH AX MOV BL,AH MOV BH,0 SHL BX CLD SEG CS CALL [BX+DISPATCH] SEG CS MOV [AXSAVE],AL POP AX POP BX POP CX POP DX POP SI POP DI POP BP POP DS POP ES POP SS SEG CS MOV SP,[SPSAVE] IRET ; Standard Functions DISPATCH DW ABORT ;0 DW CONIN DW CONOUT DW READER DW PUNCH DW LIST ;5 DW RAWIO DW GETIO DW SETIO DW PRTBUF DW BUFIN ;10 DW CONSTAT DW GETVER 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 GETIO: SETIO: GETVER: GETFATPTDL: 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,BADFAT 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 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 ; BP = Base of drive parameters ; DS = CS ; ES = CS ; AX = Directory record number ; BX = Pointer into directory buffer ; [DIRBUF] has directory record with match ; [NAME1] has file name ; All other registers destroyed. CALL MOVNAME JC RET FINDNAME: MOV AX,CS MOV DS,AX MOV AL,0 RDIRREC: PUSH AX CALL DIRREAD POP AX MOV BX,DIRBUF-16 CONTSRCH: CALL NEXTENT JZ RET NDIRREC: INC AL ;Next directory record CMP AL,[BP+DIRSIZ] JB RDIRREC STC RET NEXTENT: Add BX,16 CMP BX,DIRBUF+127 JA RET CMP B,[BX],0E5H JZ NEXTENT MOV SI,BX MOV DI,NAME1 MOV CX,11 WILDCRD: REPE CMPB JZ RET CMP B,[DI-1],"?" JZ WILDCRD JP NEXTENT DELETE: ; System call 19 CALL GETNAME JC ERRET PUSH AX PUSH BX CALL LOADFAT POP BX DELFILE: MOV B,[BX],0E5H PUSH BX MOV BX,[BX+11] MOV SI,[BP+FAT] OR BX,BX JZ DELNXT CMP BX,[BP+MAXCLUS] JA DELNXT CALL RELEASE DELNXT: POP BX CALL NEXTENT JZ DELFILE POP AX PUSH AX CALL DIRWRITE POP AX CALL NDIRREC PUSH AX JNC DELFILE POP AX CALL FATWRT XOR AL,AL RET RENAME: ;System call 23 CALL MOVNAME JC ERRET ADD SI,5 MOV DI,NAME2 CALL LODNAME CALL FINDNAME JC ERRET SAVDREC: MOV AH,AL RENFIL: MOV DI,BX MOV SI,NAME2 MOV CX,11 NEWNAM: LODB CMP AL,"?" JZ SKIPLET MOV [DI],AL SKIPLET: INC DI LOOP NEWNAM CALL NEXTENT JZ RENFIL MOV AL,AH PUSH AX CALL DIRWRITE POP AX CALL NDIRREC JNC SAVDREC ;New record read, save current number 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 SEG ES CMP [NUMIO],AL JC RET CBW XCHG AX,BP SHL BP MOV BP,[BP+CURDRV] 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,"a"-1 JLE STOLET AND AL,5FH ;Convert to upper case STOLET: CMP AL," " JB RET STOB LOOP MOVCHK RET OPEN: ;System call 15 PUSH DX PUSH DS CALL GETNAME OPENNAM: POP ES POP DI JC ERRET MOV AH,[BP+DEVNUM] INC AH SEG ES MOV [DI],AH SEG ES MOV [DI+EXTENT],0 ADD DI,16 MOV CX,BX SUB CX,DIRBUF MOV AH,CL STOW ;ENTPOS MOV AX,BP STOW ;DRVBP LEA SI,[BX+11] LODW STOW ;FIRCLUS STOW ;LSTCLUS LODB SHL AL LODW RCL AX STOW ;FILSIZ XOR AX,AX STOW ;CLUSPOS STOW ;FLSFLG, SIZCHG LOADFAT: TEST B,[BP+DIRTY],-1 JNZ RET CALL FIGFAT READFAT: PUSH DX PUSH CX PUSH BX PUSH AX CALL DREAD OR AL,AL POP AX POP BX POP CX POP DX JNZ FATERR SUB AL,[BP+FATCNT] JZ RET NEG AL JP FATWRT FATERR: ADD DX,CX DEC AL JNZ READFAT POP BP MOV SI,ALLBAD CALL HARDERR JP LOADFAT CLOSE: ;System call 16 MOV DI,DX TEST B,[DI+FLSFLG],-1 JZ NOFLSH PUSH DI MOV BP,[DI+DRVBP] MOV AL,[BP+DEVNUM] CALL BIOSFLUSH,BIOSSEG POP DI NOFLSH: TEST B,[DI+SIZCHG],-1 JZ OKRET MOV DX,DI PUSH DX PUSH DS CALL GETNAME POP ES POP DI JC BADCLOSE MOV CX,BX SUB CX,DIRBUF MOV AH,CL SEG ES CMP AX,[DI+ENTPOS] JNZ BADCLOSE SEG ES MOV CX,[DI+FIRCLUS] MOV [BX+11],CX SEG ES MOV DX,[DI+FILSIZ] SHR DX MOV [BX+14],DX MOV DL,0 RCR DL MOV [BX+13],DL CALL DIRWRITE CHKFATWRT: ; Do FATWRT only if FAT is dirty TEST B,[BP+DIRTY],-1 JZ OKRET FATWRT: ; Inputs: ; DS = CS ; BP = Base of drive parameter table ; Function: ; Write the FAT back to disk and reset FAT ; dirty flag. ; Outputs: ; AL = 0 ; BP unchanged ; All other registers destroyed MOV B,[BP+DIRTY],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+DIRTY],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,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 MOV AX,CS MOV DS,AX XOR AX,AX RDIRREC2: PUSH AX CALL DIRREAD POP AX MOV DI,DIRBUF-16 MOV CX,8 FNDFRE: ADD DI,16 CMP B,[DI],0E5H LOOPNE FNDFRE JZ FREESPOT INC AL CMP AL,[BP+DIRSIZ] JC RDIRREC2 POP DS POP DX ERRET3: MOV AL,-1 RET FREESPOT: MOV BX,DI MOV SI,NAME1 MOV CX,5 MOVB REP MOVW XCHG AL,AH MOV CL,5 REP STOB XCHG AL,AH PUSH AX PUSH BX CALL DIRWRITE POP BX POP AX JMP OPENNAM 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. CALL DIRCOMP DREAD: ; Inputs: ; BX,DS = Transfer address ; CX = Number of records ; 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. MOV AL,[BP+DEVNUM] PUSH BP PUSH BX PUSH CX PUSH DX CALL BIOSREAD,BIOSSEG POP DX POP DI POP BX POP BP JC DSKRDERR XOR AL,AL RET DSKRDERR: MOV SI,RDERR CALL HARDERR JP DREAD 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. CALL DIRCOMP DWRITE: ; Inputs: ; BX,DS = Transfer address ; CX = Number of records ; 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,0 CMP DX,[BP+FIRREC] RCR AH PUSH BP PUSH BX PUSH CX PUSH DX CALL BIOSWRITE,BIOSSEG POP DX POP DI POP BX POP BP JC DSKWRERR XOR AL,AL RET DSKWRERR: MOV SI,WRERR CALL HARDERR JP DWRITE HARDERR: SUB DI,CX ADD DX,DI ;Next record to transfer CALL RECTOBYT ADD BX,DI ;Next location for transfer CALL OUTMES GETRESP: CALL IN OR AL,20H ;To lower-case CMP AL,"a" JZ ERROR CMP AL,"r" JZ RET CMP AL,"i" JZ IGNORE CMP AL,"c" JNZ GETRESP POP AX MOV AL,1 RET IGNORE: POP AX MOV AL,0 RET ABORT: SEG CS MOV DS,[TEMP] XOR AX,AX MOV ES,AX MOV SI,SAVEXIT MOV DI,EXIT MOVW MOVW MOVW MOVW ERROR: MOV SP,BPSAVE MOV AX,CS MOV DS,AX MOV ES,AX CALL NOBUF XOR AX,AX MOV DS,AX MOV SI,EXIT MOV DI,EXITHOLD MOVW MOVW POP BP POP ES POP ES POP DS POP SS MOV SP,[SPSAVE] MOV DS,[DSSAVE] SEG CS JMP L,[EXITHOLD] SEQRD: ;System call 20 CALL GETREC MOV CX,1 CALL LOAD JCXZ SETNREX INC AX JP SETNREX SEQWRT: ;System call 21 CALL GETREC MOV CX,1 CALL STORE JCXZ SETNREX INC AX JP SETNREX RNDRD: ;System call 33 MOV CX,1 MOV DI,DX MOV AX,[DI+RR] CALL LOAD JP FINRND RNDWRT: ;System call 34 MOV CX,1 MOV DI,DX MOV AX,[DI+RR] CALL STORE JP FINRND BLKRD: ;System call 39 MOV DI,DX MOV AX,[DI+RR] CALL LOAD JP FINBLK BLKWRT: ;System call 40 MOV DI,DX MOV AX,[DI+RR] CALL STORE FINBLK: MOV [CXSAVE],CX JCXZ FINRND INC AX FINRND: SEG ES MOV [DI+RR],AX SETNREX: MOV CX,AX AND AL,7FH SEG ES MOV [DI+NR],AL AND CL,80H ROL CX XCHG CL,CH SEG ES MOV [DI+EXTENT],CX MOV AL,[DSKERR] RET SETUP: ; Inputs: ; DS:DI point to FCB ; AX = Record position in file of disk transfer ; CX = Record count ; Outputs: ; DS = CS ; ES:DI point to FCB ; CX = No. of records to transfer ; BP = Base of drive parameters ; [RECCNT] = Record count ; [RECPOS] = Record position in file ; [FCB] = DX ; [NEXTADD] = Displacement of disk transfer within segment ; [DSKERR] = 0 (no errors yet) ; [NUMTRNS] = 0 (No transfers yet) ; If SETUP detects no records will be transfered, it returns 1 level up ; with CX = 0. MOV BP,[DI+DRVBP] MOV BX,DS MOV ES,BX MOV BX,CS MOV DS,BX MOV [RECPOS],AX MOV [FCB],DX MOV BX,[DMAADD] MOV [NEXTADD],BX MOV B,[DSKERR],0 MOV [NUMTRNS],0 MOV SI,[BP+FAT] ADD BX,7FH ;See if there is any space left JC SEGEND AND BL,80H NEG BX ;Amount of room left in segment ROL BX XCHG BL,BH JNZ CHKFIT MOV BH,2 CHKFIT: CMP CX,BX ;See if it will fit in one segment JBE OK MOV CX,BX MOV B,[DSKERR],2 ;Flag that trimming took place OK: MOV [RECCNT],CX ;Record count RET SEGEND: MOV B,[DSKERR],2 MOV CX,0 POP BX ;Kill return address 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,0FFFH JZ RET XCHG BX,DI INC DX LOOP SKPCLP RET NOCLUS: INC CX DEC DX RET LOAD: ; Inputs: ; DS:DI point to FCB ; AX = Position in file to read ; CX = No. of records to read ; Outputs: ; AX = Position of last record read ; CX = No. of records read ; ES:DI point to FCB ; LSTCLUS, CLUSPOS fields in FCB set CALL SETUP SEG ES MOV BX,[DI+FILSIZ] SUB BX,AX JBE WRTERRJ CMP BX,CX JNB ENUF MOV B,[DSKERR],1 MOV [RECCNT],BX ENUF: MOV CL,[BP+CLUSSHFT] SHR AX,CL MOV CX,AX CALL FNDCLUS OR CX,CX JNZ WRTERR MOV DL,[RECPOS] AND DL,[BP+CLUSMSK] MOV CX,[RECCNT] RDLP: CALL OPTIMIZE PUSH DI PUSH AX PUSH DS MOV DS,[DMAADD+2] CALL DREAD POP DS POP CX POP BX JCXZ SETFCB MOV DL,0 CMP BX,0FFFH JNZ RDLP MOV B,[DSKERR],1 SETFCB: MOV AX,[CLUSNUM] MOV DI,[FCB] SEG ES MOV [DI+LSTCLUS],AX MOV AX,[RECPOS] MOV BX,[NUMTRNS] ADD AX,BX SEG ES CMP AX,[DI+FILSIZ] JBE NOSZINC SEG ES MOV [DI+FILSIZ],AX SEG ES MOV B,[DI+SIZCHG],-1 NOSZINC: DEC AX MOV DX,AX MOV CL,[BP+CLUSSHFT] SHR DX,CL SEG ES MOV [DI+CLUSPOS],DX MOV CX,BX RET WRTERRJ: JP WRTERR HAVSTART: MOV CX,AX PUSH BX CALL FNDCLUS JCXZ WRCLUS CALL ALLOCATE POP BX JNC NOSKIP WRTERR: MOV B,[DSKERR],1 ADDREC: MOV AX,[RECPOS] XOR CX,CX MOV DI,[FCB] RET WRTEOF: MOV CL,[BP+CLUSSHFT] SHR AX,CL MOV CX,AX CALL FNDCLUS OR CX,CX JNZ ADDREC MOV DX,0FFFH MOV B,[BP+DIRTY],-1 MOV DI,[FCB] MOV AX,[RECPOS] SEG ES MOV [DI+FILSIZ],AX SEG ES MOV B,[DI+SIZCHG],-1 XOR CX,CX RET STORE: ; Inputs: ; DS:DI point to FCB ; AX = Position in file of disk transfer ; CX = Record count ; Outputs: ; AX = Position of last record written ; CX = No. of records written ; ES:DI point to FCB ; LSTCLUS, CLUSPOS fields in FCB set CALL SETUP JCXZ WRTEOF MOV BX,CX ADD BX,AX DEC BX MOV CL,[BP+CLUSSHFT] SHR AX,CL SHR BX,CL MOV CX,AX MOV AX,BX CALL FNDCLUS SUB AX,DX ;Last cluster minus current cluster JCXZ HAVSTART ;See if no more data PUSH CX ;No. of clusters of first MOV CX,AX CALL ALLOCATE POP CX JC WRTERR DEC CX JZ NOSKIP CALL SKPCLP PUSH BX WRCLUS: POP BX NOSKIP: MOV DL,[RECPOS] AND DL,[BP+CLUSMSK] MOV CX,[RECCNT] WRTLP: CALL OPTIMIZE PUSH DI PUSH AX PUSH DS MOV DS,[DMAADD+2] CALL DWRITE POP DS POP CX POP BX MOV DL,0 OR CX,CX JNZ WRTLP CALL SETFCB SEG ES MOV B,[DI+FLSFLG],-1 RET OPTIMIZE: ; Inputs: ; DS = CS ; BX = Physical cluster ; CX = No. of records ; DL = record 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 record 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 records per cluster MOV AH,AL SUB AL,DL ;AL = Number of records left in first cluster MOV DX,CX MOV SI,[BP+FAT] MOV CX,0 OPTCLUS: ;AL has number of records available in current cluster ;AH has number of records available in next cluster ;BX has current physical cluster ;CX has number of sequential records found so far ;DX has number of records 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 records still needed MOV AX,DX MOV BX,CX XCHG BL,BH ROR BX MOV SI,[NEXTADD] ADD BX,SI ;Adjust by size of transfer MOV [NEXTADD],BX ADD [NUMTRNS],CX POP DX POP BX PUSH CX MOV CL,[BP+CLUSSHFT] DEC DX DEC DX SHL DX,CL OR DL,BL ADD DX,[BP+FIRREC] POP CX MOV BX,SI RET BLKDON: MOV CX,DX ;Make the total equal to the request JP FINCLUS GETREC: ; Inputs: ; DS:DX point to FCB ; Outputs: ; AX = Record number determined by EXTENT and NR fields ; DS:DI point to FCB ; No other registers affected. MOV DI,DX MOV AL,[DI+NR] MOV BX,[DI+EXTENT] SHL AL SHR BX RCR AL MOV AH,BL 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 flag ; FIRCLUS field of FCB set if file was null ; SI,BP unchanged. All other registers destroyed. PUSH BX MOV AX,BX ALLOC: MOV DX,BX FINDFRE: INC BX CMP BX,[BP+MAXCLUS] JL TRYOUT CMP AX,1 JG TRYIN POP BX MOV DX,0FFFH CALL RELBLKS OR [SI],0FFFH 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+DIRTY],-1 POP BX CALL UNPACK XCHG BX,DI OR DI,DI JNZ RET MOV DI,[FCB] SEG ES MOV [DI+FIRCLUS],BX OR [SI],0FFFH 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,0FFFH MOV BX,AX JNZ 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,0FFFH JZ RET MOV BX,DI JP GETEOF SRCHFRST: ;System call 17 CALL GETNAME SAVPLCE: ; Search-for-next enters here to save place and report ; findings. JC KILLSRCH MOV [SRCHDON],AL MOV [PDIR],BX MOV [SRCHBP],BP ;Information in directory entry must be copied into the first ; 16 bytes starting at the disk transfer address. MOV SI,BX LES DI,[DMAADD] MOV CX,8 REP MOVW MOV AL,0 RET KILLSRCH: MOV AL,-1 MOV [SRCHDON],AL RET SRCHNXT: ;System call 18 MOV AX,CS MOV ES,AX MOV DS,AX MOV AL,[SRCHDON] CMP AL,-1 JZ RET MOV BX,[PDIR] MOV BP,[SRCHBP] CALL CONTSRCH JP SAVPLCE FILESIZE: ;System call 35 PUSH DI PUSH DX CALL GETNAME POP DI POP ES MOV AL,-1 JC RET ADD DI,33 ;Write size in RR field LEA SI,[BX+13] LODB SHL AL LODW RCL AX ;Round to nearest record STOW MOV AL,0 RCL AL STOB 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 [DSSAVE],CS MOV BP,[CURDRV] CALL LOADFAT MOV BX,[BP+FAT] MOV AL,[BP+CLUSMSK] INC AL MOV DX,[BP+MAXCLUS] DEC DX MOV B,[BP+DIRTY],-1 MOV [BXSAVE],BX MOV [DXSAVE],DX RET GETDSKPT: ;System call 31 SEG CS MOV [DSSAVE],CS SEG CS MOV BX,[CURDRV] SEG CS MOV [BXSAVE],BX RET DSKRESET: ;System call 13 SEG CS MOV [DMAADD+2],DS MOV AX,CS MOV DS,AX MOV [DMAADD],80H MOV AX,[DRVTAB] MOV [CURDRV],AX NOBUF: MOV CL,[NUMIO] MOV CH,0 MOV SI,DRVTAB WRTFAT: LODW PUSH CX PUSH SI MOV BP,AX CALL CHKFATWRT POP SI POP CX LOOP WRTFAT MOV AL,-1 CALL BIOSFLUSH,BIOSSEG RET GETDRV: ;System call 25 SEG CS MOV BP,[CURDRV] MOV AL,[BP+0] RET INUSE: ;System call 24 MOV AX,CS MOV DS,AX MOV CL,[NUMIO] MOV CH,0 MOV SI,CX SHL SI ADD SI,CURDRV MOV BX,0 STD CHKUSE: LODW MOV BP,AX TEST B,[BP+DIRTY],-1 JZ SETBIT STC SETBIT: RCL BX LOOP CHKUSE MOV AL,BL RET SETRNDREC: ;System call 36 CALL GETREC MOV [DI+RR],AX MOV AL,0 JZ BIGRR INC AL BIGRR: MOV [DI+RR+2],AL RET SELDSK: ;System call 14 MOV DH,0 MOV BX,DX PUSH CS POP DS CMP BL,[NUMIO] JGE RET SHL BX MOV AX,[BX+DRVTAB] MOV [CURDRV],AX 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,CANCEL JZ KILNEW CMP AL,27 JZ ESC SAVCH: CMP DH,DL JNB GETCH STOB INC DH CALL OUT 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 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 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 STD 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 CLD 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 OUTCH MOV AL,8 JMP OUTCH 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 OUT 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 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 ;Eat Cntrl-S NOSTOP: CMP AL,'P'-'@' JZ PRINTON CMP AL,'N'-'@' JZ PRINTOFF CMP AL,'C'-'@' JNZ RET INT CONTC ;Execute user Ctrl-C handler RET PRINTON: SEG CS MOV B,[PFLAG],1 RET PRINTOFF: SEG CS MOV B,[PFLAG],0 RET CTRLOUT: CMP AL,10 JZ OUTCH CMP AL,13 JZ ZERPOS CMP AL,8 JZ BACKPOS CMP AL,9 JZ TAB PUSH AX MOV AL,"^" CALL OUT POP AX OR AL,40H JP OUT ZERPOS: SEG CS MOV B,[CARPOS],0 JP OUTCH BACKPOS: SEG CS DEC B,[CARPOS] JP OUTCH TAB: MOV AL,0 SEG CS XCHG AL,[CARPOS] OR AL,0F8H NEG AL PUSH CX MOV CL,AL MOV CH,0 TABLP: MOV AL," " CALL OUTCH LOOP TABLP POP CX RET CONSTAT: ;System call 11 CALL BIOSSTAT,BIOSSEG JZ RET OR AL,-1 RET CONIN: ;System call 1 CALL INCHK PUSH AX CALL OUT POP AX RET IN: CALL INCHK JZ IN RET RAWIO: ;System call 6 MOV AL,DL CMP AL,-1 JNZ RAWOUT CALL BIOSSTAT,BIOSSEG JZ RET 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 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,[TEMP] XOR SI,SI MOV DI,SI MOV CX,80H REP MOVW 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) ; 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,0FFFH JBE HAVDIF MOV CX,0FFFH 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 RECTOBYT: SHL DI SHL DI SHL DI SHL DI SHL DI SHL DI SHL DI RET ;***** DATA AREA ***** BADFAT DB 13,10,"Bad FAT",13,10,"$" ALLBAD DB 13,10,"All FATs on disk are bad",13,10,"$" RDERR DB 13,10,"Disk read error",13,10,"$" WRERR DB 13,10,"Disk write error",13,10,"$" CARPOS DB 0 STARTPOS DB 0 PFLAG DB 0 SRCHDON DB -1 PDIR DS 2 SRCHBP DS 2 EXITHOLD DS 4 ENDMEM DS 2 FATBASE DS 2 INBUF DS 255 DIRBUF DS 128 NUMIO DS 1 ;Number of disk tables NAME1 DS 11 ;File name buffer NAME2 DS 11 ;File name buffer DMAADD DS 4 ;User's disk transfer address (disp/seg) TEMP DS 2 DSKERR DS 1 FCB DS 2 ;Address of user FCB RECPOS DS 2 NEXTADD DS 2 RECCNT DS 2 CLUSNUM DS 2 NUMTRNS DS 2 DS 50 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 SSSAVE DS 2 SPSAVE DS 2 CURDRV DS 2 DRVTAB: ;Address of start of DPBs