Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ; 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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement