亚洲欧美精品沙发,日韩在线精品视频,亚洲Av每日更新在线观看,亚洲国产另类一区在线5

<pre id="hdphd"></pre>

  • <div id="hdphd"><small id="hdphd"></small></div>
      學習啦 > 學習電腦 > 操作系統(tǒng) > 操作系統(tǒng)基礎(chǔ)知識 > DOS操作系統(tǒng)源碼相關(guān)資料知識

      DOS操作系統(tǒng)源碼相關(guān)資料知識

      時間: 志藝942 分享

      DOS操作系統(tǒng)源碼相關(guān)資料知識

        你還在為不知道DOS操作系統(tǒng)源碼相關(guān)資料知識而煩惱么?接下來是小編為大家收集的DOS操作系統(tǒng)源碼相關(guān)資料知識教程,希望能幫到大家。

        DOS操作系統(tǒng)源碼相關(guān)資料知識

        微軟DOS早期源碼,現(xiàn)已公開;下了一份,看下其大致結(jié)構(gòu);

        包括1.1和2.0版本的源碼和編譯后的結(jié)果;

        貼出其1.1版本 MSDOS.ASM 源碼;有空研究;

      [plain] view plain copy ; 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

        

      看了“DOS操作系統(tǒng)源碼相關(guān)資料知識”還想看:

      1.計算機的DOS操作系統(tǒng)詳解

      2.dos操作系統(tǒng)介紹

      3.DOS操作系統(tǒng)歷史知識

      4.電腦操作系統(tǒng)介紹與發(fā)展歷程

      5.操作系統(tǒng)發(fā)展簡史

      2779956