2 TITLE VDISK
- Virtual Disk Device Driver
4 ;VDISK simulates a disk drive, using Random Access Memory as the storage medium.
6 ;This program is meant to serve as an example of a device driver. It does not
7 ;reflect the current level of VDISK.SYS.
9 ;(C) Copyright 1988 Microsoft
10 ;Licensed Material - Program Property of Microsoft
12 ;Add the following statement to CONFIG.SYS
13 ; DEVICE=[d:][path]VDISK.SYS bbb sss ddd [/E:m]
15 ; where: bbb is the desired buffer size (in kilobytes)
16 ; minimum 1KB, maximum is size of available memory,
19 ; VDISK will leave at least 64KB of available memory,
20 ; although subsequent device drivers (other than VDISK)
21 ; other programs that make themselves resident, and
22 ; COMMAND.COM will result in less than 64KB as shown
25 ; Must be large enough for 1 boot sector + FAT sectors
26 ; + 1 directory sector + at least 1 data cluster,
27 ; or the device driver won't be installed.
29 ; sss is the desired sector size (in bytes)
30 ; 128, 256, or 512, default is 128.
31 ; Will be adjusted if number of FAT entries > 0FE0H
33 ; ddd is the desired number of directory entries
34 ; Minimum 2, maximum 512, default 64.
35 ; Will be rounded upward to sector size boundary.
37 ; /E may only be used if extended memory above 1 megabyte
38 ; is to be used. INT 15H functions 87H and 88H are used
39 ; to read and write this extended memory.
40 ; The m parameter in the /E option specifies the maximum
41 ; number of sectors that the VDISK will transfer at a time.
42 ; Optional values are 1,2,3,4,5,6,7 or 8 sectors, the default
45 ; Brackets indicate optional operands.
49 ; DEVICE=\path\VDISK.SYS 160 512 64
50 ; results in a 160KB VDISK, with 512 byte sectors, 64 directory entries
52 ; DEVICE=VDISK.SYS Buffersize 60 Sectorsize 128 Directory entries 32
53 ; (since only numbers are interpreted, you may comment the line with
54 ; non-numeric characters)
56 SUBTTL Structure Definitions
58 ;-----------------------------------------------------------------------;
59 ; Request Header (Common portion) ;
60 ;-----------------------------------------------------------------------;
61 RH EQU
DS:[BX] ;addressability to Request Header structure
63 RHC
STRUC ;fields common to all request types
64 DB ?
;length of Request Header (including data)
65 DB ?
;unit code (subunit)
66 RHC_CMD
DB ?
;command code
68 DQ ?
;reserved for DOS
69 RHC ENDS
;end of common portion
71 CMD_INPUT EQU
4 ;RHC_CMD is INPUT request
73 ;status values for RHC_STA
75 STAT_DONE EQU
01H ;function complete status (high order byte)
76 STAT_CMDERR EQU 8003H
;invalid command code error
77 STAT_CRC EQU 8004H
;CRC error
78 STAT_SNF EQU 8008H
;sector not found error
79 STAT_BUSY EQU
0200H ;busy bit (9) for Removable Media call
80 ;-----------------------------------------------------------------------;
81 ; Request Header for INIT command ;
82 ;-----------------------------------------------------------------------;
84 DB (TYPE RHC
) DUP (?
) ;common portion
85 RH0_NUN
DB ?
;number of units
86 ;set to 1 if installation succeeds,
87 ;set to 0 to cause installation failure
88 RH0_ENDO
DW ?
;offset of ending address
89 RH0_ENDS
DW ?
;segment of ending address
90 RH0_BPBO
DW ?
;offset of BPB array address
91 RH0_BPBS
DW ?
;segment of BPB array address
92 RH0_DRIV
DB ?
;drive code (DOS 3 only)
95 RH0_BPBA EQU
DWORD PTR RH0_BPBO
;offset/segment of BPB array address
96 ;Note: RH0_BPBA at entry to INIT points to all after DEVICE= on CONFIG.SYS stmt
98 ;-----------------------------------------------------------------------;
99 ; Request Header for MEDIA CHECK Command ;
100 ;-----------------------------------------------------------------------;
102 DB (TYPE RHC
) DUP (?
) ;common portion
103 DB ?
;media descriptor
104 RH1_RET
DB ?
;return information
106 ;-----------------------------------------------------------------------;
107 ; Request Header for BUILD BPB Command ;
108 ;-----------------------------------------------------------------------;
110 DB (TYPE RHC
) DUP(?
) ;common portion
111 DB ?
;media descriptor
112 DW ?
;offset of transfer address
113 DW ?
;segment of transfer address
114 RH2_BPBO
DW ?
;offset of BPB table address
115 RH2_BPBS
DW ?
;segment of BPB table address
117 ;-----------------------------------------------------------------------;
118 ; Request Header for INPUT, OUTPUT, and OUTPUT with verify ;
119 ;-----------------------------------------------------------------------;
121 DB (TYPE RHC
) DUP (?
) ;common portion
122 DB ?
;media descriptor
123 RH4_DTAO
DW ?
;offset of transfer address
124 RH4_DTAS
DW ?
;segment of transfer address
125 RH4_CNT
DW ?
;sector count
126 RH4_SSN
DW ?
;starting sector number
129 RH4_DTAA EQU
DWORD PTR RH4_DTAO
;offset/segment of transfer address
131 ;-----------------------------------------------------------------------;
132 ; Segment Descriptor (part of Global Descriptor Table) ;
133 ;-----------------------------------------------------------------------;
134 DESC
STRUC ;data segment descriptor
135 DESC_LMT
DW 0 ;segment limit (length)
136 DESC_BASEL
DW 0 ;bits 15-0 of physical address
137 DESC_BASEH
DB 0 ;bits 23-16 of physical address
138 DB 0 ;access rights byte
142 SUBTTL Equates
and Macro Definitions
145 MEM_SIZE EQU 12H
;BIOS memory size determination INT
146 ;returns system size in KB in AX
148 EM_INT EQU 15H
;extended memory BIOS interrupt INT
149 EM_BLKMOVE EQU 87H
;block move function
150 EM_MEMSIZE EQU 88H
;memory size determination in KB
152 BOOT_INT EQU 19H
;bootstrap DOS
154 DOS EQU 21H
;DOS request INT
155 DOS_PCHR EQU
02H ;print character function
156 DOS_PSTR EQU
09H ;print string function
157 DOS_VERS EQU 30H
;get DOS version
159 TAB EQU
09H ;ASCII tab
160 LF EQU
0AH ;ASCII line feed
161 CR EQU
0DH ;ASCII carriage return
162 BEL EQU
07H ;ASCII bell
164 PARA_SIZE EQU
16 ;number of bytes in one 8088 paragraph
165 DIR_ENTRY_SIZE EQU
32 ;number of bytes per directory entry
166 MAX_FATE EQU
0FE0H ;largest number of FAT entries allowed
168 ;default values used if parameters are omitted
170 DFLT_BSIZE EQU
64 ;default VDISK buffer size (KB)
171 DFLT_SSZ EQU
128 ;default sector size
172 DFLT_DIRN EQU
64 ;default number of directory entries
173 DFLT_ESS EQU
8 ;default maximum sectors to transfer
175 MIN_DIRN EQU
2 ;minimum number of directory entries
176 MAX_DIRN EQU
512 ;maximum number of directory entries
178 STACK_SIZE EQU
512 ;length of stack during initialization
180 ;-----------------------------------------------------------------------;
181 ; MSG invokes the console message subroutine ;
182 ;-----------------------------------------------------------------------;
185 PUSH DX ;;save DX across call
186 MOV DX,OFFSET TEXT
;;point to message
187 CALL SHOW_MSG
;;issue message
192 SUBTTL Resident
Data Area
194 ;-----------------------------------------------------------------------;
195 ; Map INT 19H vector in low storage ;
196 ;-----------------------------------------------------------------------;
197 INT_VEC
SEGMENT AT 00H
200 BOOT_VECO
DW ?
;offset
201 BOOT_VECS
DW ?
;segment
205 CSEG
SEGMENT PARA
PUBLIC 'CODE'
207 ;-----------------------------------------------------------------------;
208 ; Resident data area. ;
210 ; All variables and constants required after initialization ;
211 ; part one are defined here. ;
212 ;-----------------------------------------------------------------------;
214 START EQU
$ ;begin resident VDISK data & code
216 ;DEVICE HEADER - must be at offset zero within device driver
217 DD -1 ;becomes pointer to next device header
218 DW 0800H ;attribute (IBM format block device)
219 ;supports OPEN/CLOSE/RM calls
220 DW OFFSET STRATEGY
;pointer to device "strategy" routine
221 DW OFFSET IRPT
;pointer to device "interrupt handler"
222 DB 1 ;number of block devices
223 DB 7 DUP (?
) ;7 byte filler (remainder of 8-byte name)
224 ;END OF DEVICE HEADER
226 ;This volume label is placed into the directory of the new VDISK
227 ;This constant is also used to determine if a previous extended memory VDISK
230 VOL_LABEL
DB 'VDISK ' ;00-10 volume name (shows program level)
231 DB 28H
;11-11 attribute (volume label)
233 DW 6000H
;22-23 time=12:00 noon
234 DW 0986H ;24-25 date=12/06/84
235 VOL_LABEL_LEN EQU
$-VOL_LABEL
;length of volume label
237 ;The following field, in the first extended memory VDISK device driver,
238 ;is the 24-bit address of the first free byte of extended memory.
239 ;This address is not in the common offset/segment format.
240 ;The initial value, 10 0000H, is 1 megabyte.
242 AVAIL_LO
DW 0 ;address of first free byte of
243 AVAIL_HI
DB 10H
;extended memory
245 ;The INT 19H vector is "stolen" by the first VDISK installed in extended memory.
246 ;The original content of the interrupt vector is saved here.
250 INTV19S
DW ?
;segment
253 PARAS_PER_SECTOR
DW ?
;number of 16-byte paragraphs in one sector
255 START_BUFFER_PARA
DW ?
;segment address of start of VDISK buffer
256 ;for extended memory, this segment address
257 ;is the end of the VDISK device driver.
259 EM_SW
DB 0 ;non-zero if Extended Memory
261 EM_STAT
DW 0 ;AX from last unsuccessful extended memory I/O
263 START_EM_LO
DW ?
;24-bit address of start of VDISK buffer
264 START_EM_HI
DB ?
;(extended memory only)
266 WPARA_SIZE
DW PARA_SIZE
;number of bytes in one paragraph
268 MAX_CNT
DW ?
;(0FFFFH/BPB_SSZ) truncated, the maximum
269 ;number of sectors that can be transferred
270 ;without worrying about 64KB wrap
272 SECT_LEFT
DW ?
;sectors left to transfer
274 IO_SRCA
LABEL DWORD ;offset/segment of source
276 IO_SRCS
DW ?
;segment
278 IO_TGTA
LABEL DWORD ;offset/segment of target
280 IO_TGTS
DW ?
;segment
282 ;-----------------------------------------------------------------------;
283 ; BIOS Parameter Block (BPB) ;
284 ;-----------------------------------------------------------------------;
285 ;This is where the characteristics of the virtual disk are established.
286 ;A copy of this block is moved into the boot record of the virtual disk.
287 ;DEBUG can be used to read sector zero of the virtual disk to examine the
288 ;boot record copy of this block.
290 BPB
LABEL BYTE ;BIOS Parameter Block (BPB)
291 BPB_SSZ
DW 0 ;number of bytes per disk sector
292 BPB_AUSZ
DB 1 ;sectors per allocation unit
293 BPB_RES
DW 1 ;number of reserved sectors (for boot record)
294 BPB_FATN
DB 1 ;number of File Allocation Table (FAT) copies
295 BPB_DIRN
DW 0 ;number of root directory entries
296 BPB_SECN
DW 1 ;total number of sectors
297 ;computed from buffer size and sector size
298 ;(this includes reserved, FAT, directory,
300 BPB_MCB
DB 0FEH ;media descriptor byte
301 BPB_FATSZ
DW 1 ;number of sectors occupied by a single FAT
302 ;computed from BPBSSZ and BPBSECN
303 BPB_LEN EQU
$-BPB
;length of BIOS parameter block
305 BPB_PTR
DW BPB
;BIOS Parameter Block pointer array (1 entry)
306 ;-----------------------------------------------------------------------;
307 ; Request Header (RH) address, saved here by "strategy" routine ;
308 ;-----------------------------------------------------------------------;
311 RH_PTRS
DW ?
;segment
312 ;-----------------------------------------------------------------------;
313 ; Global Descriptor Table (GDT), used for extended memory moves ;
314 ;-----------------------------------------------------------------------;
315 ;Access Rights Byte (93H) is
316 ; P=1 (segment is mapped into physical memory)
317 ; E=0 (data segment descriptor)
318 ; D=0 (grow up segment, offsets must be <= limit)
319 ; W=1 (data segment may be written into)
320 ; DPL=0 (privilege level 0)
322 GDT
LABEL BYTE ;begin global descriptor table
323 DESC
<> ;dummy descriptor
324 DESC
<> ;descriptor for GDT itself
325 SRC DESC
<,,,93H
,> ;source descriptor
326 TGT DESC
<,,,93H
,> ;target descriptor
327 DESC
<> ;BIOS CS descriptor
328 DESC
<> ;stack segment descriptor
330 SUBTTL
INT 19H
(boot
) interrupt handler
332 ;-----------------------------------------------------------------------;
333 ; INT 19H Interrupt Handler routine ;
334 ;-----------------------------------------------------------------------;
335 ;The INT 19H vector is altered by VDISK initialization to point to this
336 ;routine within the first extended memory VDISK device driver.
338 ;The vector points to the device driver so that subsequent VDISKs installed
339 ;in extended memory can find the first one to determine what memory has
340 ;already been allocated to VDISKs.
342 ;This routine restores the original INT 19H vector's content, then jumps
343 ;to the original routine.
345 ;INT 19H, the "Boot" INT, is always altered when DOS is booted.
347 ;This routine is entered with interrupts disabled.
349 VDISK_INT19 PROC
;INT 19H received
350 PUSH DS ;save registers we're going to alter
354 MOV DS,AX ;set DS = 0
357 MOV AX,CS:INTV19O
;get offset of saved vector
358 MOV DS:BOOT_VECO
,AX ;store offset in interrupt vector
360 MOV AX,CS:INTV19S
;get segment of saved vector
361 MOV DS:BOOT_VECS
,AX ;store segment in interrupt vector
366 JMP CS:INTV19
;go to original interrupt routine
372 SUBTTL Device Strategy
& interrupt
entry points
374 ;-----------------------------------------------------------------------;
375 ; Device "strategy" entry point ;
377 ; Retain the Request Header address for use by Interrupt routine ;
378 ;-----------------------------------------------------------------------;
380 MOV CS:RH_PTRO
,BX ;offset
381 MOV CS:RH_PTRS
,ES ;segment
384 ;-----------------------------------------------------------------------;
385 ; Table of command processing routine entry points ;
386 ;-----------------------------------------------------------------------;
388 DW OFFSET INIT_P1
; 0 - Initialization
389 DW OFFSET MEDIA_CHECK
; 1 - Media check
390 DW OFFSET BLD_BPB
; 2 - Build BPB
391 DW OFFSET INPUT_IOCTL
; 3 - IOCTL input
392 DW OFFSET INPUT
; 4 - Input
393 DW OFFSET INPUT_NOWAIT
; 5 - Non destructive input no wait
394 DW OFFSET INPUT_STATUS
; 6 - Input status
395 DW OFFSET INPUT_FLUSH
; 7 - Input flush
396 DW OFFSET OUTPUT
; 8 - Output
397 DW OFFSET OUTPUT_VERIFY
; 9 - Output with verify
398 DW OFFSET OUTPUT_STATUS
;10 - Output status
399 DW OFFSET OUTPUT_FLUSH
;11 - Output flush
400 DW OFFSET OUTPUT_IOCTL
;12 - IOCTL output
401 DW OFFSET DEVICE_OPEN
;13 - Device OPEN
402 DW OFFSET DEVICE_CLOSE
;14 - Device CLOSE
403 MAX_CMD EQU
($-CMD_TABLE
)/2 ;highest valid command follows
404 DW OFFSET REMOVABLE_MEDIA
;15 - Removable media
406 ;-----------------------------------------------------------------------;
407 ; Device "interrupt" entry point ;
408 ;-----------------------------------------------------------------------;
409 IRPT PROC
FAR ;device interrupt entry point
410 PUSH DS ;save all registers Revised
418 ;BP isn't used, so it isn't saved
419 CLD ;all moves forward
421 LDS BX,CS:RH_PTRA
;get RH address passed to "strategy" into DS:BX
423 MOV AL,RH
.RHC_CMD
;command code from Request Header
424 CBW ;zero AH (if AL > 7FH, next compare will
427 CMP AL,MAX_CMD
;if command code is too high
428 JA IRPT_CMD_HIGH
;jump to error routine
430 MOV DI,OFFSET IRPT_CMD_EXIT
;return addr from command processor
431 PUSH DI ;push return address onto stack
432 ;command routine issues "RET"
434 ADD AX,AX ;double command code for table offset
435 MOV DI,AX ;put into index register for JMP
437 XOR AX,AX ;initialize return to "no error"
439 ;At entry to command processing routine:
441 ; DS:BX = Request Header address
442 ; CS = VDISK code segment address
445 ; top of stack is return address, IRPT_CMD_EXIT
447 JMP CS:CMD_TABLE
[DI] ;call routine to handle the command
450 IRPT_CMD_ERROR: ;CALLed for unsupported character mode commands
452 INPUT_IOCTL: ;IOCTL input
453 INPUT_NOWAIT: ;Non-destructive input no wait
454 INPUT_STATUS: ;Input status
455 INPUT_FLUSH: ;Input flush
457 OUTPUT_IOCTL: ;IOCTL output
458 OUTPUT_STATUS: ;Output status
459 OUTPUT_FLUSH: ;Output flush
461 POP AX ;pop return address off stack
463 IRPT_CMD_HIGH: ;JMPed to if RHC_CMD > MAX_CMD
464 MOV AX,STAT_CMDERR
;"invalid command" and error
466 IRPT_CMD_EXIT: ;return from command routine
467 ;AX = value to OR into status word
468 LDS BX,CS:RH_PTRA
;restore DS:BX as Request Header pointer
469 OR AH,STAT_DONE
;add "done" bit to status word
470 MOV RH
.RHC_STA
,AX ;store status into request header
471 POP SI ;restore registers
482 SUBTTL Command Processing routines
484 ;-----------------------------------------------------------------------;
485 ; Command Code 1 - Media Check ;
486 ; At entry, DS:BX point to request header, AX = 0 ;
487 ;-----------------------------------------------------------------------;
489 MOV RH
.RH1_RET
,1 ;indicate media not changed
490 RET ;AX = zero, no error
492 ;-----------------------------------------------------------------------;
493 ; Command Code 2 - Build BPB ;
494 ; At entry, DS:BX point to request header, AX = 0 ;
495 ;-----------------------------------------------------------------------;
497 MOV RH
.RH2_BPBO
,OFFSET BPB
;return pointer to our BPB
499 RET ;AX = zero, no error
501 ;-----------------------------------------------------------------------;
502 ; Command Code 13 - Device Open ;
503 ; Command Code 14 - Device Close ;
504 ; Command Code 15 - Removable media ;
505 ; At entry, DS:BX point to request header, AX = 0 ;
506 ;-----------------------------------------------------------------------;
508 MOV AX,STAT_BUSY
;set status bit 9 (busy)
509 ;indicating non-removable media
510 DEVICE_OPEN: ;NOP for device open
511 DEVICE_CLOSE: ;NOP for device close
513 REMOVABLE_MEDIA ENDP
;fall thru to return
514 ;-----------------------------------------------------------------------;
515 ; Command Code 4 - Input ;
516 ; Command Code 8 - Output ;
517 ; Command Code 9 - Output with verify ;
518 ; At entry, DS:BX point to request header, AX = 0 ;
519 ;-----------------------------------------------------------------------;
525 ;Make sure I/O is entirely within the VDISK sector boundaries
527 MOV CX,CS:BPB_SECN
;get total sector count
528 MOV AX,RH
.RH4_SSN
;starting sector number
529 CMP AX,CX ;can't exceed total count
530 JA INOUT_E1
;jump if start > total
532 ADD AX,RH
.RH4_CNT
;start + sector count
533 CMP AX,CX ;can't exceed total count
534 JNA INOUT_A
;jump if start + count <= total
536 INOUT_E1: ;I/O not within VDISK sector boundaries
537 MOV RH
.RH4_CNT
,0 ;set sectors transferred to zero
538 MOV AX,STAT_SNF
;indicate 'Sector not found' error
539 RET ;return with error status in AX
541 INOUT_A: ;I/O within VDISK bounds
542 MOV AX,RH
.RH4_CNT
;get sector count
543 MOV CS:SECT_LEFT
,AX ;save as sectors left to process
545 CMP CS:EM_SW
,0 ;extended memory mode?
546 JNE INOUT_EM
;jump to extended memory I/O code
548 ;Compute offset and segment of VDISK buffer for starting segment in CX:SI
550 MOV AX,RH
.RH4_SSN
;starting sector number
551 MUL CS:PARAS_PER_SECTOR
;* length of one sector in paragraphs
552 ADD AX,CS:START_BUFFER_PARA
;+ segment of VDISK buffer sector 0
553 MOV CX,AX ;segment address to CX
554 XOR SI,SI ;offset is zero
556 ;Compute address of caller's Data Transfer Addr in DX:AX with smallest offset,
557 ;so that there is no possibility of overflowing a 64KB boundary moving MAX_CNT
561 MUL RH
.RH4_DTAS
;* segment of caller's DTA in DX,AX
562 ADD AX,RH
.RH4_DTAO
;+ offset of caller's DTA
563 ADC DL,0 ;carry in from addition
564 DIV CS:WPARA_SIZE
;AX is segment of caller's DTA
565 ;DX is smallest offset possible
568 ;AX:DX is caller's DTA segment:offset, CX:SI is VDISK buffer segment:offset
570 ;If this is an OUTPUT request, exchange the source and target addresses
572 CMP RH
.RHC_CMD
,CMD_INPUT
;INPUT operation?
573 JE INOUT_B
;jump if INPUT operation
575 XCHG AX,CX ;swap source and target segment
576 XCHG DX,SI ;swap source and target offset
578 INOUT_B: ;CX:SI is source, AX:DX is target
579 MOV CS:IO_SRCS
,CX ;save source segment
580 MOV CS:IO_SRCO
,SI ;save source offset
581 MOV CS:IO_TGTS
,AX ;save target segment
582 MOV CS:IO_TGTO
,DX ;save target offset
584 JMP SHORT INOUT_E
;AX := SECT_LEFT, test for zero
585 INOUT_C: ;SECT_LEFT in AX, non-zero
587 ; Compute number of sectors to transfer in a single move,
588 ; AX = minimum of (SECT_LEFT, MAX_CNT)
590 ; MAX_CNT is the maximum number of sectors that can be moved without
591 ; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
593 MOV CX,CS:MAX_CNT
;MAX sectors with one move
594 CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
595 JBE INOUT_D
;then move SECT_LEFT sectors
597 MOV AX,CX ;else move MAX_CNT sectors
599 SUB CS:SECT_LEFT
,AX ;reduce number of sectors left to move
601 ;Move AX sectors from source to target
603 MUL CS:BPB_SSZ
;sectors * sector size = byte count
604 ;(cannot overflow into DX)
605 SHR AX,1 ;/2 = word count
606 MOV CX,AX ;word count to CX for REP MOVSW
608 LDS SI,CS:IO_SRCA
;source segment/offset to DS:SI
609 LES DI,CS:IO_TGTA
;target segment/offset to ES:DI
611 REP MOVSW ;move MOV_CNT sectors
613 ;Update source and target paragraph addresses
614 ;AX has number of words moved
616 SHR AX,1 ;words moved / 8 = paragraphs moved
620 ADD CS:IO_SRCS
,AX ;add paragraphs moved to source segment
621 ADD CS:IO_TGTS
,AX ;add paragraphs moved to target segment
623 ;Determine if more sectors need to be transferred
625 INOUT_E: ;do while SECT_LEFT <> zero
626 MOV AX,CS:SECT_LEFT
;get sectors left to transfer
628 JNZ INOUT_C
;go back to transfer some sectors
629 RET ;AX = zero, all sectors transferred
631 SUBTTL Extended Memory I
/O routine
633 ;-----------------------------------------------------------------------;
634 ; Extended Memory I/O routine ;
635 ;-----------------------------------------------------------------------;
636 INOUT_EM: ;Extended memory I/O routine
637 ;change to larger stack
638 MOV SI,SS ;save old SS in SI
639 MOV DX,SP ;save old SP in DX
640 CLI ;disable interrupts
642 MOV SS,AX ;set SS = CS
643 MOV SP,OFFSET EM_STACK
;point to new stack
644 STI ;enable interrupts
645 PUSH SI ;save old SS at top of new stack
646 PUSH DX ;save old SP on new stack
648 MOV SI,RH
.RH4_DTAO
;caller's DTA offset
650 ;Compute 24-bit address of VDISK sector in CX (hi) and SI (low)
652 MOV AX,RH
.RH4_SSN
;starting sector number
653 MUL CS:BPB_SSZ
;* sector size = offset within buffer
654 ADD AX,CS:START_EM_LO
;+ base address of this VDISK buffer
655 ADC DL,CS:START_EM_HI
656 MOV CX,DX ;save high byte
657 MOV SI,AX ;save low word
659 ;Compute 24-bit address of caller's DTA in DX (hi) and AX (low)
662 MUL RH
.RH4_DTAS
;* segment of caller's DTA
663 ADD AX,RH
.RH4_DTAO
;+ offset of caller's DTA
664 ADC DL,0 ;carry in from addition
666 ;Caller's DTA address is in CX,SI, VDISK buffer address is in DX,AX.
668 ;If this is an OUTPUT request, exchange the source and target addresses
670 CMP RH
.RHC_CMD
,CMD_INPUT
;INPUT operation?
671 JE INOUT_EM_B
;jump if INPUT operation
673 XCHG DX,CX ;swap source and target high byte
674 XCHG AX,SI ;swap source and target low word
676 INOUT_EM_B: ;CX,SI is source, DX,AX is target
678 MOV SRC
.DESC_BASEL
,SI ;low 16 bits of source address
679 MOV SRC
.DESC_BASEH
,CL ;high 8 bits of source address
681 MOV TGT
.DESC_BASEL
,AX ;low 16 bits of target address
682 MOV TGT
.DESC_BASEH
,DL ;high 8 bits of target address
684 JMP SHORT INOUT_EM_E
;AX := SECT_LEFT, test for zero
685 INOUT_EM_C: ;SECT_LEFT in AX, non-zero
687 ; Compute number of sectors to transfer in a single move,
688 ; AX = minimum of (SECT_LEFT, MAX_CNT)
690 ; MAX_CNT is the maximum number of sectors that can be moved without
691 ; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
693 MOV CX,CS:MAX_CNT
;MAX sectors with one move
694 CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
695 JBE INOUT_EM_D
;then move SECT_LEFT sectors
697 MOV AX,CX ;else move MAX_CNT sectors
699 SUB CS:SECT_LEFT
,AX ;reduce number of sectors left to move
701 ;Move AX sectors from source to target
703 MUL CS:BPB_SSZ
;sectors * sector size = byte count
704 ;(cannot overflow into DX)
705 MOV TGT
.DESC_LMT
,AX ;store segment limit (byte count)
708 PUSH AX ;preserve byte count on stack
710 SHR AX,1 ;/2 = word count
711 MOV CX,AX ;word count to CX
715 MOV SI,OFFSET GDT
;ES:SI point to GDT
717 MOV AH,EM_BLKMOVE
;function is block move
718 INT EM_INT
;move an even number of words
720 POP CX ;get byte count back from stack
722 OR AH,AH ;get error code
724 JNZ INOUT_EM_XE
;jump if I/O error encountered
726 ;Update source and target addresses
728 ADD SRC
.DESC_BASEL
,CX ;add bytes moved to source
729 ADC SRC
.DESC_BASEH
,0 ;pick up any carry
731 ADD TGT
.DESC_BASEL
,CX ;add bytes moved to target
732 ADC TGT
.DESC_BASEH
,0 ;pick up any carry
734 ;Determine if more sectors need to be transferred
736 INOUT_EM_E: ;do while SECT_LEFT <> zero
737 MOV AX,CS:SECT_LEFT
;get sectors left to transfer
739 JNZ INOUT_EM_C
;go back to transfer some sectors
741 INOUT_EM_X2: ;revert to original stack
744 CLI ;disable interrupts
745 MOV SS,SI ;restore old SS
746 MOV SP,DI ;restore old SP
747 STI ;enable interrupts
748 RET ;return to IRPT_EXIT
750 INOUT_EM_XE: ;some error with INT 15H
751 MOV CS:EM_STAT
,AX ;save error status for debugging
752 MOV RH
.RH4_CNT
,0 ;indicate no sectors transferred
753 MOV AX,STAT_CRC
;indicate CRC error
754 JMP INOUT_EM_X2
;fix stack and exit
757 DW 40 DUP (?
) ;stack for extended memory I/O
762 ;-----------------------------------------------------------------------;
763 ; Adjust the assembly-time instruction counter to a paragraph ;
765 ;-----------------------------------------------------------------------;
768 ORG ($-START
) + 16 - (($-START
) MOD 16)
771 VDISK EQU
$ ;start of virtual disk buffer
772 VDISKP EQU
($-START
) / PARA_SIZE
;length of program in paragraphs
773 ;-----------------------------------------------------------------------;
774 ; If this VDISK is in extended memory, this address is passed ;
775 ; back to DOS as the end address that is to remain resident. ;
777 ; It this VDISK is not in extended memory, the VDISK buffer ;
778 ; begins at this address, and the address passed back to DOS ;
779 ; as the end address that is to remain resident is this address ;
780 ; plus the length of the VDISK buffer. ;
781 ;-----------------------------------------------------------------------;
783 BOOT_RECORD
LABEL BYTE ;Format of Boot Record documented in
784 ;DOS Technical Reference Manual
785 DB 0,0,0 ;3-byte jump to boot code (not bootable)
786 DB 'VDISK ' ;8-byte vendor identification
787 BOOT_BPB
LABEL BYTE ;boot record copy of BIOS parameter block
788 DW ?
;number of bytes per disk sector
789 DB ?
;sectors per allocation unit
790 DW ?
;number of reserved sectors (for boot record)
791 DB ?
;number of File Allocation Table (FAT) copies
792 DW ?
;number of root directory entries
793 DW ?
;total number of sectors
794 DB ?
;media descriptor byte
795 DW ?
;number of sectors occupied by a single FAT
796 ;end of boot record BIOS Parameter block
798 ;The following three words mean nothing to VDISK, they are placed here
799 ;to conform to the DOS standard for boot records.
800 DW 8 ;sectors per track
801 DW 1 ;number of heads
802 DW 0 ;number of hidden sectors
803 ;The following word is the 16-bit kilobyte address of the first byte in
804 ;extended memory that is not occupied by a VDISK buffer
805 ;It is placed into this location so that other users of extended memory
806 ;may find where all the VDISKs end.
808 ;This field may be accessed by moving the boot record of the First extended
809 ;memory VDISK from absolute location 10 0000H. Before assuming that the
810 ;value below is valid, the vendor ID (constant VDISK) should be verified
811 ;to make sure that SOME VDISK has been installed.
813 ;For example, if two VDISKs are installed, one 320KB and one 64KB, the
814 ;address calculations are as follows:
816 ;Extended memory start address = 100000H (1024KB)
817 ;Start addr of 1st VDISK buffer = 100000H (1024KB)
818 ;Length of 1st VDISK buffer = 050000H ( 320KB)
819 ;End addr of 1st VDISK buffer = 14FFFFH
820 ;Start addr of 2nd VDISK buffer = 150000H (1344KB)
821 ;Length of 2nd VDISK buffer = 010000H ( 64KB)
822 ;End addr of 2nd VDISK buffer = 15FFFFH
823 ;First byte after all VDISKs = 160000H (1408KB)
824 ;Divide by 1024 = 0580H (1408D)
826 ;Content of BOOT_EM = 0580H
828 BOOT_EM_OFF EQU
$-BOOT_RECORD
;offset from 10 0000H of the following word
829 BOOT_EM
DW 1024 ;KB addr of first free byte of extended memory
830 ;-----------------------------------------------------------------------;
831 ; Part 2 of Initialization (executed last) ;
832 ;-----------------------------------------------------------------------;
833 ;Initialization is divided into two parts.
835 ;INIT_P1 is overlaid by the virtual disk buffer
837 ;INIT_P1 is executed first, then jumps to INIT_P2. INIT_P2 returns to caller.
839 ;Exercise caution if extending the initialization part 2 code.
840 ;It overlays the area immediately following the boot sector.
841 ;If this section of code must be expanded, make sure it fits into the minimum
842 ;sector size of 128 bytes.
843 ;Label TEST_LENGTH must equate to a non-negative value (TEST_LENGTH >= 0).
844 ;If this code it must be extended beyond the 128 byte length of the boot sector,
845 ;move all of INIT_P2 before label VDISK.
847 ;Registers at entry to INIT_P2 (set up at end of INIT_P1):
848 ; BL = media control byte from BPB (for FAT)
849 ; CX = number of FAT copies
850 ; DX = number of bytes in one FAT - 3
851 ; SI = OFFSET of Volume Label field
852 ; ES:DI = VDISK buffer address of first FAT sector
853 ; CS = DS = VDISK code segment
855 INIT_P2 PROC
;second part of initialization
856 ASSUME
DS:CSEG
;DS set in INIT_P1
858 ;Initialize File Allocation Table(s) (FATs)
860 INIT_P2_FAT: ;set up one FAT, sector number in AX
862 PUSH CX ;save loop counter on stack
863 MOV AL,BL ;media control byte
864 STOSB ;store media control byte, increment DI
865 MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH
868 MOV CX,DX ;FAT size in bytes - 3
869 XOR AX,AX ;value to store in remainder of FAT
870 REP STOSB ;clear remainder of FAT
872 POP CX ;get loop counter off stack
873 LOOP INIT_P2_FAT
;loop for all copies of the FAT
875 ;Put the volume label in the first directory entry
877 MOV CX,VOL_LABEL_LEN
;length of volume directory entry
878 REP MOVSB ;move volume id to directory
880 ;Zero the remainder of the directory
882 MOV AX,DIR_ENTRY_SIZE
;length of 1 directory entry
883 MUL BPB_DIRN
;* number entries = bytes of directory
884 SUB AX,VOL_LABEL_LEN
;less length of volume label
885 MOV CX,AX ;length of rest of directory
887 REP STOSB ;clear directory to nulls
888 RET ;return with AX=0
891 PATCH_AREA
DB 5 DUP ('PATCH AREA ')
892 TEST_LENGTH EQU
128-($-VDISK
) ;if negative, boot record has too much
893 ;data area, move some fields below VDISK
894 ;-----------------------------------------------------------------------;
895 ; All fields that must remain resident after device driver ;
896 ; initialization must be defined before this point. ;
897 ;-----------------------------------------------------------------------;
898 DB 'MS DOS Version 4.00 - Virtual Disk Device Driver'
899 DB '-------- Licensed Material ---------'
900 DB 'Program Property of Microsoft Corporation. '
901 DB '(C)Copyright 1988 Microsoft'
902 DB 'Thank You For Your '
905 MAXSEC_TRF
DW 0 ;maximum number of sectors to transfer when
908 BUFF_SIZE
DW 0 ;desired VDISK buffer size in kilobytes
910 MIN_MEMORY_LEFT
DW 64 ;minimum amount of system memory (kilobytes)
911 ;that must remain after VDISK is installed
913 FIRST_EM_SW
DB ?
;0FFH if this is the first device driver
914 ;to be installed in extended memory
915 ;00H if another VDISK extended memory driver
918 FIRST_VDISK
DW ?
;segment address of 1st VDISK device driver
919 PARA_PER_KB
DW 1024/PARA_SIZE
;paragraphs in one kilobyte
920 C1024
DW 1024 ;bytes in one kilobyte
921 DIRE_SIZE
DW DIR_ENTRY_SIZE
;bytes in one directory entry
922 DIR_SECTORS
DW ?
;number of sectors of directory
924 ERR_FLAG
DB 0 ;error indicators to condition messages
925 ERR_BSIZE EQU 80H
;buffer size adjusted
926 ERR_SSZ EQU 40H
;sector size adjusted
927 ERR_DIRN EQU 20H
;number of directory entries adjusted
928 ERR_PASS EQU 10H
;some adjustment made that requires
929 ;recomputation of values previously computed
930 ERR_SSZB EQU ERR_SSZ
+ERR_PASS
;sector size altered this pass
931 ERR_SYSSZ EQU
08H ;system storage too small for VDISK
932 ERR_SWTCH EQU
04H ;invalid switch character
933 ERR_EXTSW EQU
02H ;extender card switches don't match memory size
934 ERR_ESIZE EQU
01H ;Transfer size adjusted
936 ; additional errors added - kwc
938 major_version equ
4 ;Major DOS version
939 minor_version equ
00 ;Minor DOS Version
941 expected_version equ
(MINOR_VERSION
SHL 8)+MAJOR_VERSION
944 err_baddos equ
01h ; Invalid DOS Version
946 SUBTTL Initialization
, Part one
948 ;-----------------------------------------------------------------------;
949 ; Command Code 0 - Initialization ;
950 ; At entry, DS:BX point to request header, AX = 0 ;
951 ;-----------------------------------------------------------------------;
952 ;Initialization is divided into two parts.
953 ;This part, executed first, is later overlaid by the VDISK buffer.
955 INIT_P1 PROC
;first part of initialization
956 MOV DX,SS ;save stack segment register
957 MOV CX,SP ;save stack pointer register
958 CLI ;inhibit interrupts while changing SS:SP
959 MOV AX,CS ;move CS to SS through AX
961 MOV SP,OFFSET MSGEND
;end of VDISKMSG
962 ADD SP,STACK_SIZE
;+ length of our stack
963 STI ;allow interrupts
964 PUSH DX ;save old SS register on new stack
965 PUSH CX ;save old SP register on new stack
967 push bx ;secure registers before DOS int
968 push cx ;secure registers before DOS int
970 ; add version check - kwc
974 pop cx ;restore pointer values
975 pop bx ;restore pointer values
976 cmp ax,expected_version
979 or cs:err_flag2
,err_baddos
982 CALL GET_PARMS
;get parameters from CONFIG.SYS line
988 CALL APPLY_DEFAULTS
;supply any values not specified
989 CALL DETERMINE_START
;compute start address of VDISK buffer
990 CALL VALIDATE
;validate parameters
991 CALL COPY_BPB
;Copy BIOS Parameter Block to boot record
993 CALL VERIFY_EXTENDER
;Verify that extender card switches are right
995 TEST ERR_FLAG
,ERR_EXTSW
;are switches wrong?
996 JNZ INIT_P1_A
;if so, exit with messages
998 test CS:err_flag2
,err_baddos
1001 CMP EM_SW
,0 ;extended memory requested?
1002 JE INIT_P1_A
;jump if not
1004 TEST ERR_FLAG
,ERR_SYSSZ
;is system too small for VDISK?
1005 JNZ INIT_P1_A
;if so, don't do extended memory init
1007 CALL UPDATE_AVAIL
;update AVAIL_HI and AVAIL_LO to reflect
1008 ;addition of extended memory VDISK
1009 CALL FORMAT_VDISK
;construct a boot record, FATs and
1010 ;directory in storage immediately
1011 ;following this device driver
1012 CALL MOVE_VDISK
;move formatted boot record, FATs,
1013 ;and directory to extended memory
1014 CALL UPDATE_BOOT
;place the end address of ALL VDISKs
1015 ;in the boot record of the first VDISK
1016 CMP FIRST_EM_SW
,0 ;is this the first extended memory VDISK?
1017 JE INIT_P1_A
;no, exit
1019 CALL STEAL_INT19
;point INT 19H to this VDISK
1021 CALL FILL_RH
;fill in INIT request header
1022 CALL WRITE_MESSAGES
;display all messages
1023 POP CX ;get old SP from stack
1024 POP DX ;get old SS from stack
1025 CLI ;disable interrupts while changing SS:SP
1026 MOV SS,DX ;restore stack segment register
1027 MOV SP,CX ;restore stack pointer register
1028 STI ;enable interrupts
1029 ;-----------------------------------------------------------------------;
1030 ; INIT_P2 must be short enough to fit into the boot sector ;
1031 ; (minimum size of boot sector is 128 bytes), so we set up ;
1032 ; as many pointers as we can to help keep INIT_P2 short. ;
1034 ; ES:DI = storage address of first FAT sector ;
1035 ; BL = media control byte ;
1036 ; CX = number of FAT copies ;
1037 ; DX = number of bytes in one FAT, less 3 ;
1038 ; SI = offset of VOL label field ;
1039 ;-----------------------------------------------------------------------;
1040 MOV ES,START_BUFFER_PARA
;start paragraph of VDISK buffer
1042 MOV AX,BPB_RES
;number of reserved sectors
1043 MUL BPB_SSZ
;* sector size
1044 MOV DI,AX ;ES:DI point to FAT start
1046 MOV BL,BPB_MCB
;media control byte
1048 MOV CL,BPB_FATN
;number of FAT copies
1051 MOV AX,BPB_FATSZ
;FAT size in sectors
1052 MUL BPB_SSZ
;* sector size = total FAT bytes
1054 SUB AX,3 ;-3 (FEFFFF stored by code)
1057 MOV SI,OFFSET VOL_LABEL
;point to VOL label directory entry
1058 JMP INIT_P2
;jump to second part of initialization
1059 ;this is redundant if the VDISK is in
1060 ;extended memory, but is executed anyway
1062 SUBTTL GET_PARMS Parameter Line Scan
1064 ;-----------------------------------------------------------------------;
1065 ;GET_PARMS gets the parameters from the CONFIG.SYS statement ;
1068 ; DS:SI indexes parameter string ;
1069 ; AL contains character from parameter string ;
1070 ; CX value from GET_NUMBER ;
1071 ;-----------------------------------------------------------------------;
1072 ASSUME
DS:NOTHING
;DS:BX point to Request Header
1073 GET_PARMS PROC
;get parameters from CONFIG.SYS line
1075 LDS SI,RH
.RH0_BPBA
;DS:SI point to all after DEVICE=
1077 XOR AL,AL ;not at end of line
1079 ;Skip until first delimiter is found. There may be digits in the path string.
1081 ;DS:SI points to \pathstring\VDISK.SYS nn nn nn
1082 ;The character following VDISK.SYS may have been changed to a null (00H).
1083 ;All letters have been changed to uppercase.
1085 GET_PARMS_A: ;skip to DOS delimiter character
1086 CALL GET_PCHAR
;get parameter character into AL
1087 JZ GET_PARMS_X
;get out if end of line encountered
1088 OR AL,AL ;test for null
1101 JNE GET_PARMS_A
;skip until delimiter or CR
1106 PUSH SI ;save to rescan
1107 MOV CS:EM_SW
,0 ;indicate no /E found
1108 JMP GET_SLASH
;see if current character is an slash
1110 GET_PARMS_D: ;scan for /
1112 JZ GET_PARMS_B
;exit if end of line
1114 GET_SLASH: ;check for slash
1115 CMP AL,'/' ;found slash?
1116 JNE GET_PARMS_D
;no, continue scan
1118 CALL GET_PCHAR
;get char following slash
1119 CMP AL,'E' ;don't have to test for lower case E,
1120 ;letters have been changed to upper case
1121 JNE GET_PARMS_E
;not 'E'
1122 MOV CS:EM_SW
,AL ;indicate /E found
1124 CALL GET_PCHAR
;get char following E
1125 CMP AL,':' ;is it a delimeter ?
1126 JNE GET_PARMS_D
;not a ':'
1129 CALL GET_MAXSIZE
;get maximum sector size
1132 JMP GET_PARMS_D
;continue forward scan
1134 GET_PARMS_E: ;/ found, not 'E'
1135 OR CS:ERR_FLAG
,ERR_SWTCH
;indicate invalid switch character
1136 JMP GET_PARMS_D
;continue scan
1140 GET_PARMS_B: ;now pointing to first delimiter
1141 POP SI ;get pointer, used to rescan for /E
1142 XOR AL,AL ;not at EOL now
1143 CALL GET_PCHAR
;get first character
1144 CALL SKIP_TO_DIGIT
;skip to first digit
1145 JZ GET_PARMS_X
;found EOL, no digits remain
1147 CALL GET_NUMBER
;extract digits, convert to binary
1148 MOV CS:BUFF_SIZE
,CX ;store buffer size
1150 CALL SKIP_TO_DIGIT
;skip to next digit
1151 JZ GET_PARMS_X
;found EOL, no digits remain
1153 CALL GET_NUMBER
;extract digits, convert to binary
1154 MOV CS:BPB_SSZ
,CX ;store sector size
1156 CALL SKIP_TO_DIGIT
;skip to next digit
1157 JZ GET_PARMS_X
;found EOL, no digits remain
1159 CALL GET_NUMBER
;extract digits, convert to binary
1160 MOV CS:BPB_DIRN
,CX ;store number of directory entries
1164 GET_PARMS_X: ;premature end of line
1170 GET_MAXSIZE PROC
;get maximum sector size
1172 CALL GET_PCHAR
;get next character
1173 CALL CHECK_NUM
;is it a number ?
1174 JZ GET_NEXTNUM
;yes, go get next number
1175 OR CS:ERR_FLAG
,ERR_ESIZE
;indicate invalid sector size
1177 GET_NEXTNUM: ;get next number
1178 CALL GET_NUMBER
;extract digits and convert to binary
1179 MOV CS:MAXSEC_TRF
,CX ;save maximum sector size to transfer
1185 GET_PCHAR PROC
;internal proc to get next character into AL
1186 CMP AL,CR
;carriage return already encountered?
1187 JE GET_PCHAR_X
;don't read past end of line
1188 CMP AL,LF
;line feed already encountered?
1189 JE GET_PCHAR_X
;don't read past end of line
1190 LODSB ;get char from DS:SI, increment SI
1191 CMP AL,CR
;is the char a carriage return?
1192 JE GET_PCHAR_X
;yes, set Z flag at end of line
1193 CMP AL,LF
;no, is it a line feed?
1194 GET_PCHAR_X: ;attempted read past end of line
1196 GET_PCHAR ENDP
;returns char in AL
1199 CHECK_NUM PROC
;check AL for ASCII digit
1201 JB CHECK_NUM_X
;exit if it is
1204 JA CHECK_NUM_X
;exit if it is
1206 CMP AL,AL ;set Z flag to indicate numeric
1208 RET ;Z set if numeric, NZ if not numeric
1212 SKIP_TO_DIGIT PROC
;skip to first numeric character
1213 CALL CHECK_NUM
;is current char a digit?
1214 JZ SKIP_TO_DIGIT_X
;if so, skip is complete
1216 CALL GET_PCHAR
;get next character from line
1217 JNZ SKIP_TO_DIGIT
;loop until first digit or CR or LF
1218 RET ;character is CR or LF
1221 CMP AL,0 ;digit found, force NZ
1226 GN_ERR
DB ?
;zero if no overflow in accumulation
1228 GET_NUMBER PROC
;convert string of digits to binary value
1229 XOR CX,CX ;accumulate number in CX
1230 MOV CS:GN_ERR
,CL ;no overflow yet
1231 GET_NUMBER_A: ;accumulate next digit
1232 SUB AL,'0' ;convert ASCII to binary
1234 XCHG AX,CX ;previous accumulation in AX, new digit in CL
1235 MUL CS:C10
;DX:AX := AX*10
1236 OR CS:GN_ERR
,DL ;set GN_ERR <> 0 if overflow
1237 ADD AX,CX ;add new digit from
1238 XCHG AX,CX ;number now in CX
1239 DEC SI ;back up to prior entry
1240 MOV AL,' ' ;blank out prior entry
1242 INC SI ;set to current entry
1243 CALL GET_PCHAR
;get next character
1244 CALL CHECK_NUM
;see if it was numeric
1245 JZ GET_NUMBER_A
;continue accumulating
1246 CMP CS:GN_ERR
,0 ;did we overflow?
1247 JE GET_NUMBER_B
;if not, we're done
1248 XOR CX,CX ;return zero (always invalid) if overflow
1250 RET ;number in CX, next char in AL
1255 SUBTTL APPLY_DEFAULTS
1257 ;-----------------------------------------------------------------------;
1258 ; APPLY_DEFAULTS supplies any parameter values that the user ;
1259 ; failed to specify ;
1260 ;-----------------------------------------------------------------------;
1264 CMP BUFF_SIZE
,AX ;is buffer size zero?
1265 JNE APPLY_DEFAULTS_A
;no, user specified something
1267 MOV BUFF_SIZE
,DFLT_BSIZE
;supply default buffer size
1268 OR ERR_FLAG
,ERR_BSIZE
;indicate buffersize adjusted
1271 CMP BPB_SSZ
,AX ;is sector size zero?
1272 JNE APPLY_DEFAULTS_B
;no, user specified something
1274 MOV BPB_SSZ
,DFLT_SSZ
;supply default sector size
1275 OR ERR_FLAG
,ERR_SSZ
;indicate sector size adjusted
1278 CMP BPB_DIRN
,AX ;are directory entries zero?
1279 JNE APPLY_DEFAULTS_C
;no, user specified something
1281 MOV BPB_DIRN
,DFLT_DIRN
;supply default directory entries
1282 OR ERR_FLAG
,ERR_DIRN
;indicate directory entries adjusted
1285 CMP EM_SW
,0 ;extended memory ?
1286 JE APPLY_DEFAULTS_D
;no, jump around
1287 CMP MAXSEC_TRF
,AX ;is maximum sectors zero?
1288 JNE APPLY_DEFAULTS_D
;no, user specified something
1290 MOV MAXSEC_TRF
,DFLT_ESS
;supply default maximum number of
1292 OR ERR_FLAG
,ERR_ESIZE
;indicate transfer size adjusted
1297 SUBTTL DETERMINE_START address of VDISK buffer
1299 ;-----------------------------------------------------------------------;
1300 ; DETERMINE_START figures out the starting address of the VDISK ;
1302 ;-----------------------------------------------------------------------;
1304 DETERMINE_START PROC
1306 ;If extended memory is NOT being used, the VDISK buffer immediately
1307 ;follows the resident code.
1309 ;If extended memory IS being used, START_BUFFER_PARA becomes the
1310 ;end of device driver address passed back to DOS.
1312 MOV AX,CS ;start para of VDISK code
1313 ADD AX,VDISKP
;+ length of resident code
1314 MOV START_BUFFER_PARA
,AX ;save as buffer start para
1316 CMP EM_SW
,0 ;is extended memory requested?
1317 JE DETERMINE_START_X
;if not, we're done here
1319 ;If this is the first extended memory VDISK device driver to be installed,
1320 ;the start address for I/O is 1 megabyte.
1322 ;If one or more extended memory VDISK device drivers have been installed,
1323 ;the start address for I/O for THIS device driver is acquired from the
1324 ;fields AVAIL_LO and AVAIL_HI in the FIRST VDISK device driver.
1326 ;The first extended memory VDISK device driver is located by INT 19H's vector.
1328 MOV FIRST_EM_SW
,0FFH ;indicate first VDISK device driver
1329 MOV FIRST_VDISK
,CS ;segment addr of first VDISK
1331 PUSH DS ;preserve DS
1333 MOV DS,AX ;set DS = 0
1336 MOV AX,DS:BOOT_VECS
;get segment addr of INT 19H routine
1342 MOV SI,OFFSET VOL_LABEL
;DS:SI point to VOL label field
1343 ;in first VDISK (if present)
1344 MOV DI,SI ;ES:DI point to VOL label field of
1347 MOV CX,VOL_LABEL_LEN
;length of volume label
1348 REP CMPSB ;does INT 19H vector point to a VDISK
1350 JNE DETERMINE_START_A
;jump if this is the first VDISK
1352 ;Another extended memory VDISK device driver has been installed.
1353 ;Its AVAIL_LO and AVAIL_HI are the first free byte of extended memory.
1355 MOV CS:FIRST_EM_SW
,0 ;indicate not first device driver
1356 MOV CS:FIRST_VDISK
,DS ;save pointer to 1st device driver
1358 ;Copy AVAIL_LO and AVAIL_HI from first VDISK to this VDISK
1360 MOV SI,OFFSET AVAIL_LO
;DS:SI point to AVAIL_LO in first VDISK
1361 MOV DI,SI ;ES:DI point to AVAIL_LO in this VDISK
1362 MOVSW ;copy AVAIL_LO from first to this VDISK
1363 MOVSB ;copy AVAIL_HI
1365 DETERMINE_START_A: ;copy AVAIL_LO and AVAIL_HI to START_EM
1368 MOV SI,OFFSET AVAIL_LO
;source offset
1369 MOV DI,OFFSET START_EM_LO
;destination offset
1371 MOVSW ;move AVAIL_LO to START_EM_LO
1372 MOVSB ;move AVAIL_HI to START_EM_HI
1375 DETERMINE_START ENDP
1377 SUBTTL VALIDATE parameters
1379 ;-----------------------------------------------------------------------;
1380 ; VALIDATE adjusts parameters as necessary ;
1381 ;-----------------------------------------------------------------------;
1382 VAL_SSZ_TBL
LABEL WORD ;table of valid sector sizes
1383 VAL_SSZ_S
DW 128 ;smallest valid sector size
1385 VAL_SSZ_L
DW 512 ;largest valid sector size
1386 VAL_SSZ_N EQU
($-VAL_SSZ_TBL
)/2 ;number of table entries
1389 VALIDATE PROC
;validate parameters
1390 MOV BPB_AUSZ
,1 ;initial allocation unit is 1 sector
1392 CALL VAL_BSIZE
;validate buffer size
1394 CALL VAL_SSZ
;validate (adjust if necessary) BPB_SSZ
1397 AND ERR_FLAG
,255-ERR_PASS
;indicate nothing changed this pass
1399 MOV AX,BPB_SSZ
;sector size
1400 CWD ;clear DX for division
1401 DIV WPARA_SIZE
;sector size/para size
1402 MOV PARAS_PER_SECTOR
,AX ;number of paragraphs/sector
1404 MOV AX,BUFF_SIZE
;requested buffersize in KB
1405 MUL C1024
;DX:AX = buffer size in bytes
1406 DIV BPB_SSZ
;/sector size = # sectors
1407 MOV BPB_SECN
,AX ;store number of sectors
1409 CALL VAL_DIRN
;validate number of directory entries
1411 TEST ERR_FLAG
,ERR_PASS
;may have reset sector size
1412 JNZ VALIDATE_A
;recompute directory & FAT sizes
1414 CALL VAL_FAT
;compute FAT entries, validity test
1416 TEST ERR_FLAG
,ERR_PASS
;if cluster size altered this pass
1417 JNZ VALIDATE_A
;recompute directory & FAT sizes
1419 ;Make certain buffer size is large enough to contain:
1422 ; directory sector(s)
1423 ; at least 1 data cluster
1425 MOV AL,BPB_FATN
;number of FAT copies
1427 MUL BPB_FATSZ
;* sectors for 1 FAT = FAT sectors
1428 ADD AX,BPB_RES
;+ reserved sectors
1429 ADD AX,DIR_SECTORS
;+ directory sectors
1430 MOV CL,BPB_AUSZ
;get sectors/cluster
1431 XOR CH,CH ;CX = sectors in one cluster
1432 ADD AX,CX ;+ one data cluster
1433 CMP BPB_SECN
,AX ;compare with sectors available
1434 JAE VALIDATE_X
;jump if enough sectors
1436 CMP DIR_SECTORS
,1 ;down to 1 directory sector?
1437 JBE VALIDATE_C
;can't let it go below 1
1439 MOV AX,BPB_SSZ
;sector size
1440 CWD ;clear DX for division
1441 DIV DIRE_SIZE
;sectorsize/dir entry size = entries/sector
1442 SUB BPB_DIRN
,AX ;reduce directory entries by 1 sector
1444 OR ERR_FLAG
,ERR_DIRN
;indicate directory entries adjusted
1445 JMP VALIDATE_A
;retry with new directory entries number
1447 VALIDATE_C: ;not enough space for any VDISK
1448 OR ERR_FLAG
,ERR_SYSSZ
1452 SUBTTL VAL_BSIZE Validate buffer size
1454 ;-----------------------------------------------------------------------;
1455 ; VAL_BSIZE adjusts the buffer size as necessary ;
1456 ;-----------------------------------------------------------------------;
1458 CALL GET_MSIZE
;determine memory available to VDISK
1459 ;returns available KB in AX
1460 OR AX,AX ;is any memory available at all?
1461 JNZ VAL_BSIZE_B
;yes, continue
1463 OR ERR_FLAG
,ERR_SYSSZ
;indicate system too small for VDISK
1464 MOV BUFF_SIZE
,1 ;set up minimal values to continue init
1465 MOV AX,VAL_SSZ_S
;smallest possible sector size
1467 MOV BPB_DIRN
,4 ;4 directory entries
1470 VAL_BSIZE_B: ;some memory is available
1471 CMP AX,BUFF_SIZE
;is available memory >= requested?
1472 JAE VAL_BSIZE_C
;if so, we're done
1474 MOV BUFF_SIZE
,AX ;give all available memory
1475 OR ERR_FLAG
,ERR_BSIZE
;indicate buffersize adjusted
1480 GET_MSIZE PROC
;determine memory available to VDISK
1481 ;returns KB available in AX
1482 CMP EM_SW
,0 ;extended memory?
1483 JE GET_MSIZE_2
;use non-extended memory routine
1485 MOV AH,EM_MEMSIZE
;function code to AH
1486 INT EM_INT
;get extended memory size in AX
1487 JC GET_MSIZE_Z
;if error, no extended memory installed
1489 MUL C1024
;DX,AX = bytes of extended memory
1490 ADD DX,10H
;DX,AX = high addr of extended memory+1
1491 SUB AX,AVAIL_LO
;- address of first available byte
1492 SBB DL,AVAIL_HI
;is number of free bytes
1493 DIV C1024
;AX = number of whole free kilobytes
1496 GET_MSIZE_2: ;non-extended memory size determination
1498 ;Compute AX = total system size, - (VDISK end address + 64KB)
1500 MOV AX,START_BUFFER_PARA
;paragraph end of VDISK code
1501 XOR DX,DX ;clear for division
1502 DIV PARA_PER_KB
;KB address of load point
1503 ADD DX,0FFFFH ;round upward to KB boundary
1504 ADC AX,MIN_MEMORY_LEFT
;pick up CY and the 64KB we should leave
1505 PUSH AX ;save across interrupt
1506 INT MEM_SIZE
;get total system size
1507 POP DX ;amount of total that we can't use
1508 SUB AX,DX ;available space to VDISK
1509 JNC GET_MSIZE_X
;exit if positive
1512 XOR AX,AX ;indicate no memory available
1513 GET_MSIZE_X: ;exit from memory size determination
1519 SUBTTL VAL_SSZ Validate Sector Size
1521 ;-----------------------------------------------------------------------;
1522 ; VAL_SSZ validates sector size, adjusting if necessary ;
1523 ;-----------------------------------------------------------------------;
1524 VAL_SSZ PROC
;validate sector size
1525 CMP CS:EM_SW
,0 ;extended memory ?
1526 JE VAL_SSZ_ST
;no,go check sector size
1527 MOV BX,MAXSEC_TRF
;move number of sectors to transfer
1528 CMP BX,1 ;> or equal to 1 ?
1529 JB DFLT_TRF
;set default if it is
1530 CMP BX,8 ;> than 8 ?
1531 JA DFLT_TRF
;set default if it is
1532 JMP VAL_SSZ_ST
;continue processing
1534 DFLT_TRF: ;set default
1535 MOV MAXSEC_TRF
,DFLT_ESS
;
1537 OR CS:ERR_FLAG
,ERR_ESIZE
;indicate transfer size adjusted
1539 VAL_SSZ_ST: ;validate sector size
1540 MOV MAX_CNT
,BX ;initialize maximum number of sectors
1541 ;to transfer for extended memory case
1542 MOV BX,BPB_SSZ
;requested sector size
1543 MOV CX,VAL_SSZ_N
;number of table entries
1544 MOV SI,OFFSET VAL_SSZ_TBL
;DS:SI point to table start
1546 LODSW ;get table entry, step table pointer
1547 CMP AX,BX ;is value in table?
1548 JE VAL_SSZ_X
;exit if value found
1549 LOOP VAL_SSZ_A
;loop until table end
1551 MOV BX,DFLT_SSZ
;get default sector size
1552 MOV BPB_SSZ
,BX ;set sector size to default value
1553 OR ERR_FLAG
,ERR_SSZ
;indicate sector size adjusted
1556 ;Compute the maximum number of sectors that can be moved in 64KB (less one)
1557 ;Restricting moves to this amount avoids 64KB boundary problems.
1559 CMP CS:EM_SW
,0 ;extended memory ?
1560 JNE SIZE_DONE
;yes, we are done
1562 MOV AX,0FFFFH ;64KB - 1
1563 DIV BX ;/sector size
1564 MOV MAX_CNT
,AX ;max sectors in one move
1569 SUBTTL VAL_DIRN Validate number of directory entries
1571 ;-----------------------------------------------------------------------;
1572 ; VAL_DIRN validates and adjusts the number of directory entries. ;
1574 ; Minimum is MIN_DIRN, maximum is MAX_DIRN. If outside these ;
1575 ; limits, DFLT_DIRN is used. ;
1577 ; The number of directory entries is rounded upward to fill ;
1579 ;-----------------------------------------------------------------------;
1581 MOV AX,BPB_DIRN
;requested directory entries
1582 CMP AX,MIN_DIRN
;if less than minimum
1583 JB VAL_DIRN_A
;use default instead
1585 CMP AX,MAX_DIRN
;if <= maximum
1586 JBE VAL_DIRN_B
;accept value as provided
1589 MOV AX,DFLT_DIRN
;use default directory entries
1590 OR ERR_FLAG
,ERR_DIRN
;indicate directory entries adjusted
1591 VAL_DIRN_B: ;AX is number of directory entries
1592 MUL DIRE_SIZE
;* 32 = bytes of directory requested
1593 DIV BPB_SSZ
;/ sector size = # of directory sectors
1594 OR DX,DX ;test remainder for zero
1595 JZ VAL_DIRN_C
;jump if exact fit
1597 INC AX ;increment directory sectors
1598 OR ERR_FLAG
,ERR_DIRN
;indicate directory entries adjusted
1599 VAL_DIRN_C: ;make sure enough sectors available
1600 MOV DX,BPB_SECN
;total sectors on media
1601 SUB DX,BPB_RES
;less reserved sectors
1602 SUB DX,2 ;less minimum FAT and 1 data sector
1603 CMP AX,DX ;if directory sectors <= available
1604 JLE VAL_DIRN_D
;use requested amount
1606 MOV AX,1 ;use only one directory sector
1607 OR ERR_FLAG
,ERR_DIRN
;indicate directory entries adjusted
1609 MOV DIR_SECTORS
,AX ;save number of directory sectors
1610 MUL BPB_SSZ
;dir sectors * sector size = dir bytes
1611 DIV DIRE_SIZE
;dir bytes / entry size = entries
1612 MOV BPB_DIRN
,AX ;store adjusted directory entries
1616 SUBTTL VAL_FAT Validate
File Allocation Table
(FAT
)
1618 ;-----------------------------------------------------------------------;
1619 ;VAL_FAT computes: ;
1620 ;BPB_FATSZ, the number of sectors required per FAT copy ;
1622 ;Each FAT entry is 12 bits long, for a maximum of 4095 FAT entries. ;
1623 ;(A few FAT entries are reserved, so the highest number of FAT entries ;
1624 ;we permit is 0FE0H.) With large buffer sizes and small sector sizes, ;
1625 ;we have more allocation units to describe than a 12-bit entry will ;
1626 ;describe. If the number of FAT entries is too large, the sector size ;
1627 ;is increased (up to a maximum of 512 bytes), and then the allocation ;
1628 ;unit (cluster) size is doubled, until we have few enough allocation ;
1629 ;units to be properly described in 12 bits. ;
1631 ;This computation is slightly conservative in that the FAT entries ;
1632 ;necessary to describe the FAT sectors are included in the computation. ;
1633 ;-----------------------------------------------------------------------;
1635 MOV AX,BPB_SECN
;total number of sectors
1636 SUB AX,BPB_RES
;don't count boot sector(s)
1637 SUB AX,DIR_SECTORS
;don't count directory sectors
1638 JG VAL_FAT_A
;jump if some remaining
1639 MOV BPB_SSZ
,DFLT_SSZ
;force default sector size
1640 OR ERR_FLAG
,ERR_SSZ
+ERR_PASS
;indicate sector size adjusted
1641 JMP SHORT VAL_FAT_X
;recompute all values
1643 XOR DX,DX ;clear DX for division
1644 MOV CL,BPB_AUSZ
;CX = sectors/cluster
1646 DIV CX ;whole number of clusters in AX
1647 ADD DX,0FFFFH ;set carry if remainder
1648 ADC AX,0 ;increment AX if remainder
1649 CMP AX,MAX_FATE
;number of FAT entries too large?
1650 JBE VAL_FAT_C
;no, continue
1652 MOV AX,BPB_SSZ
;pick up current sector size
1653 CMP AX,VAL_SSZ_L
;already at largest permitted?
1654 JE VAL_FAT_B
;yes, can't make it any larger
1656 SHL BPB_SSZ
,1 ;double sector size
1657 OR ERR_FLAG
,ERR_SSZB
;indicate sector size adjusted
1658 JMP SHORT VAL_FAT_X
;recompute all sizes with new BPBSSZ
1660 VAL_FAT_B: ;sector size is at maximum
1661 SHL BPB_AUSZ
,1 ;double allocation unit size
1662 OR ERR_FLAG
,ERR_PASS
;indicate another pass required
1663 JMP SHORT VAL_FAT_X
;recompute values
1665 VAL_FAT_C: ;FAT size = 1.5 * number of clusters
1666 MOV CX,AX ;number of clusters
1670 ADC AX,3 ;add 3 bytes for first 2 FAT entries
1671 ;(media descriptor and FFFFH), and CY
1672 XOR DX,DX ;clear DX for division
1673 DIV BPB_SSZ
;FAT size/sector size
1674 ADD DX,0FFFFH ;set carry if remainder
1675 ADC AX,0 ;round upward
1676 MOV BPB_FATSZ
,AX ;number of sectors for 1 FAT copy
1684 SUBTTL COPY_BPB Copy BPB to Boot Record
1686 ;-----------------------------------------------------------------------;
1687 ; COPY_BPB copies the BIOS Parameter Block (BPB) ;
1688 ; to the VDISK Boot Record ;
1689 ;-----------------------------------------------------------------------;
1691 COPY_BPB PROC
;Copy BBP to Boot Record
1695 MOV CX,BPB_LEN
;length of BPB
1696 MOV SI,OFFSET BPB
;source offset
1697 MOV DI,OFFSET BOOT_BPB
;target offset
1698 REP MOVSB ;copy BPB to boot record
1702 SUBTTL VERIFY_EXTENDER
1704 ;-----------------------------------------------------------------------;
1705 ; VERIFY_EXTENDER makes sure that if an Expansion Unit is ;
1706 ; installed, the memory size switches on the Extender Card ;
1707 ; are correctly set. ;
1708 ;-----------------------------------------------------------------------;
1712 EXT_P210 EQU
0210H ;write to latch expansion bus data
1713 ;read to verify expansion bus data
1714 EXT_P213 EQU
0213H ;Expansion Unit status
1716 VERIFY_EXTENDER PROC
1720 MOV DX,EXT_P210
;Expansion bus data port address
1722 MOV AX,5555H
;set data pattern
1723 OUT DX,AL ;write 55H to control port
1727 JMP SHORT $+2 ;Let the I/O circuits catch up
1728 IN AL,020h ;Clear the CMOS bus drivers!
1730 IN AL,DX ;recover data
1731 CMP AH,AL ;did we recover the same data?
1732 JNE VERIFY_EXTENDER_X
;if not, no extender card
1734 NOT AX ;set AX = 0AAAAH
1735 OUT DX,AL ;write 0AAH to control port
1736 PUSH DX ;load data line
1737 POP DX ;load data line
1739 JMP SHORT $+2 ;Let the I/O circuits catch up
1740 IN AL,020h ;Clear the CMOS bus drivers!
1742 IN AL,DX ;recover data
1743 CMP AH,AL ;did we recover the same data?
1744 JNE VERIFY_EXTENDER_X
;if not, no extender card
1746 ;Expansion Unit is present.
1748 ;Determine what the switch settings should be on the Extender Card
1750 INT MEM_SIZE
;get system memory size in KB in AX
1751 ADD AX,63D ;memory size + 63K
1753 SHR AX,CL ;divide by 64
1754 ;AX is highest segment address
1755 MOV AH,AL ;save number of segments
1757 ;Read Expander card switch settings
1759 MOV DX,EXT_P213
;expansion unit status
1760 IN AL,DX ;read status
1761 ;bits 7-4 (hi nibble) are switches
1762 MOV CL,4 ;shift count
1763 SHR AL,CL ;shift switches to bits 3-0 of AL
1765 CMP AH,AL ;do switches match memory size?
1766 JE VERIFY_EXTENDER_X
;yes, exit normally
1768 OR ERR_FLAG
,ERR_EXTSW
;indicate switch settings are wrong
1772 VERIFY_EXTENDER ENDP
1776 ;-----------------------------------------------------------------------;
1777 ; UPDATE_AVAIL updates the address of the first byte in extended ;
1778 ; memory not used by any VDISK buffer ;
1779 ;-----------------------------------------------------------------------;
1780 UPDATE_AVAIL PROC
;update AVAIL_LO and AVAIL_HI of first VDISK
1781 MOV AX,BUFF_SIZE
;number of KB of VDISK buffer
1782 MUL C1024
;DX,AX = number of bytes of VDISK buffer
1785 MOV DS,FIRST_VDISK
;set DS to first VDISK
1786 ADD DS:AVAIL_LO
,AX ;update first available byte location
1794 ;-----------------------------------------------------------------------;
1795 ; This Request Header is used by MOVE_VDISK to move the ;
1796 ; first few sectors of the virtual disk (boot, FAT, and ;
1797 ; Directory) into extended memory. ;
1798 ;-----------------------------------------------------------------------;
1800 MOVE_RH
DB MOVE_RH_L
;length of request header
1802 DB 8 ;output operation
1804 DQ ?
;reserved for DOS
1805 DB ?
;media descriptor byte
1806 MOVE_RHO
DW ?
;offset of data transfer address
1807 MOVE_RHS
DW ?
;segment of data transfer address
1808 MOVE_RHCNT
DW ?
;count of sectors to transfer
1809 DW 0 ;starting sector number
1810 MOVE_RH_L EQU
$-MOVE_RH
;length of request header
1812 ;-----------------------------------------------------------------------;
1813 ; FORMAT_VDISK formats the boot sector, FAT, and directory of an ;
1814 ; extended memory VDISK in storage immediately following ;
1815 ; VDISK code, in preparation for moving to extended memory. ;
1816 ;-----------------------------------------------------------------------;
1817 FORMAT_VDISK PROC
;format boot record, FATs and directory
1819 MOV AX,CS ;compute 20-bit address
1820 MUL WPARA_SIZE
;16 * segment
1821 ADD AX,OFFSET MSGEND
;+ offset
1822 ADC DL,0 ;pick up carry
1823 ADD AX,STACK_SIZE
;plus stack size
1824 ADC DL,0 ;pick up carry
1826 DIV WPARA_SIZE
;split into segment(AX)&offset(DX)
1827 MOV MOVE_RHS
,AX ;save in Request Header for move
1830 MOV DI,DX ;offset to DI
1831 MOV ES,AX ;segment to ES
1833 ;copy the boot record
1835 MOV SI,OFFSET BOOT_RECORD
;point to source field
1836 MOV AX,BPB_RES
;number of reserved sectors
1837 MUL BPB_SSZ
;* sector size = length of boot records
1838 MOV CX,AX ;length to CX for move
1839 REP MOVSB ;move boot record(s)
1843 MOV CL,BPB_FATN
;number of FATs
1845 FORMAT_VDISK_A: ;set up one FAT
1846 PUSH CX ;save loop counter on stack
1847 MOV AL,BPB_MCB
;media control byte
1848 STOSB ;store media control byte, increment DI
1849 MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH
1851 MOV AX,BPB_FATSZ
;number of sectors per FAT
1852 MUL BPB_SSZ
;* sector size = length of FAT in bytes
1853 SUB AX,3 ;less the 3 bytes we've stored
1854 MOV CX,AX ;count to CX
1856 REP STOSB ;clear remainder of FAT
1857 POP CX ;get loop counter off stack
1858 LOOP FORMAT_VDISK_A
;loop for all copies of the FAT
1860 ;Format the directory
1862 MOV SI,OFFSET VOL_LABEL
;point to volume label
1863 MOV CX,VOL_LABEL_LEN
;length of volume directory entry
1864 REP MOVSB ;move volume id to directory
1865 MOV AX,DIR_ENTRY_SIZE
;length of 1 directory entry
1866 MUL BPB_DIRN
;* number entries = bytes of directory
1867 SUB AX,VOL_LABEL_LEN
;less length of volume label
1868 MOV CX,AX ;CX = length of rest of directory
1870 REP STOSB ;clear directory to nulls
1876 ;-----------------------------------------------------------------------;
1877 ; MOVE_VDISK moves the formatted boot sector, FAT, and directory ;
1878 ; into extended memory. ;
1879 ;-----------------------------------------------------------------------;
1882 MOV AL,BPB_FATN
;number of FAT copies
1884 MUL BPB_FATSZ
;number of FAT sectors
1885 ADD AX,BPB_RES
;+ reserved sectors
1886 ADD AX,DIR_SECTORS
;+ directory sectors
1887 MOV MOVE_RHCNT
,AX ;store as I/O length
1889 MOV BX,OFFSET MOVE_RH
;DS:BX point to request header
1890 PUSH DS ;make sure DS gets preserved
1891 CALL INOUT
;move to extended memory
1898 ;-----------------------------------------------------------------------;
1899 ; UPDATE_BOOT updates the BOOT_EM word in the first extended ;
1900 ; memory VDISK (address 10 001EH) to show the kilobyte address ;
1901 ; of the first extended memory byte not used by any VDISK buffer. ;
1902 ;-----------------------------------------------------------------------;
1905 MOV DS,FIRST_VDISK
;set DS to first VDISK
1906 MOV AX,DS:AVAIL_LO
;24-bit end address of all VDISKs
1910 DIV C1024
;address / 1024
1911 MOV BOOT_EM
,AX ;store in temporary location
1913 MOV AX,2 ;length of block move is 2 bytes
1917 MOV AX,PARA_SIZE
;16
1918 MOV CX,CS ;our segment address
1919 MUL CX ;16 * segment address
1920 ADD AX,OFFSET BOOT_EM
;+ offset of source data
1921 ADC DL,0 ;pick up any carry
1923 MOV SRC
.DESC_BASEL
,AX ;store source base address
1924 MOV SRC
.DESC_BASEH
,DL
1926 MOV TGT
.DESC_BASEL
,BOOT_EM_OFF
;offset of BOOT_EM
1927 MOV TGT
.DESC_BASEH
,10H
;1 megabyte
1929 MOV CX,1 ;move 1 word
1933 MOV SI,OFFSET GDT
;ES:DI point to global descriptor table
1935 MOV AH,EM_BLKMOVE
;function code
1936 INT EM_INT
;move BOOT_EM to 10 001EH
1942 ;-----------------------------------------------------------------------;
1943 ; STEAL_INT19 changes the INT 19H vector to point to this VDISK ;
1944 ; so that subsequent extended memory VDISKS may locate the ;
1945 ; AVAIL_HI and AVAIL_LO fields to determine their buffer start ;
1947 ;-----------------------------------------------------------------------;
1951 MOV DS,AX ;set DS = 0
1953 CLI ;disable interrupts
1954 LES DI,DS:BOOT_VEC
;get original vector's content
1955 MOV CS:INTV19O
,DI ;save original vector
1957 MOV DS:BOOT_VECO
,OFFSET VDISK_INT19
;offset of new INT routine
1958 MOV DS:BOOT_VECS
,CS ;segment of new INT routine
1959 STI ;enable interrupts again
1964 SUBTTL FILL_RH Fill
in Request Header
1966 ;-----------------------------------------------------------------------;
1967 ; FILL_RH fills in the Request Header returned to DOS ;
1968 ;-----------------------------------------------------------------------;
1970 FILL_RH PROC
;fill in INIT Request Header fields
1971 MOV CX,START_BUFFER_PARA
;segment end of VDISK resident code
1972 MOV AX,PARAS_PER_SECTOR
;paragraphs per sector
1973 MUL BPB_SECN
;* number of sectors
1974 ADD AX,CX ;+ starting segment
1975 MOV DX,AX ;DX is segment of end VDISK buffer
1976 CMP EM_SW
,0 ;if extended memory not requested
1977 JE FILL_RH_A
;skip DX adjustment
1979 MOV DX,CX ;end of code segment addr
1980 FILL_RH_A: ;DX is proper ending segment address
1981 MOV AL,1 ;number of units
1982 test CS:err_flag2
,err_baddos
1985 TEST ERR_FLAG
,ERR_SYSSZ
+ERR_EXTSW
;if bypassing install
1986 JZ FILL_RH_B
;jump if installing driver
1989 MOV DX,CS ;segment of end address
1990 XOR AL,AL ;number of units is zero
1992 PUSH DS ;preserve DS
1993 LDS BX,RH_PTRA
;get Request Header addr in DS:BX
1994 MOV RH
.RH0_NUN
,AL ;store number of units (0 or 1)
1995 MOV RH
.RH0_ENDO
,0 ;end offset is always zero
1996 MOV RH
.RH0_ENDS
,DX ;end of VDISK or end of buffer
1997 MOV RH
.RH0_BPBO
,OFFSET BPB_PTR
1998 MOV RH
.RH0_BPBS
,CS ;BPB array address
2003 SUBTTL WRITE_MESSAGES
and associated routines
2005 ;-----------------------------------------------------------------------;
2006 ; WRITE_MESSAGE writes a series of messages to the standard ;
2007 ; output device showing the VDISK parameter values actually used. ;
2008 ;-----------------------------------------------------------------------;
2010 CHAR4
DB 'nnnn$' ;build 4 ASCII decimal digits
2013 WRITE_MESSAGES PROC
;display all messages
2015 MSG IMSG
;'VDISK virtual disk $'
2017 test CS:err_flag2
,err_baddos
2018 jz check_dos_version
2023 ;If DOS Version 3.x is in use, the Request Header contains a drive code
2024 ;that is displayed to show which drive letter was assigned to this
2025 ;VDISK. This field is not present in the DOS Version 2 Request Header.
2028 MOV AH,DOS_VERS
;get DOS version call
2031 CMP AL,3 ;DOS Version 3 or greater?
2032 JB WRITE_MESSAGES_A
;no, bypass drive letter
2034 PUSH DS ;preserve DS
2035 LDS BX,RH_PTRA
;get Request Header Address
2036 MOV DL,RH
.RH0_DRIV
;get drive code
2037 ADD DL,'A' ;convert to drive letter
2040 MOV AH,DOS_PCHR
;function code to write character in DL
2041 INT DOS
;display drive letter
2043 MOV DL,':' ;display trailing colon
2047 MSG MSGCRLF
;end the first line
2049 ;If any of the user specified values has been adjusted, issue an
2050 ;appropriate message
2052 TEST ERR_FLAG
,ERR_BSIZE
;was buffersize adjusted?
2053 JZ WRITE_MESSAGES_B
;if not, skip message
2055 MSG ERRM1
;buffer size adjusted
2058 TEST ERR_FLAG
,ERR_SSZ
;was sector size adjusted?
2059 JZ WRITE_MESSAGES_C
;if not, skip message
2061 MSG ERRM2
;sector size adjusted
2064 TEST ERR_FLAG
,ERR_DIRN
;were directory entries adjusted?
2065 JZ WRITE_MESSAGES_D0
;if not, skip message
2067 MSG ERRM3
;directory entries adjusted
2070 TEST ERR_FLAG
,ERR_ESIZE
;was transfer size adjusted?
2071 JZ WRITE_MESSAGES_D
;if not, skip message
2073 MSG ERRM7
;transfer size adjusted
2076 TEST ERR_FLAG
,ERR_SWTCH
;was an invalid switch character found?
2077 JZ WRITE_MESSAGES_E
;if not, skip message
2079 MSG ERRM5
;invalid switch character
2082 TEST ERR_FLAG
,ERR_SYSSZ
;is system size too small to install?
2083 JZ WRITE_MESSAGES_F
;if not, bypass error message
2085 MSG ERRM4
;too large for system storage
2086 RET ;skip messages showing adjusted sizes
2089 TEST ERR_FLAG
,ERR_EXTSW
;extender card switches wrong?
2090 JZ WRITE_MESSAGES_G
;if not, bypass error message
2092 MSG ERRM6
;extender card switches wrong msg
2093 RET ;skip remaining messages
2095 WRITE_MESSAGES_G: ;display adjusted size messages
2096 MSG MSG1
;buffer size:
2098 MOV DX,BUFF_SIZE
;buffer size in binary
2099 CALL STOR_SIZE
;convert binary to ASCII decimal
2100 MSG CHAR4
;print 4 decimals
2103 MSG MSG3
;sector size:
2105 CALL STOR_SIZE
;convert binary to ASCII decimal
2106 MSG CHAR4
;print 4 decimals
2107 MSG MSGCRLF
;finish off line
2109 MSG MSG4
;directory entries:
2110 MOV DX,BPB_DIRN
;number of directory entries
2112 MSG CHAR4
;print 4 decimals
2113 MSG MSGCRLF
;finish off the line
2115 CMP CS:EM_SW
,0 ;extended memory ?
2117 MSG MSG5
;transfer size:
2119 CALL STOR_SIZE
;convert binary to ASCII decimal
2120 MSG CHAR4
;print 4 decimals
2121 MSG MSGCRLF
;finish off line
2124 MSG MSGCRLF
;one more blank line to set it off
2125 RET ;return to INIT_P1
2127 ;SHOW_MSG displays a string at DS:DX on the standard output device
2128 ;String is terminated by a $
2130 SHOW_MSG PROC
;display string at DS:DX
2131 PUSH AX ;preserve AX across call
2132 MOV AH,DOS_PSTR
;DOS function code
2133 INT DOS
;invoke DOS print string function
2138 ;STOR_SIZE converts the content of DX to 4 decimal characters in CHAR4
2139 ;(DX must be <= 9999)
2141 STOR_SIZE PROC
;convert DX to 4 decimals in CHAR4
2142 ;develop 4 packed decimal digits in AX
2143 XOR AX,AX ;clear result register
2144 MOV CX,16 ;shift count
2146 SHL DX,1 ;shift high bit into carry
2147 ADC AL,AL ;double AL, carry in
2148 DAA ;adjust for packed decimal
2150 ADC AL,AL ;double high byte, carry in
2153 LOOP STOR_SIZE_B
;AX contains 4 packed decimal digits
2156 POP ES ;point ES:DI to output string
2159 MOV CX,1310H
;10H in CL is difference between blank and zero
2160 ;13H in CH is decremented and ANDed to force
2161 ;last character not to be zero suppressed
2162 PUSH AX ;save AX on stack
2163 MOV DL,AH ;2 decimals to DL
2164 CALL STOR_SIZE_2
;display DL as 2 decimal characters
2165 POP DX ;bring low 2 decimals into DL
2166 STOR_SIZE_2: ;display DL as 2 decimal characters
2167 MOV DH,DL ;save 2 decimals in DH
2168 SHR DL,1 ;shift high order decimal right to low position
2172 CALL STOR_SIZE_1
;display low nibble of DL
2173 MOV DL,DH ;get low decimal from pair
2174 STOR_SIZE_1: ;display low nibble of DL as 1 decimal char
2175 AND DL,0FH ;clear high nibble
2176 JZ STOR_SIZE_Z
;if digit is significant,
2177 XOR CL,CL ;defeat zero suppression
2179 DEC CH ;decrement zero suppress counter
2180 AND CL,CH ;always display least significant digit
2181 OR DL,'0' ;convert packed decimal to ASCII
2182 SUB DL,CL ;zero suppress (nop or change '0' to ' ')
2183 MOV AL,DL ;char to DL
2184 STOSB ;store char at ES:DI, increment DI
2190 INIT_P1 ENDP
;end of INIT part one
2192 ;-----------------------------------------------------------------------;
2193 ; VDISK Message definitions ;
2194 ;-----------------------------------------------------------------------;
2196 IMSG
DB 'VDISK virtual disk ','$'
2198 ERRM1
DB ' Buffer size adjusted',CR
,LF
,'$'
2199 ERRM2
DB ' Sector size adjusted',CR
,LF
,'$'
2200 ERRM3
DB ' Directory entries adjusted',CR
,LF
,'$'
2201 ERRM4
DB ' VDISK not installed - insufficient memory'
2202 DB CR
,LF
,CR
,LF
,BEL
,'$'
2203 ERRM5
DB ' Invalid switch character',CR
,LF
,'$'
2204 ERRM6
DB ' VDISK not installed - Extender Card switches'
2206 DB ' do not match system memory size'
2207 DB CR
,LF
,CR
,LF
,BEL
,'$'
2208 ERRM7
DB ' Transfer size adjusted',CR
,LF
,'$'
2209 ERRM8
DB ' VDISK not installed - Incorrect DOS version'
2210 DB CR
,LF
,CR
,LF
,BEL
,'$'
2212 MSG1
DB ' Buffer size: $'
2214 MSGCRLF
DB CR
,LF
,'$'
2215 MSG3
DB ' Sector size: $'
2216 MSG4
DB ' Directory entries: $'
2217 MSG5
DB ' Transfer size: $'
2218 MSGEND
LABEL BYTE ; End of message text