From 29e167c3408fceb94e4c3da1a1eea2347e5abb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Thu, 26 Aug 2021 06:21:02 +0300 Subject: [PATCH] Add ordos.sys --- README | 2 + build.bat | 4 + clean.bat | 4 + msdos.asm | 4030 ++++++++++++++++++++++++++++++++++++++++++++++++++++ stddos.asm | 22 + 5 files changed, 4062 insertions(+) create mode 100644 msdos.asm create mode 100644 stddos.asm diff --git a/README b/README index 0551294..bebe21e 100644 --- a/README +++ b/README @@ -16,6 +16,8 @@ edlin.com ms-dos v1.25/bin/EDLIN.COM exe2bin.exe ms-dos v1.25/bin/EXE2BIN.EXE link.exe ms-dos v1.25/bin/LINK.EXE masm.exe ms-dos v2.0/bin/MASM.EXE +msdos.asm ms-dos v1.25/source/MSDOS.ASM +stddos.asm ms-dos v1.25/source/STDDOS.ASM Notes ----- diff --git a/build.bat b/build.bat index 865d88a..1bbedfc 100644 --- a/build.bat +++ b/build.bat @@ -1,3 +1,7 @@ masm command; link command; exe2bin command.exe command.com + +masm stddos; +link stddos; +exe2bin stddos.exe ordos.sys diff --git a/clean.bat b/clean.bat index 05feaea..7b9404c 100644 --- a/clean.bat +++ b/clean.bat @@ -1,3 +1,7 @@ del command.obj del command.exe del command.com + +del stddos.obj +del stddos.exe +del ordos.sys diff --git a/msdos.asm b/msdos.asm new file mode 100644 index 0000000..f0f01a2 --- /dev/null +++ b/msdos.asm @@ -0,0 +1,4030 @@ +; 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 diff --git a/stddos.asm b/stddos.asm new file mode 100644 index 0000000..6a1c89c --- /dev/null +++ b/stddos.asm @@ -0,0 +1,22 @@ + TITLE MS-DOS version 1.25 by Tim Paterson March 3, 1982 + PAGE 60,132 +; Use the following booleans to set the switches +FALSE EQU 0 +TRUE EQU NOT FALSE + +; Use the switches below to produce the standard Microsoft version of the IBM +; version of the operating system +MSVER EQU FALSE +IBM EQU TRUE + +; Set this switch to cause DOS to move itself to the end of memory +HIGHMEM EQU FALSE + +; Turn on switch below to allow testing disk code with DEBUG. It sets +; up a different stack for disk I/O (functions > 11) than that used for +; character I/O which effectively makes the DOS re-entrant. + +DSKTEST EQU FALSE + + INCLUDE MSDOS.ASM +