国产宅男网站在线|亚洲A级性爱免费视频|亚洲中精品级在线|午夜福利AA毛

  • <dd id="gf5jf"><th id="gf5jf"></th></dd>

    <cite id="gf5jf"><label id="gf5jf"></label></cite>
  • <div id="gf5jf"><listing id="gf5jf"></listing></div>
    學(xué)習(xí)啦 > 學(xué)習(xí)電腦 > 操作系統(tǒng) > 操作系統(tǒng)基礎(chǔ)知識(shí) > DOS操作系統(tǒng)源碼相關(guān)資料知識(shí)

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

    時(shí)間: 志藝942 分享

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

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

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

      微軟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)資料知識(shí)”還想看:

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

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

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

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

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

    2779956