]>
wirehaze git hosting - MS-DOS.git/blob - v1.25/source/MSDOS.ASM
1 ; 86-DOS High-performance operating system for the 8086 version 1.25
5 ; ****************** Revision History *************************
6 ; >> EVERY change must noted below!! <<
8 ; 0.34 12/29/80 General release, updating all past customers
9 ; 0.42 02/25/81 32-byte directory entries added
10 ; 0.56 03/23/81 Variable record and sector sizes
11 ; 0.60 03/27/81 Ctrl-C exit changes, including register save on user stack
12 ; 0.74 04/15/81 Recognize I/O devices with file names
13 ; 0.75 04/17/81 Improve and correct buffer handling
14 ; 0.76 04/23/81 Correct directory size when not 2^N entries
15 ; 0.80 04/27/81 Add console input without echo, Functions 7 & 8
16 ; 1.00 04/28/81 Renumber for general release
17 ; 1.01 05/12/81 Fix bug in `STORE'
18 ; 1.10 07/21/81 Fatal error trapping, NUL device, hidden files, date & time,
19 ; RENAME fix, general cleanup
20 ; 1.11 09/03/81 Don't set CURRENT BLOCK to 0 on open; fix SET FILE SIZE
21 ; 1.12 10/09/81 Zero high half of CURRENT BLOCK after all (CP/M programs don't)
22 ; 1.13 10/29/81 Fix classic "no write-through" error in buffer handling
23 ; 1.20 12/31/81 Add time to FCB; separate FAT from DPT; Kill SMALLDIR;
24 ; Add FLUSH and MAPDEV calls; allow disk mapping in DSKCHG;
25 ; Lots of smaller improvements
26 ; 1.21 01/06/82 HIGHMEM switch to run DOS in high memory
27 ; 1.22 01/12/82 Add VERIFY system call to enable/disable verify after write
28 ; 1.23 02/11/82 Add defaulting to parser; use variable escape character
29 ; Don't zero extent field in IBM version (back to 1.01!)
30 ; 1.24 03/01/82 Restore fcn. 27 to 1.0 level; add fcn. 28
31 ; 1.25 03/03/82 Put marker (00) at end of directory to speed searches
33 ; *************************************************************
36 ; Interrupt Entry Points:
40 ; INTBASE+8: BASE EXIT ADDRESS
41 ; INTBASE+C: CONTROL-C ABORT
42 ; INTBASE+10H: FATAL ERROR ABORT
43 ; INTBASE+14H: BIOS DISK READ
44 ; INTBASE+18H: BIOS DISK WRITE
45 ; INTBASE+40H: Long jump to CALL entry point
49 CANCEL EQU 1
BH ;Cancel with ESC
50 TOGLINS EQU TRUE
;One key toggles insert mode
51 TOGLPRN EQU TRUE
;One key toggles printer echo
52 NUMDEV EQU
6 ;Include "COM1" as I/O device name
56 CANCEL EQU
"X"-"@" ;Cancel with Ctrl-X
57 TOGLINS EQU FALSE
;Separate keys for insert mode on and off
58 TOGLPRN EQU FALSE
;Separate keys for printer echo on and off
59 NUMDEV EQU
5 ;Number of I/O device names
68 ENTRYPOINT EQU INTBASE
+40H
76 ; Field definition for FCBs
79 DB 12 DUP (?
) ;Drive code and name
81 RECSIZ
DW ?
;Size of record (user settable)
82 FILSIZ
DW ?
;Size of file in bytes
83 DRVBP
DW ?
;BP for SEARCH FIRST and SEARCH NEXT
84 FDATE
DW ?
;Date of last writing
85 FTIME
DW ?
;Time of last writing
86 DEVID
DB ?
;Device ID number, bits 0-5
87 ;bit 7=0 for file, bit 7=1 for I/O device
88 ;If file, bit 6=0 if dirty
89 ;If I/O device, bit 6=0 if EOF (input)
90 FIRCLUS
DW ?
;First cluster of file
91 LSTCLUS
DW ?
;Last cluster accessed
92 CLUSPOS
DW ?
;Position of last cluster accessed
93 DB ?
;Forces NR to offset 32
95 RR
DB 3 DUP (?
) ;Random record
97 FILDIRENT
= FILSIZ
;Used only by SEARCH FIRST and SEARCH NEXT
99 ; Description of 32-byte directory entry (same as returned by SEARCH FIRST
100 ; and SEARCH NEXT, functions 17 and 18).
102 ; Location bytes Description
104 ; 0 11 File name and extension ( 0E5H if empty)
105 ; 11 1 Attributes. Bits 1 or 2 make file hidden
106 ; 12 10 Zero field (for expansion)
107 ; 22 2 Time. Bits 0-4=seconds/2, bits 5-10=minute, 11-15=hour
108 ; 24 2 Date. Bits 0-4=day, bits 5-8=month, bits 9-15=year-1980
109 ; 26 2 First allocation unit ( < 4080 )
110 ; 28 4 File size, in bytes (LSB first, 30 bits max.)
112 ; The File Allocation Table uses a 12-bit entry for each allocation unit on
113 ; the disk. These entries are packed, two for every three bytes. The contents
114 ; of entry number N is found by 1) multiplying N by 1.5; 2) adding the result
115 ; to the base address of the Allocation Table; 3) fetching the 16-bit word at
116 ; this address; 4) If N was odd (so that N*1.5 was not an integer), shift the
117 ; word right four bits; 5) mask to 12 bits (AND with 0FFF hex). Entry number
118 ; zero is used as an end-of-file trap in the OS and as a flag for directory
119 ; entry size (if SMALLDIR selected). Entry 1 is reserved for future use. The
120 ; first available allocation unit is assigned entry number two, and even
121 ; though it is the first, is called cluster 2. Entries greater than 0FF8H are
122 ; end of file marks; entries of zero are unallocated. Otherwise, the contents
123 ; of a FAT entry is the number of the next cluster in the file.
126 ; Field definition for Drive Parameter Block
129 DEVNUM
DB ?
;I/O driver number
130 DRVNUM
DB ?
;Physical Unit number
131 SECSIZ
DW ?
;Size of physical sector in bytes
132 CLUSMSK
DB ?
;Sectors/cluster - 1
133 CLUSSHFT
DB ?
;Log2 of sectors/cluster
134 FIRFAT
DW ?
;Starting record of FATs
135 FATCNT
DB ?
;Number of FATs for this drive
136 MAXENT
DW ?
;Number of directory entries
137 FIRREC
DW ?
;First sector of first cluster
138 MAXCLUS
DW ?
;Number of clusters on drive + 1
139 FATSIZ
DB ?
;Number of records occupied by FAT
140 FIRDIR
DW ?
;Starting record of directory
141 FAT
DW ?
;Pointer to start of FAT
144 DPBSIZ EQU
20 ;Size of the structure in bytes
145 DIRSEC
= FIRREC
;Number of dir. sectors (init temporary)
146 DSKSIZ
= MAXCLUS
;Size of disk (temp used during init only)
148 ;The following are all of the segments used
149 ;They are declared in the order that they should be placed in the executable
154 CONSTANTS
SEGMENT BYTE
160 DOSGROUP GROUP
CODE,CONSTANTS
,DATA
166 ; BOIS entry point definitions
175 SEGBIOS
SEGMENT AT BIOSSEG
177 DB 3 DUP (?
) ;Reserve room for jump to init code
178 BIOSSTAT
DB 3 DUP (?
) ;Console input status check
179 BIOSIN
DB 3 DUP (?
) ;Get console character
180 BIOSOUT
DB 3 DUP (?
) ;Output console character
181 BIOSPRINT
DB 3 DUP (?
) ;Output to printer
182 BIOSAUXIN
DB 3 DUP (?
) ;Get byte from auxilliary
183 BIOSAUXOUT
DB 3 DUP (?
) ;Output byte to auxilliary
184 BIOSREAD
DB 3 DUP (?
) ;Disk read
185 BIOSWRITE
DB 3 DUP (?
) ;Disk write
186 BIOSDSKCHG
DB 3 DUP (?
) ;Dsik-change status
187 BIOSSETDATE
DB 3 DUP (?
) ;Set date
188 BIOSSETTIME
DB 3 DUP (?
) ;Set time
189 BIOSGETTIME
DB 3 DUP (?
) ;Get time and date
190 BIOSFLUSH
DB 3 DUP (?
) ;Clear console input buffer
191 BIOSMAPDEV
DB 3 DUP (?
) ;Dynamic disk table mapper
194 ; Location of user registers relative user stack pointer
214 ASSUME
CS:DOSGROUP
,DS:DOSGROUP
,ES:DOSGROUP
,SS:DOSGROUP
220 ESCCHAR
DB ESCCH
;Lead-in character for escape sequences
223 DB "S" ;Copy one char
224 DB "V" ;Skip one char
228 DB "E" ;Kill line (no change in template)
229 DB "J" ;Reedit line (new template)
231 DB "P" ;Enter insert mode
232 DB "Q" ;Exit insert mode
233 DB "R" ;Escape character
238 DB 77 ;Copy one char - -->
239 DB 59 ;Copy one char - F1
240 DB 83 ;Skip one char - DEL
241 DB 60 ;Copy to char - F2
242 DB 62 ;Skip to char - F4
243 DB 61 ;Copy line - F3
244 DB 61 ;Kill line (no change to template ) - Not used
245 DB 63 ;Reedit line (new template) - F5
246 DB 75 ;Backspace - <--
247 DB 82 ;Enter insert mode - INS (toggle)
248 DB 65 ;Escape character - F7
252 ESCTABLEN EQU
$-ESCTAB
254 HEADER
DB 13,10,"MS-DOS version 1.25"
263 DB "Copyright 1981,82 Microsoft, Inc.",13,10,"$"
270 COMMAND: ;Interrupt call entry point
277 ENTRY: ;System call entry point and dispatcher
278 POP AX ;IP from the long call at 5
279 POP AX ;Segment from the long call at 5
280 POP CS:[TEMP
] ;IP from the CALL 5
281 PUSHF ;Start re-ordering the stack
283 PUSH AX ;Save segment
284 PUSH CS:[TEMP
] ;Stack now ordered as if INT had been used
285 CMP CL,MAXCALL
;This entry point doesn't get as many calls
313 MOV SP,OFFSET DOSGROUP
:IOSTACK
321 MOV SP,OFFSET DOSGROUP
:DSKSTACK
323 CALL CS:[BX+DISPATCH
]
329 MOV BYTE PTR [BP.AXSAVE
],AL
414 CALL FAR PTR BIOSFLUSH
435 CALL FAR PTR BIOSAUXIN
444 CALL FAR PTR BIOSAUXOUT
452 ; BX = Cluster number
453 ; BP = Base of drive parameters
454 ; SI = Pointer to drive FAT
456 ; DI = Contents of FAT for given cluster
457 ; Zero set means DI=0 (free cluster)
458 ; No other registers affected. Fatal error if cluster too big.
477 MOV AH,80H
;Signal Bad FAT to INT 24H handler
478 MOV DI,0FFFH ;In case INT 24H returns (it shouldn't)
480 POP AX ;Try to ignore bad FAT
488 ; BX = Cluster number
490 ; SI = Pointer to drive FAT
492 ; The data is stored in the FAT at the given cluster.
493 ; BX,DX,DI all destroyed
494 ; No other registers affected
517 MOV SI,OFFSET DOSGROUP
:IONAME
;List of I/O devices with file names
518 MOV BH,NUMDEV
;BH = number of device names
520 MOV DI,OFFSET DOSGROUP
:NAME1
521 MOV CX,4 ;All devices are 4 letters
522 REPE CMPSB ;Check for name in list
523 JZ IOCHK
;If first 3 letters OK, check for the rest
524 ADD SI,CX ;Point to next device name
533 CMP BH,NUMDEV
;Is it the first device?
535 MOV BH,2 ;Make it the same as AUX
539 MOV CX,2 ;Check rest of name but not extension
541 REPE SCASW ;Make sure rest of name is blanks
543 RET1: RET ;Zero set so CREATE works
546 ; Same as GETNAME except ES:DI points to FCB on successful return
562 ; Find file name in disk directory. First byte is
563 ; drive number (0=current disk). "?" matches any
566 ; Carry set if file not found
568 ; Zero set if attributes match (always except when creating)
569 ; BP = Base of drive parameters
572 ; BX = Pointer into directory buffer
573 ; SI = Pointer to First Cluster field in directory entry
574 ; [DIRBUF] has directory record with match
575 ; [NAME1] has file name
576 ; All other registers destroyed.
579 JC RET2
;Bad file name?
591 OR AH,AH ;End of directory?
593 CMP AH,[DELALL
] ;Free entry?
596 MOV DI,OFFSET DOSGROUP
:NAME1
601 CMP BYTE PTR [DI-1],"?"
609 CMP [ENTFREE
],-1 ;Found a free entry before?
610 JNZ TSTALL
;If so, ignore this one
614 CMP AH,[DELALL
] ;At end of directory?
615 JZ NEXTENT
;No - continue search
616 STC ;Report not found
620 ;Check if attributes allow finding it
621 MOV AH,[ATTRIB
] ;Attributes of search
623 AND AH,[SI] ;Compare with attributes of file
625 AND AH,6 ;Only look at bits 1 and 2
627 TEST BYTE PTR [CREATING
],-1 ;Pass back mismatch if creating
628 JZ NEXTENT
;Otherwise continue searching
635 ; [LASTENT] has previously searched directory entry
637 ; Locates next sequential directory entry in preparation for search
641 ; AL = Current directory block
642 ; BX = Pointer to next directory entry in [DIRBUF]
643 ; DX = Pointer to first byte after end of DIRBUF
644 ; [LASTENT] = New directory entry number
647 INC AX ;Start with next entry
656 RCL DX,1 ;Account for overflow in last shift
658 AND BL,255-31 ;Must be multiple of 32
660 MOV BX,DX ;Position within sector
661 MOV AH,[BP.DEVNUM
] ;AL=Directory sector no.
668 MOV DX,OFFSET DOSGROUP
:DIRBUF
676 ; Same as outputs of GETENTRY, above
678 ; Update AL, BX, and [LASTENT] for next directory entry.
679 ; Carry set if no more.
689 INC AL ;Next directory sector
693 MOV BX,OFFSET DOSGROUP
:DIRBUF
704 DELETE: ; System call 19
709 AND AL,6 ;Look only at hidden bits
710 CMP AL,6 ;Both must be set
714 MOV DI,OFFSET DOSGROUP
:NAME1
715 REPE SCASB ;See if name is *.*
717 MOV BYTE PTR CS:[DELALL
],0 ;DEL *.* - flag deleting all
722 OR BH,BH ;Check if device name
723 JS RET4
;Can't delete I/O devices
725 MOV BYTE PTR [DIRTYDIR
],-1
744 RENAME: ;System call 23
748 MOV DI,OFFSET DOSGROUP
:NAME2
750 JC ERRET
;Report error if second name invalid
753 OR BH,BH ;Check if I/O device name
754 JS ERRET
;If so, can't rename it
755 MOV SI,OFFSET DOSGROUP
:NAME1
756 MOV DI,OFFSET DOSGROUP
:NAME3
757 MOV CX,6 ;6 words (12 bytes)--include attribute byte
758 REP MOVSW ;Copy name to search for
760 MOV DI,OFFSET DOSGROUP
:NAME1
761 MOV SI,OFFSET DOSGROUP
:NAME2
772 MOV BYTE PTR [DI],6 ;Stop duplicates with any attributes
773 CALL DEVNAME
;Check if giving it a device name
775 PUSH [LASTENT
] ;Save position of match
776 MOV [LASTENT
],-1 ;Search entire directory for duplicate
777 CALL CONTSRCH
;See if new name already exists
779 JNC RENERR
;Error if found
780 CALL GETENT
;Re-read matching entry
782 MOV SI,OFFSET DOSGROUP
:NAME1
785 REP MOVSW ;Replace old name with new one
786 MOV BYTE PTR [DIRTYDIR
],-1 ;Flag change in directory
787 MOV SI,OFFSET DOSGROUP
:NAME3
788 MOV DI,OFFSET DOSGROUP
:NAME1
789 MOV CX,6 ;Include attribute byte
790 REP MOVSW ;Copy name back into search buffer
807 ; DS, DX point to FCB or extended FCB
809 ; DS:DX point to normal FCB
812 ; BP has base of driver parameters
813 ; [NAME1] has name in upper case
814 ; All registers except DX destroyed
815 ; Carry set if bad file name or drive
817 MOV CS:WORD PTR [CREATING
],0E500H ;Not creating, not DEL *.*
820 MOV DI,OFFSET DOSGROUP
:NAME1
823 MOV CS:[EXTFCB
],AL ;Set flag if extended FCB in use
824 MOV AH,0 ;Set default attributes
825 CMP AL,-1 ;Is it an extended FCB?
827 ADD DX,7 ;Adjust to point to normal FCB
828 ADD SI,6 ;Point to drive select byte
829 MOV AH,[SI-1] ;Get attribute byte
830 LODSB ;Get drive select byte
832 MOV CS:[ATTRIB
],AH ;Save attributes
835 ; This entry point copies a file name from DS,SI
836 ; to ES,DI converting to upper case.
837 CMP BYTE PTR [SI]," " ;Don't allow blank as first letter
838 STC ;In case of error
844 JNZ STOLET
;Is it a delimiter?
845 CMP AL," " ;This is the only delimiter allowed
846 STC ;In case of error
851 CLC ;Got through whole name - no error
865 OPEN: ;System call 15
868 ; Enter here to perform OPEN on file already found
869 ; in directory. DS=CS, BX points to directory
870 ; entry in DIRBUF, SI points to First Cluster field, and
871 ; ES:DI point to the FCB to be opened. This entry point
874 OR BH,BH ;Check if file is I/O device
875 JS OPENDEV
;Special handler if so
882 STOSW ;Zero low byte of extent field if IBM only
885 ADD DI,12 ;Point to high half of CURRENT BLOCK field
886 STOSB ;Set it to zero (CP/M programs set low byte)
888 MOV AL,128 ;Default record size
889 STOSW ;Set record size
890 LODSW ;Get starting cluster
891 MOV DX,AX ;Save it for the moment
892 MOVSW ;Transfer size to FCB
894 MOV AX,[SI-8] ;Get date
895 STOSW ;Save date in FCB
896 MOV AX,[SI-10] ;Get time
897 STOSW ;Save it in FCB
901 MOV AX,DX ;Restore starting cluster
902 STOSW ; first cluster
903 STOSW ; last cluster accessed
905 STOSW ; position of last cluster
910 ADD DI,13 ;point to 2nd half of extent field
912 STOSB ;Set it to zero
914 STOSW ;Set record size to 128
917 STOSW ;Set current size to zero
919 STOSW ;Date is todays
921 STOSW ;Use current time
922 MOV AL,BH ;Get device number
927 XCHG AX,DI ;Put error code in DI
928 MOV AH,2 ;While trying to read FAT
929 MOV AL,[THISDRV
] ;Tell which drive
941 ; If disk may have been changed, FAT is read in and buffers are
942 ; flagged invalid. If not, no action is taken.
944 ; BP = Base of drive parameters
945 ; Carry set if invalid drive returned by MAPDEV
946 ; All other registers destroyed
949 XOR AH,AH ;Set default response to zero & clear carry
950 CALL FAR PTR BIOSDSKCHG
;See what BIOS has to say
953 MOV AL,[THISDRV
] ;Use physical unit number
955 OR AH,[SI-1] ;Dirty byte for FAT
956 JS NEWDSK
;If either say new disk, then it's so
959 CMP AX,WORD PTR [BUFDRVNO
] ;Does buffer have dirty sector of this drive?
962 CMP AL,[BUFDRVNO
] ;See if buffer is for this drive
963 JNZ BUFOK
;If not, don't touch it
964 MOV [BUFSECNO
],0 ;Flag buffers invalid
965 MOV WORD PTR [BUFDRVNO
],00FFH
980 MOV AH,[SI] ;Get first byte of FAT
981 OR AH,0F8H ;Put in range
982 CALL FAR PTR BIOSMAPDEV
984 MOV [SI-2],AX ;Set device no. and reset dirty bit
986 MOV AL,[SI-2] ;Get device number
988 MOV BP,[DRVTAB
] ;Just in case drive isn't valid
989 AND AL,3FH
;Mask out dirty bit
1005 CALL FIGFAT
;Reset registers
1006 CALL DREAD
;Try first FAT once more
1013 CLOSE: ;System call 16
1015 CMP BYTE PTR [DI],-1 ;Check for extended FCB
1019 TEST BYTE PTR [DI.DEVID
],0C0H ;Allow only dirty files
1020 JNZ OKRET1
;can't close if I/O device, or not writen
1021 MOV AL,[DI] ;Get physical unit number
1022 DEC AL ;Make zero = drive A
1023 MOV AH,1 ;Look for dirty buffer
1024 CMP AX,CS:WORD PTR [BUFDRVNO
]
1026 ;Write back dirty buffer if on same drive
1031 MOV BYTE PTR [DIRTYBUF
],0
1043 MOV CX,ES:[DI.FIRCLUS
]
1045 MOV DX,ES:WORD PTR [DI.FILSIZ
]
1047 MOV DX,ES:WORD PTR [DI.FILSIZ
+2]
1049 MOV DX,ES:[DI.FDATE
]
1051 MOV DX,ES:[DI.FTIME
]
1056 ; Do FATWRT only if FAT is dirty and uses same I/O driver
1060 CMP [SI-2],AX ;See if FAT dirty and uses same driver
1067 ; BP = Base of drive parameter table
1069 ; Write the FAT back to disk and reset FAT
1074 ; All other registers destroyed
1077 MOV BYTE PTR [BX-1],0
1097 MOV BYTE PTR [SI-1],0
1103 ; Loads registers with values needed to read or
1107 MOV CL,[BP.FATSIZ
] ;No. of records occupied by FAT
1109 MOV DX,[BP.FIRFAT
] ;Record number of start of FATs
1114 ; Prepare registers for directory read or write
1118 MOV BX,OFFSET DOSGROUP
:DIRBUF
1123 CREATE: ;System call 22
1126 MOV DI,OFFSET DOSGROUP
:NAME1
1131 MOV CS:BYTE PTR [CREATING
],-1
1136 MOV AX,[ENTFREE
] ;First free entry found in FINDNAME
1139 CALL GETENT
;Point at that free entry
1149 JNZ ERRPOP
;Error if attributes don't match
1150 OR BH,BH ;Check if file is I/O device
1151 JS OPENJMP
;If so, no action
1152 MOV CX,[SI] ;Get pointer to clusters
1159 CALL RELEASE
;Free any data already allocated
1164 MOV SI,OFFSET DOSGROUP
:NAME1
1188 CLC ;Clear carry so OPEN won't fail
1198 ; AL = Directory block number
1199 ; BP = Base of drive parameters
1201 ; Read the directory block into DIRBUF.
1204 ; All other registers destroyed.
1221 ; BX,DS = Transfer address
1222 ; CX = Number of sectors
1223 ; DX = Absolute record number
1224 ; BP = Base of drive parameters
1226 ; Calls BIOS to perform disk read. If BIOS reports
1227 ; errors, will call HARDERR for further action.
1228 ; BP preserved. All other registers destroyed.
1232 MOV CS:BYTE PTR [READOP
],0
1234 CMP AL,1 ;Check for retry
1236 RET ;Ignore otherwise
1241 ;Hard disk error handler. Entry conditions:
1242 ; DS:BX = Original disk transfer address
1243 ; DX = Original logical sector number
1244 ; CX = Number of sectors to go (first one gave the error)
1245 ; AX = Hardware error code
1246 ; DI = Original sector transfer count
1247 ; BP = Base of drive parameters
1248 ; [READOP] = 0 for read, 1 for write
1250 XCHG AX,DI ;Error code in DI, count in AX
1251 SUB AX,CX ;Number of sectors successfully transferred
1252 ADD DX,AX ;First sector number to retry
1254 MUL [BP.SECSIZ
] ;Number of bytes transferred
1256 ADD BX,AX ;First address for retry
1257 MOV AH,0 ;Flag disk section in error
1258 CMP DX,[BP.FIRFAT
] ;In reserved area?
1260 INC AH ;Flag for FAT
1261 CMP DX,[BP.FIRDIR
] ;In FAT?
1264 CMP DX,[BP.FIRREC
] ;In directory?
1266 INC AH ;Must be in data area
1268 SHL AH,1 ;Make room for read/write bit
1271 MOV AL,[BP.DRVNUM
] ;Get drive number
1273 PUSH BP ;The only thing we preserve
1275 CLI ;Prepare to play with stack
1277 MOV SP,CS:[SPSAVE
] ;User stack pointer restored
1278 INT 24H
;Fatal error interrupt vector
1296 CALL FAR PTR BIOSREAD
1305 TEST BYTE PTR [DIRTYDIR
],-1
1312 ; AL = Directory block number
1313 ; BP = Base of drive parameters
1315 ; Write the directory block into DIRBUF.
1318 ; All other registers destroyed.
1320 MOV BYTE PTR [DIRTYDIR
],0
1321 MOV AL,BYTE PTR [DIRBUFID
]
1328 ; BX,DS = Transfer address
1329 ; CX = Number of sectors
1330 ; DX = Absolute record number
1331 ; BP = Base of drive parameters
1333 ; Calls BIOS to perform disk write. If BIOS reports
1334 ; errors, will call HARDERR for further action.
1335 ; BP preserved. All other registers destroyed.
1343 CALL FAR PTR BIOSWRITE
1349 MOV CS:BYTE PTR [READOP
],1
1351 CMP AL,1 ;Check for retry
1357 LDS SI,CS:DWORD PTR [SPSAVE
]
1380 MOV DI,OFFSET DOSGROUP
:EXITHOLD
1393 JMP CS:DWORD PTR [EXITHOLD
]
1396 SEQRD: ;System call 20
1401 SEQWRT: ;System call 21
1410 RNDRD: ;System call 33
1415 RNDWRT: ;System call 34
1420 BLKRD: ;System call 39
1425 BLKWRT: ;System call 40
1429 LDS SI,DWORD PTR [SPSAVE
]
1435 MOV ES:WORD PTR [DI.RR
],AX
1439 MOV ES:[DI.RR
+3],DH ;Save 4 byte of RECPOS only if significant
1449 MOV ES:[DI.EXTENT
],AX
1457 CMP BYTE PTR [DI],-1
1461 MOV AX,WORD PTR [DI.RR
]
1462 MOV DX,WORD PTR [DI.RR
+2]
1467 MOV BYTE PTR [DSKERR
],4
1474 ; DS:DI point to FCB
1475 ; DX:AX = Record position in file of disk transfer
1479 ; ES:DI point to FCB
1480 ; BL = DEVID from FCB
1481 ; CX = No. of bytes to transfer
1482 ; BP = Base of drive parameters
1484 ; [RECCNT] = Record count
1485 ; [RECPOS] = Record position in file
1487 ; [NEXTADD] = Displacement of disk transfer within segment
1488 ; [SECPOS] = Position of first sector
1489 ; [BYTPOS] = Byte position in file
1490 ; [BYTSECPOS] = Byte position in first sector
1491 ; [CLUSNUM] = First cluster
1492 ; [SECCLUSPOS] = Sector within first cluster
1493 ; [DSKERR] = 0 (no errors yet)
1494 ; [TRANS] = 0 (No transfers yet)
1495 ; [THISDRV] = Physical drive unit number
1496 ; If SETUP detects no records will be transfered, it returns 1 level up
1511 POP ES ;Set ES to DS
1513 POP DS ;Set DS to CS
1514 OR AL,AL ;Is it a device?
1516 MOV AL,0 ;Fake in drive 0 so we can get SP
1521 CMP SI,64 ;Check if highest byte of RECPOS is significant
1523 MOV DH,0 ;Ignore MSB if record >= 64 bytes
1526 MOV WORD PTR [RECPOS
],AX
1527 MOV WORD PTR [RECPOS
+2],DX
1531 MOV BYTE PTR [DSKERR
],0
1532 MOV BYTE PTR [TRANS
],0
1535 MOV WORD PTR [BYTPOS
],AX
1541 ADC DX,0 ;Ripple carry
1543 MOV WORD PTR [BYTPOS
+2],AX
1545 MOV AX,WORD PTR [BYTPOS
]
1547 CMP DX,BX ;See if divide will overflow
1555 MOV AX,CX ;Record count
1556 MOV CL,[BP.CLUSSHFT
]
1559 MUL SI ;Multiply by bytes per record
1561 ADD AX,[DMAADD
] ;See if it will fit in one segment
1563 JZ OK
;Must be less than 64K
1565 NEG AX ;Amount of room left in segment
1566 JNZ PARTSEG
;All 64K available?
1567 DEC AX ;If so, reduce by one
1570 DIV SI ;How many records will fit?
1572 MUL SI ;Translate that back into bytes
1573 MOV BYTE PTR [DSKERR
],2 ;Flag that trimming took place
1577 MOV BL,ES:[DI.DEVID
]
1582 MOV BYTE PTR [DSKERR
],1
1585 POP BX ;Kill return address
1592 ; CX = Length of disk transfer in bytes
1593 ; BP = Base of drive parameters
1594 ; [BYTSECPOS] = Byte position witin first sector
1596 ; [BYTCNT1] = Bytes to transfer in first sector
1597 ; [SECCNT] = No. of whole sectors to transfer
1598 ; [BYTCNT2] = Bytes to transfer in last sector
1599 ;AX, BX, DX destroyed. No other registers affected.
1604 JZ SAVFIR
;Partial first sector?
1606 NEG AX ;Max number of bytes left in first sector
1607 SUB BX,AX ;Subtract from total length
1609 ADD AX,BX ;Don't use all of the rest of the sector
1610 XOR BX,BX ;And no bytes are left
1615 DIV [BP.SECSIZ
] ;How many whole sectors?
1617 MOV [BYTCNT2
],DX ;Bytes remaining for last sector
1625 ; CX = No. of clusters to skip
1626 ; BP = Base of drive parameters
1628 ; ES:DI point to FCB
1630 ; BX = Last cluster skipped to
1631 ; CX = No. of clusters remaining (0 unless EOF)
1632 ; DX = Position of last cluster
1633 ; DI destroyed. No other registers affected.
1635 MOV BX,ES:[DI.LSTCLUS
]
1636 MOV DX,ES:[DI.CLUSPOS
]
1643 MOV BX,ES:[DI.FIRCLUS
]
1662 ; AL = 0 if buffer must be read, 1 if no pre-read needed
1663 ; BP = Base of drive parameters
1664 ; [CLUSNUM] = Physical cluster number
1665 ; [SECCLUSPOS] = Sector position of transfer within cluster
1666 ; [BYTCNT1] = Size of transfer
1668 ; Insure specified sector is in buffer, flushing buffer before
1669 ; read if necessary.
1671 ; SI = Pointer to buffer
1672 ; DI = Pointer to transfer address
1673 ; CX = Number of bytes
1675 ; [TRANS] set to indicate a transfer will occur
1685 JZ FINBUF
;Already have it?
1688 XCHG [DIRTYBUF
],AL ;Read dirty flag and reset it
1701 TEST BYTE PTR [PREREAD
],-1
1704 MOV [BUFSECNO
],AX ;Set buffer valid in case of disk error
1718 MOV BYTE PTR [TRANS
],1 ;A transfer is taking place
1729 XOR AL,AL ;Pre-read necessary
1743 INC AX ;Set for next sector
1745 CMP AX,[VALSEC
] ;Has sector been written before?
1747 JA NOREAD
;Skip preread if SECPOS>VALSEC
1764 MOV BYTE PTR [DIRTYBUF
],1
1768 TEST BYTE PTR [TRANS
],-1
1794 CMP AL,13 ;Check for carriage return
1796 MOV BYTE PTR [SI],10
1801 CALL OUT ;Transmit linefeed
1805 OR AL,1 ;Clear zero flag--not end of file
1811 JNZ SETFCBJ
;Zero set if Ctrl-Z found in input
1813 AND ES:BYTE PTR [DI.DEVID
],0FFH-40H
;Mark as no more data available
1819 LES DI,DWORD PTR [DMAADD
]
1837 CMP BYTE PTR [CONBUF
],128
1839 MOV WORD PTR [CONBUF
],0FF80H ;Set up 128-byte buffer with no template
1844 MOV DX,OFFSET DOSGROUP
:CONBUF
1845 CALL BUFIN
;Get input buffer
1849 MOV SI,2 + OFFSET DOSGROUP
:CONBUF
1850 CMP BYTE PTR [SI],1
AH ;Check for Ctrl-Z in first character
1855 CALL OUT ;Send linefeed
1868 ; DS:DI point to FCB
1869 ; DX:AX = Position in file to read
1870 ; CX = No. of records to read
1872 ; DX:AX = Position of last record read
1873 ; CX = No. of bytes read
1874 ; ES:DI point to FCB
1875 ; LSTCLUS, CLUSPOS fields in FCB set
1878 OR BL,BL ;Check for named device I/O
1880 MOV AX,ES:WORD PTR [DI.FILSIZ
]
1881 MOV BX,ES:WORD PTR [DI.FILSIZ
+2]
1882 SUB AX,WORD PTR [BYTPOS
]
1883 SBB BX,WORD PTR [BYTPOS
+2]
1907 MOV BYTE PTR [TRANS
],1 ;A transfer is taking place
1920 PUSHF ;Save carry flag
1922 POPF ;Restore carry flag
1923 POP DI ;Initial transfer address
1924 POP AX ;First sector transfered
1926 JC NOTBUFFED
;Was one of those sectors in the buffer?
1927 CMP BYTE PTR [DIRTYBUF
],0 ;Is buffer dirty?
1928 JZ NOTBUFFED
;If not no problem
1929 ;We have transfered in a sector from disk when a dirty copy of it is in the buffer.
1930 ;We must transfer the sector from the buffer to correct memory address
1931 SUB AX,[BUFSECNO
] ;How many sectors into the transfer?
1934 MUL CX ;How many bytes into the transfer?
1938 MOV ES,[DMAADD
+2] ;Get disk transfer segment
1952 INC [LASTPOS
] ;We'll be using next cluster
1959 SUB AX,[DMAADD
] ;Number of bytes transfered
1961 MOV CX,ES:[SI.RECSIZ
]
1962 DIV CX ;Number of records
1963 CMP AX,[RECCNT
] ;Check if all records transferred
1965 MOV BYTE PTR [DSKERR
],1
1967 JZ FULLREC
;If remainder 0, then full record transfered
1968 MOV BYTE PTR [DSKERR
],3 ;Flag partial last record
1969 SUB CX,DX ;Bytes left in last record
1972 XCHG AX,BX ;Save the record count temporarily
1973 XOR AX,AX ;Fill with zeros
1979 XCHG AX,BX ;Restore record count to AX
1981 INC AX ;Add last (partial) record to total
1984 MOV DI,SI ;ES:DI point to FCB
1987 MOV ES:[DI.LSTCLUS
],AX
1989 MOV ES:[DI.CLUSPOS
],AX
1991 MOV AX,WORD PTR [RECPOS
]
1992 MOV DX,WORD PTR [RECPOS
+2]
1993 JCXZ RET28
;If no records read, don't change position
1995 ADD AX,CX ;Update current record position
2013 LDS SI,DWORD PTR [DMAADD
]
2020 JZ ENDWRDEV
;Done if device is NUL
2055 MOV BYTE PTR [DSKERR
],1
2057 MOV AX,WORD PTR [RECPOS
]
2058 MOV DX,WORD PTR [RECPOS
+2]
2070 ; DS:DI point to FCB
2071 ; DX:AX = Position in file of disk transfer
2074 ; DX:AX = Position of last record written
2075 ; CX = No. of records written
2076 ; ES:DI point to FCB
2077 ; LSTCLUS, CLUSPOS fields in FCB set
2081 MOV ES:[DI.FDATE
],AX
2082 MOV ES:[DI.FTIME
],DX
2085 AND BL,3FH
;Mark file as dirty
2086 MOV ES:[DI.DEVID
],BL
2088 MOV AX,WORD PTR [BYTPOS
]
2089 MOV DX,WORD PTR [BYTPOS
+2]
2093 ADC DX,0 ;AX:DX=last byte accessed
2094 DIV [BP.SECSIZ
] ;AX=last sector accessed
2095 MOV CL,[BP.CLUSSHFT
]
2096 SHR AX,CL ;Last cluster to be accessed
2098 MOV AX,ES:WORD PTR [DI.FILSIZ
]
2099 MOV DX,ES:WORD PTR [DI.FILSIZ
+2]
2103 INC AX ;Round up if any remainder
2105 MOV [VALSEC
],AX ;Number of sectors that have been written
2107 MOV CX,[CLUSNUM
] ;First cluster accessed
2111 SUB AX,DX ;Last cluster minus current cluster
2112 JZ DOWRT
;If we have last clus, we must have first
2113 JCXZ HAVSTART
;See if no more data
2114 PUSH CX ;No. of clusters short of first
2139 MOV BYTE PTR [TRANS
],1 ;A transfer is taking place
2145 JC NOTINBUF
;Is one of the sectors buffered?
2146 MOV [BUFSECNO
],0 ;If so, invalidate the buffer since we're
2147 MOV WORD PTR [BUFDRVNO
],0FFH ; completely rewritting it
2159 INC [LASTPOS
] ;We'll be using next cluster
2172 ADD AX,WORD PTR [BYTPOS
]
2173 MOV DX,WORD PTR [BYTPOS
+2]
2177 CMP AX,ES:WORD PTR [DI.FILSIZ
]
2178 SBB CX,ES:WORD PTR [DI.FILSIZ
+2]
2180 MOV ES:WORD PTR [DI.FILSIZ
],AX
2181 MOV ES:WORD PTR [DI.FILSIZ
+2],DX
2196 MOV CL,[BP.CLUSSHFT
]
2205 MOV AX,WORD PTR [BYTPOS
]
2206 MOV ES:WORD PTR [DI.FILSIZ
],AX
2207 MOV AX,WORD PTR [BYTPOS
+2]
2208 MOV ES:WORD PTR [DI.FILSIZ
+2],AX
2216 MOV BYTE PTR [SI-1],1
2221 XCHG BX,ES:[DI.FIRCLUS
]
2232 ; BX = Physical cluster
2233 ; CX = No. of records
2234 ; DL = sector within cluster
2235 ; BP = Base of drives parameters
2236 ; [NEXTADD] = transfer address
2238 ; AX = No. of records remaining
2239 ; BX = Transfer address
2240 ; CX = No. or records to be transferred
2241 ; DX = Physical sector address
2243 ; Carry clear if a sector to transfer is in the buffer
2244 ; Carry set otherwise
2245 ; [CLUSNUM] = Last cluster accessed
2247 ; BP unchanged. Note that segment of transfer not set.
2252 INC AL ;Number of sectors per cluster
2254 SUB AL,DL ;AL = Number of sectors left in first cluster
2259 ;AL has number of sectors available in current cluster
2260 ;AH has number of sectors available in next cluster
2261 ;BX has current physical cluster
2262 ;CX has number of sequential sectors found so far
2263 ;DX has number of sectors left to transfer
2276 MOV [CLUSNUM
],BX ;Last cluster accessed
2277 SUB DX,CX ;Number of sectors still needed
2280 MUL [BP.SECSIZ
] ;Number of sectors times sector size
2282 ADD AX,SI ;Adjust by size of transfer
2284 POP AX ;Number of sectors still needed
2285 POP DX ;Starting cluster
2286 SUB BX,DX ;Number of new clusters accessed
2288 POP BX ;BL = sector postion within cluster
2291 ;Now let's see if any of these sectors are already in the buffer
2293 JC RET100
;If DX > [BUFSECNO] then not in buffer
2295 ADD SI,CX ;Last sector + 1
2298 JC RET100
;If SI <= [BUFSECNO] then not in buffer
2301 CMP AL,[BUFDRVNO
] ;Is buffer for this drive?
2303 JZ RET100
;If so, then we match
2307 SUB CX,DX ;Number of sectors in cluster we don't want
2308 SUB AH,CL ;Number of sectors in cluster we accepted
2309 DEC AH ;Adjust to mean position within cluster
2311 MOV CX,DX ;Anyway, make the total equal to the request
2318 ; DX = Physical cluster number
2319 ; BL = Sector postion within cluster
2320 ; BP = Base of drive parameters
2322 ; DX = physical sector number
2323 ;No other registers affected.
2326 MOV CL,[BP.CLUSSHFT
]
2338 ; DS:DX point to FCB
2341 ; DX:AX = Record number determined by EXTENT and NR fields
2342 ; DS:DI point to FCB
2343 ; No other registers affected.
2346 CMP BYTE PTR [DI],-1 ;Check for extended FCB
2366 ; ES = Segment of FCB
2367 ; BX = Last cluster of file (0 if null file)
2368 ; CX = No. of clusters to allocate
2369 ; DX = Position of cluster BX
2370 ; BP = Base of drive parameters
2372 ; [FCB] = Displacement of FCB within segment
2374 ; IF insufficient space
2377 ; CX = max. no. of records that could be added to file
2380 ; BX = First cluster allocated
2381 ; FAT is fully updated including dirty bit
2382 ; FIRCLUS field of FCB set if file was null
2383 ; SI,BP unchanged. All other registers destroyed.
2401 POP AX ;No. of clusters requested
2402 SUB AX,CX ;AX=No. of clusters allocated
2405 INC DX ;Position of first cluster allocated
2406 ADD AX,DX ;AX=max no. of cluster in file
2409 INC DX ;DX=records/cluster
2410 MUL DX ;AX=max no. of records in file
2412 SUB CX,WORD PTR [RECPOS
] ;CX=max no. of records that could be written
2414 XOR CX,CX ;If CX was negative, zero it
2438 MOV BYTE PTR [SI-1],1
2440 POP CX ;Don't need this stuff since we're successful
2448 MOV ES:[DI.FIRCLUS
],BX
2456 ; BX = Cluster in file
2458 ; BP = Base of drive parameters
2460 ; Frees cluster chain starting with [BX]
2461 ; AX,BX,DX,DI all destroyed. Other registers unchanged.
2465 ; Enter here with DX=0FFFH to put an end-of-file mark
2466 ; in the first cluster and free the rest in the chain.
2480 ; BX = Cluster in a file
2481 ; SI = Base of drive FAT
2484 ; BX = Last cluster in the file
2485 ; DI destroyed. No other registers affected.
2494 SRCHFRST: ;System call 17
2497 ; Search-for-next enters here to save place and report
2503 MOV ES:[DI.FILDIRENT
],AX
2504 MOV ES:[DI.DRVBP
],BP
2505 ;Information in directory entry must be copied into the first
2506 ; 33 bytes starting at the disk transfer address.
2508 LES DI,DWORD PTR [DMAADD
]
2521 STOSB ;Set drive number
2523 REP MOVSW ;Copy remaining 10 characters of name
2528 KILLSRCH1 EQU KILLSRCH
+1
2529 ;The purpose of the KILLSRCH1 label is to provide a jump label to the following
2530 ; instruction which leaves out the segment override.
2531 MOV WORD PTR ES:[DI.FILDIRENT
],-1
2536 MOV ES:[DI.FILDIRENT
],BX
2537 LES DI,DWORD PTR [DMAADD
]
2539 STOSB ;Zero drive byte
2540 SUB SI,4 ;Point to device name
2547 STOSW ;Fill with 8 blanks
2554 SRCHNXT: ;System call 18
2557 JC NEAR PTR KILLSRCH1
2559 MOV AX,[DI.FILDIRENT
]
2561 JS NEAR PTR KILLSRCH1
2573 FILESIZE: ;System call 35
2577 ADD DI,33 ;Write size in RR field
2578 MOV CX,ES:[DI.RECSIZ
-33]
2584 XOR DX,DX ;Intialize size to zero
2585 OR BH,BH ;Check for named I/O device
2588 INC SI ;Point to length field
2589 MOV AX,[SI+2] ;Get high word of size
2591 PUSH AX ;Save high part of result
2592 LODSW ;Get low word of size
2594 OR DX,DX ;Check for zero remainder
2597 INC AX ;Round up for partial record
2598 JNZ DEVSIZ
;Propagate carry?
2606 JAE RET14
;Only 3-byte field if RECSIZ >= 64
2611 SETDMA: ;System call 26
2613 MOV CS:[DMAADD
+2],DS
2620 GETFATPT: ;System call 27
2621 MOV DL,0 ;Use default drive
2623 GETFATPTDL: ;System call 28
2636 LDS SI,DWORD PTR [SPSAVE
]
2644 GETDSKPT: ;System call 31
2650 LDS SI,DWORD PTR [SPSAVE
]
2656 DSKRESET: ;System call 13
2660 ; DS=CS. Writes back all dirty FATs. All registers destroyed.
2683 GETDRV: ;System call 25
2688 SETRNDREC: ;System call 36
2694 MOV [DI+36],DH ;Set 4th byte only if record size < 64
2698 SELDSK: ;System call 14
2705 BUFIN: ;System call 10
2717 CMP BYTE PTR [BX+SI],0DH
2726 MOV CS:[STARTPOS
],AL
2728 MOV DI,OFFSET DOSGROUP
:INBUF
2734 CMP AL,"F"-"@" ;Ignore ^F
2771 MOV DI,OFFSET DOSGROUP
:ESCTAB
2776 JMP [BP+OFFSET DOSGROUP
:ESCFUNC
]
2789 MOV SI,OFFSET DOSGROUP
:INBUF
2809 MOV AL,CS:[STARTPOS]
2844 CMP ES:BYTE PTR [DI+1],9
2850 SUB BL,CS:[STARTPOS]
3015 CONOUT: ;System call 2
3022 INC CS:BYTE PTR [CARPOS]
3027 CALL FAR PTR BIOSOUT
3028 TEST CS:BYTE PTR [PFLAG],-1
3030 CALL FAR PTR BIOSPRINT
3034 CALL FAR PTR BIOSSTAT
3038 CALL FAR PTR BIOSIN ;Eat Cntrl-S
3050 ; "^C
" and CR/LF is printed. Then the user registers are restored and the
3051 ; user CTRL-C handler is executed. At this point the top of the stack has
3052 ; 1) the interrupt return address should the user CTRL-C handler wish to
3053 ; allow processing to continue; 2) the original interrupt return address
3054 ; to the code that performed the function call in the first place. If the
3055 ; user CTRL-C handler wishes to continue, it must leave all registers
3056 ; unchanged and IRET. The function that was interrupted will simply be
3058 MOV AL,3 ;Display "^C
"
3061 CLI ;Prepare to play with stack
3063 MOV SP,CS:[SPSAVE] ;User stack now restored
3072 POP ES ;User registers now restored
3073 INT CONTC ;Execute user Ctrl-C handler
3074 JMP COMMAND ;Repeat command otherwise
3078 NOT CS:BYTE PTR [PFLAG]
3082 MOV CS:BYTE PTR [PFLAG],1
3086 MOV CS:BYTE PTR [PFLAG],0
3114 MOV CS:BYTE PTR [CARPOS],0
3118 DEC CS:BYTE PTR [CARPOS]
3122 CONSTAT: ;System call 11
3130 CONIN: ;System call 1
3143 RAWIO: ;System call 6
3147 LDS SI,DWORD PTR CS:[SPSAVE] ;Get pointer to register save area
3148 CALL FAR PTR BIOSSTAT
3150 OR BYTE PTR [SI.FSAVE],40H ;Set user's zero flag
3155 AND BYTE PTR [SI.FSAVE],0FFH-40H ;Reset user's zero flag
3156 RAWINP: ;System call 7
3160 CALL FAR PTR BIOSOUT
3163 LIST: ;System call 5
3169 CALL FAR PTR BIOSPRINT
3172 PRTBUF: ;System call 9
3181 OUTMES: ;String output for internal messages
3182 LODS CS:BYTE PTR [SI]
3189 MAKEFCB: ;Interrupt call 41
3193 MOV DL,0 ;Flag--not ambiguous file name
3194 TEST AL,DRVBIT ;Use current drive field if default?
3196 MOV BYTE PTR ES:[DI],0 ;No - use default drive
3200 TEST AL,NAMBIT ;Use current name fiels as defualt?
3201 XCHG AX,BX ;Save bits in BX
3203 JZ FILLB ;If not, go fill with blanks
3205 XOR CX,CX ;Don't fill any
3209 TEST BL,EXTBIT ;Use current extension as default
3215 XCHG AX,CX ;Put zero in AX
3217 STOSW ;Initialize two words after to zero
3218 SUB DI,16 ;Point back at start
3219 TEST BL,1 ;Scan off separators if not zero
3221 CALL SCANB ;Peel off blanks and tabs
3222 CALL DELIM ;Is it a one-time-only delimiter?
3224 INC SI ;Skip over the delimiter
3226 CALL SCANB ;Always kill preceding blanks and tabs
3229 JBE NODRV ;Quit if termination character
3230 CMP BYTE PTR[SI],":" ;Check for potential drive specifier
3232 INC SI ;Skip over colon
3233 SUB AL,"@
" ;Convert drive letter to binary drive number
3234 JBE BADDRV ;Valid drive numbers are 1-15
3240 STOSB ;Put drive specifier in first byte
3242 DEC DI ;Counteract next two instructions
3245 INC DI ;Skip drive byte
3247 CALL GETWORD ;Get 8-letter file name
3248 CMP BYTE PTR [SI],"."
3250 INC SI ;Skip over dot if present
3251 MOV CX,3 ;Get 3-letter extension
3254 LDS BX,CS:DWORD PTR [SPSAVE]
3266 JBE NONAM ;Exit if invalid character
3273 CMP AL,"*" ;Check for ambiguous file specifier
3281 OR DL,1 ;Flag ambiguous file name
3297 ;Get a byte from [SI], convert it to upper case, and compare for delimiter.
3298 ;ZF set if a delimiter, CY set if a control character (other than TAB).
3305 SUB AL,20H ;Convert to upper case
3321 CMP AL,":" ;Allow ":" as separator in IBM version
3336 CMP AL,9 ;Filter out tabs too
3338 ;WARNING! " " MUST be the last compare
3342 SETVECT: ; Interrupt call 37
3353 NEWBASE: ; Interrupt call 38
3355 LDS SI,CS:DWORD PTR [SPSAVE]
3366 ; AX = Size of memory in paragraphs
3369 ; Completely prepares a program base at the
3370 ; specified segment.
3375 ; [2] = First unavailable segment ([ENDMEM])
3376 ; [5] to [9] form a long call to the entry point
3377 ; [10] to [13] have exit address (from INT 22H)
3378 ; [14] to [17] have ctrl-C exit address (from INT 23H)
3379 ; [18] to [21] have fatal error address (from INT 24H)
3380 ; DX,BP unchanged. All other registers destroyed.
3399 MOV BX,ENTRYPOINTSEG
3408 MOV DS:[0],20CDH ;"INT INTTAB"
3409 MOV DS:(BYTE PTR [5]),LONGCALL
3415 SHL CL,1 ;Minutes to left part of byte
3417 SHL CX,1 ;Push hours and minutes to left end
3420 SHR DH,1 ;Count every two seconds
3421 OR CL,DH ;Combine seconds with hours and minutes
3424 MOV AX,WORD PTR [MONTH] ;Fetch month and year
3425 SHL AL,1 ;Push month to left to make room for day
3433 FOURYEARS EQU 3*365+366
3436 ;Gets time in CX:DX. Figures new date if it has changed.
3438 CALL FAR PTR BIOSGETTIME
3439 CMP AX,[DAYCNT] ;See if day count is the same
3441 CMP AX,FOURYEARS*30 ;Number of days in 120 years
3442 JAE RET22 ;Ignore if too large
3448 MOV CX,FOURYEARS ;Number of days in 4 years
3449 DIV CX ;Compute number of 4-year units
3452 SHL AX,1 ;Multiply by 8 (no. of half-years)
3453 MOV CX,AX ;<240 implies AH=0
3454 MOV SI,OFFSET DOSGROUP:YRTAB ;Table of days in each year
3455 CALL DSLIDE ;Find out which of four years we're in
3456 SHR CX,1 ;Convert half-years to whole years
3457 JNC SK ;Extra half-year?
3461 MOV CL,1 ;At least at first month in year
3462 MOV SI,OFFSET DOSGROUP:MONTAB ;Table of days in each month
3463 CALL DSLIDE ;Find out which month we're in
3465 INC DX ;Remainder is day of month (start with one)
3467 CALL WKDAY ;Set day of week
3476 LODSB ;Get count of days
3477 CMP DX,AX ;See if it will fit
3478 JB RET23 ;If not, done
3480 INC CX ;Count one more month/year
3484 ;Set year with value in CX. Adjust length of February for this year.
3485 MOV BYTE PTR [YEAR],CL
3487 TEST CL,3 ;Check for leap year
3489 JNZ SAVFEB ;28 days if no leap year
3490 INC AL ;Add leap day
3492 MOV [MONTAB+1],AL ;Store for February
3496 YRTAB DB 200,166 ;Leap year
3502 MONTAB DB 31 ;January
3503 DB 28 ;February--reset each time year changes
3515 GETDATE: ;Function call 42
3518 CALL READTIME ;Check for rollover to next day
3520 MOV BX,WORD PTR [DAY]
3521 LDS SI,DWORD PTR [SPSAVE] ;Get pointer to user registers
3522 MOV [SI.DXSAVE],BX ;DH=month, DL=day
3523 ADD AX,1980 ;Put bias back
3524 MOV [SI.CXSAVE],AX ;CX=year
3528 SETDATE: ;Function call 43
3529 MOV AL,-1 ;Be ready to flag error
3530 SUB CX,1980 ;Fix bias in year
3531 JC RET24 ;Error if not big enough
3532 CMP CX,119 ;Year must be less than 2100
3537 JZ RET24 ;Error if either month or day is 0
3538 CMP DH,12 ;Check against max. month
3542 CALL CHKYR ;Set Feb. up for new year
3544 MOV BX,OFFSET DOSGROUP:MONTAB-1
3545 XLAT ;Look up days in month
3547 MOV AL,-1 ;Restore error flag, just in case
3548 JB RET24 ;Error if too many days
3550 MOV WORD PTR [DAY],DX ;Set both day and month
3556 MOV CL,BYTE PTR [YEAR]
3558 MOV SI,OFFSET DOSGROUP:YRTAB
3560 SHL CX,1 ;Two entries per year, so double count
3561 CALL DSUM ;Add up the days in each year
3562 MOV CL,BH ;Month of year
3563 MOV SI,OFFSET DOSGROUP:MONTAB
3564 DEC CX ;Account for months starting with one
3565 CALL DSUM ;Add up days in each month
3566 MOV CL,BL ;Day of month
3567 DEC CX ;Account for days starting with one
3568 ADD DX,CX ;Add in to day total
3569 XCHG AX,DX ;Get day count in AX
3571 CALL FAR PTR BIOSSETDATE
3577 INC AX ;First day was Tuesday
3578 DIV CX ;Compute day of week
3592 GETTIME: ;Function call 44
3596 LDS SI,DWORD PTR [SPSAVE] ;Get pointer to user registers
3602 SETTIME: ;Function call 45
3603 ;Time is in CX:DX in hours, minutes, seconds, 1/100 sec.
3604 MOV AL,-1 ;Flag in case of error
3605 CMP CH,24 ;Check hours
3607 CMP CL,60 ;Check minutes
3609 CMP DH,60 ;Check seconds
3611 CMP DL,100 ;Check 1/100's
3613 CALL FAR PTR BIOSSETTIME
3618 ; Default handler for division overflow trap
3622 MOV SI,OFFSET DOSGROUP:DIVMES
3626 INT 23H ;Use Ctrl-C abort on divide overflow
3629 CODSIZ EQU $-CODSTRT ;Size of code segment
3633 ;***** DATA AREA *****
3634 CONSTANTS SEGMENT BYTE
3636 CONSTRT EQU $ ;Start of constants segment
3640 DB "PRN ","LST ","NUL ","AUX ","CON "
3643 DB "COM1","PRN ","LPT1","NUL ","AUX ","CON "
3645 DIVMES DB 13,10,"Divide overflow",13,10,"$"
3649 DIRTYDIR DB 0 ;Dirty buffer flag
3650 NUMDRV DB 0 ;Number of drives
3651 NUMIO DB ? ;Number of disk tables
3652 VERFLG DB 0 ;Initialize with verify off
3654 DMAADD DW 80H ;User's disk transfer address (disp/seg)
3669 CURDRV DB 0 ;Default to drive A
3670 DRVTAB DW 0 ;Address of start of DPBs
3671 DOSLEN EQU CODSIZ+($-CONSTRT) ;Size of CODE + CONSTANTS segments
3675 ; Init code overlaps with data area below
3678 INBUF DB 128 DUP (?)
3679 CONBUF DB 131 DUP (?) ;The rest of INBUF and console buffer
3681 EXITHOLD DB 4 DUP (?)
3683 NAME1 DB 11 DUP (?) ;File name buffer
3688 ;WARNING - the following two items are accessed as a word
3695 SECCLUSPOS DB ? ;Position of first sector within cluster
3698 PREREAD DB ? ;0 means preread; 1 means optional
3703 FCB DW ? ;Address of user FCB
3709 SECPOS DW ? ;Position of first sector accessed
3710 VALSEC DW ? ;Number of valid (previously written) sectors
3711 BYTSECPOS DW ? ;Position of first byte within sector
3712 BYTPOS DB 4 DUP (?) ;Byte position in file of access
3713 BYTCNT1 DW ? ;No. of bytes in first sector
3714 BYTCNT2 DW ? ;No. of bytes in last sector
3715 SECCNT DW ? ;No. of whole sectors
3718 DB 80H DUP (?) ;Stack space
3730 ;Init code below overlaps with data area above
3735 ;This section of code is safe from being overwritten by block move
3736 REP MOVS BYTE PTR [DI],[SI]
3738 MOV ES:[DMAADD+2],DX
3739 MOV SI,[DRVTAB] ;Address of first DPB
3741 MOV CL,[NUMIO] ;Number of DPBs
3743 MOV DI,ES:[SI.FAT] ;get pointer to FAT
3744 DEC DI ;Point to dirty byte
3745 STOSB ;Flag as unused
3746 ADD SI,DPBSIZ ;Point to next DPB
3749 CALL SETMEM ;Set up segment
3761 LODSB ;Get no. of drives & no. of I/O drivers
3763 MOV DI,OFFSET DOSGROUP:MEMSTRT
3768 LODSB ;Physical unit no.
3772 CBW ;Index into FAT size table
3774 ADD AX,OFFSET DOSGROUP:FATSIZTAB
3776 LODSW ;Pointer to DPT
3798 MOVSW ;FIRFAT (= number of reserved sectors)
3801 MOV AX,DX ;SECSIZ again
3804 MOV CX,AX ;Directory entries per sector
3806 ADD AX,ES:[BP.MAXENT]
3809 STOSW ;DIRSEC (temporarily)
3810 MOVSW ;DSKSIZ (temporarily)
3818 CMP AL,DL ;Compare newly computed FAT size with trial
3819 JZ HAVFATSIZ ;Has sequence converged?
3820 CMP AL,DH ;Compare with previous trial
3822 MOV DL,AL ;Shuffle trials
3823 JNZ GETFATSIZ ;Continue iterations if not oscillating
3824 DEC WORD PTR ES:[BP.DSKSIZ] ;Damp those oscillations
3825 JMP SHORT FNDFATSIZ ;Try again
3828 MOV SI,OFFSET DOSGROUP:BADMES
3835 MUL ES:BYTE PTR[BP.FATCNT] ;Space occupied by all FATs
3836 ADD AX,ES:[BP.FIRFAT]
3838 ADD AX,ES:[BP.DIRSEC]
3839 MOV ES:[BP.FIRREC],AX ;Destroys DIRSEC
3841 MOV ES:[BP.MAXCLUS],CX
3842 MOV AX,BX ;Pointer into FAT size table
3843 STOSW ;Allocate space for FAT pointer
3844 MOV AL,ES:[BP.FATSIZ]
3847 CMP AX,ES:[BX] ;Bigger than already allocated
3851 POP SI ;Restore pointer to init. table
3865 ;Calculate true address of buffers, FATs, free space
3867 MOV AX,OFFSET DOSGROUP:DIRBUF
3869 MOV [BUFFER],AX ;Start of buffer
3871 MOV [DRVTAB],AX ;Start of DPBs
3872 SHL BP,1 ;Two sectors - directory and buffer
3873 ADD BP,DI ;Allocate buffer space
3874 ADD BP,ADJFAC ;True address of FATs
3876 MOV SI,OFFSET DOSGROUP:FATSIZTAB
3880 INC BP ;Add one for Dirty byte
3881 INC BP ;Add one for I/O device number
3882 LODSW ;Get size of this FAT
3884 STOSW ;Save address of this FAT
3885 ADD BP,AX ;Compute size of next FAT
3886 CMP AX,BP ;If size was zero done
3889 SUB AL,CL ;Compute number of FATs used
3891 XOR AX,AX ;Set zero flag
3892 REPZ SCASW ;Make sure all other entries are zero
3894 ADD BP,15 ;True start of free space
3896 SHR BP,CL ;First free segment
3901 CMP CX,1 ;Use memory scan?
3903 MOV CX,DX ;Start scanning just after DOS
3918 MOV BP,CX ;Segment of DOS
3919 MOV DX,CS ;Program segment
3924 ; BP has segment of DOS (whether to load high or run in place)
3925 ; DX has program segment (whether after DOS or overlaying DOS)
3926 ; CX has size of memory in paragraphs (reduced by DOS size if HIGHMEM)
3935 REP MOVSW ;Move DOS to high memory
3941 MOV AX,OFFSET DOSGROUP:QUIT
3942 STOSW ;Set abort address--displacement
3944 MOV BYTE PTR DS:[ENTRYPOINT],LONGJUMP
3945 MOV WORD PTR DS:[ENTRYPOINT+1],OFFSET DOSGROUP:ENTRY
3946 MOV WORD PTR DS:[ENTRYPOINT+3],AX
3947 MOV WORD PTR DS:[0],OFFSET DOSGROUP:DIVOV ;Set default divide trap address
3950 REP STOSW ;Set 5 segments (skip 2 between each)
3951 MOV WORD PTR DS:[INTBASE+4],OFFSET DOSGROUP:COMMAND
3952 MOV WORD PTR DS:[INTBASE+12],OFFSET DOSGROUP:IRET ;Ctrl-C exit
3953 MOV WORD PTR DS:[INTBASE+16],OFFSET DOSGROUP:IRET ;Fatal error exit
3954 MOV AX,OFFSET BIOSREAD
3960 MOV WORD PTR DS:[INTBASE+18H],OFFSET BIOSWRITE
3961 MOV WORD PTR DS:[EXIT],100H
3962 MOV WORD PTR DS:[EXIT+2],DX
3964 MOV SI,OFFSET DOSGROUP:HEADER
3971 ;Move the FATs into position
3975 MOV DI,OFFSET DOSGROUP:MEMSTRT.FAT
3977 MOV SI,WORD PTR [DI] ;Get address within FAT address table
3978 MOVSW ;Set address of this FAT
3979 ADD DI,DPBSIZ-2 ;Point to next DPB
3981 POP CX ;True address of first FAT
3982 MOV SI,OFFSET DOSGROUP:MEMSTRT ;Place to move DPBs from
3983 MOV DI,[DRVTAB] ;Place to move DPBs to
3984 SUB CX,DI ;Total length of DPBs
3986 JBE MOVJMP ;Are we moving to higher or lower memory?
3987 DEC CX ;Move backwards to higher memory
3997 MUL ES:BYTE PTR[BP.FATCNT]
3998 ADD AX,ES:[BP.FIRFAT]
3999 ADD AX,ES:[BP.DIRSEC]
4001 ;AX has equivalent of FIRREC
4002 SUB AX,ES:[BP.DSKSIZ]
4004 MOV CL,ES:[BP.CLUSSHFT]
4011 ADC AX,DX ;Size of FAT in bytes
4012 MOV SI,ES:[BP.SECSIZ]
4020 DB 13,10,"INIT TABLE BAD",13,10,"$"
4023 DW 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4028 ADJFAC EQU DIRBUF-MEMSTRT