summaryrefslogtreecommitdiff
path: root/v4.0/src/DEV/VDISK/VDISK.ASM
diff options
context:
space:
mode:
authorGravatar Mark Zbikowski2024-04-25 21:24:10 +0100
committerGravatar Microsoft Open Source2024-04-25 22:32:27 +0000
commit2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch)
tree80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/DEV/VDISK/VDISK.ASM
parentMerge pull request #430 from jpbaltazar/typoptbr (diff)
downloadms-dos-main.tar.gz
ms-dos-main.tar.xz
ms-dos-main.zip
MZ is back!HEADmain
Diffstat (limited to 'v4.0/src/DEV/VDISK/VDISK.ASM')
-rw-r--r--v4.0/src/DEV/VDISK/VDISK.ASM2221
1 files changed, 2221 insertions, 0 deletions
diff --git a/v4.0/src/DEV/VDISK/VDISK.ASM b/v4.0/src/DEV/VDISK/VDISK.ASM
new file mode 100644
index 0000000..5994111
--- /dev/null
+++ b/v4.0/src/DEV/VDISK/VDISK.ASM
@@ -0,0 +1,2221 @@
1 PAGE ,132
2 TITLE VDISK - Virtual Disk Device Driver
3
4;VDISK simulates a disk drive, using Random Access Memory as the storage medium.
5
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.
8
9;(C) Copyright 1988 Microsoft
10;Licensed Material - Program Property of Microsoft
11
12;Add the following statement to CONFIG.SYS
13; DEVICE=[d:][path]VDISK.SYS bbb sss ddd [/E:m]
14
15; where: bbb is the desired buffer size (in kilobytes)
16; minimum 1KB, maximum is size of available memory,
17; default is 64KB.
18
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
23; by CHKDSK.
24
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.
28
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
32
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.
36
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
43; is 8 sectors.
44
45; Brackets indicate optional operands.
46
47
48; Samples:
49; DEVICE=\path\VDISK.SYS 160 512 64
50; results in a 160KB VDISK, with 512 byte sectors, 64 directory entries
51
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)
55
56 SUBTTL Structure Definitions
57 PAGE
58;-----------------------------------------------------------------------;
59; Request Header (Common portion) ;
60;-----------------------------------------------------------------------;
61RH EQU DS:[BX] ;addressability to Request Header structure
62
63RHC STRUC ;fields common to all request types
64 DB ? ;length of Request Header (including data)
65 DB ? ;unit code (subunit)
66RHC_CMD DB ? ;command code
67RHC_STA DW ? ;status
68 DQ ? ;reserved for DOS
69RHC ENDS ;end of common portion
70
71CMD_INPUT EQU 4 ;RHC_CMD is INPUT request
72
73;status values for RHC_STA
74
75STAT_DONE EQU 01H ;function complete status (high order byte)
76STAT_CMDERR EQU 8003H ;invalid command code error
77STAT_CRC EQU 8004H ;CRC error
78STAT_SNF EQU 8008H ;sector not found error
79STAT_BUSY EQU 0200H ;busy bit (9) for Removable Media call
80;-----------------------------------------------------------------------;
81; Request Header for INIT command ;
82;-----------------------------------------------------------------------;
83RH0 STRUC
84 DB (TYPE RHC) DUP (?) ;common portion
85RH0_NUN DB ? ;number of units
86 ;set to 1 if installation succeeds,
87 ;set to 0 to cause installation failure
88RH0_ENDO DW ? ;offset of ending address
89RH0_ENDS DW ? ;segment of ending address
90RH0_BPBO DW ? ;offset of BPB array address
91RH0_BPBS DW ? ;segment of BPB array address
92RH0_DRIV DB ? ;drive code (DOS 3 only)
93RH0 ENDS
94
95RH0_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
97
98;-----------------------------------------------------------------------;
99; Request Header for MEDIA CHECK Command ;
100;-----------------------------------------------------------------------;
101RH1 STRUC
102 DB (TYPE RHC) DUP (?) ;common portion
103 DB ? ;media descriptor
104RH1_RET DB ? ;return information
105RH1 ENDS
106;-----------------------------------------------------------------------;
107; Request Header for BUILD BPB Command ;
108;-----------------------------------------------------------------------;
109RH2 STRUC
110 DB (TYPE RHC) DUP(?) ;common portion
111 DB ? ;media descriptor
112 DW ? ;offset of transfer address
113 DW ? ;segment of transfer address
114RH2_BPBO DW ? ;offset of BPB table address
115RH2_BPBS DW ? ;segment of BPB table address
116RH2 ENDS
117;-----------------------------------------------------------------------;
118; Request Header for INPUT, OUTPUT, and OUTPUT with verify ;
119;-----------------------------------------------------------------------;
120RH4 STRUC
121 DB (TYPE RHC) DUP (?) ;common portion
122 DB ? ;media descriptor
123RH4_DTAO DW ? ;offset of transfer address
124RH4_DTAS DW ? ;segment of transfer address
125RH4_CNT DW ? ;sector count
126RH4_SSN DW ? ;starting sector number
127RH4 ENDS
128
129RH4_DTAA EQU DWORD PTR RH4_DTAO ;offset/segment of transfer address
130
131;-----------------------------------------------------------------------;
132; Segment Descriptor (part of Global Descriptor Table) ;
133;-----------------------------------------------------------------------;
134DESC STRUC ;data segment descriptor
135DESC_LMT DW 0 ;segment limit (length)
136DESC_BASEL DW 0 ;bits 15-0 of physical address
137DESC_BASEH DB 0 ;bits 23-16 of physical address
138 DB 0 ;access rights byte
139 DW 0 ;reserved
140DESC ENDS
141
142 SUBTTL Equates and Macro Definitions
143 PAGE
144
145MEM_SIZE EQU 12H ;BIOS memory size determination INT
146 ;returns system size in KB in AX
147
148EM_INT EQU 15H ;extended memory BIOS interrupt INT
149EM_BLKMOVE EQU 87H ;block move function
150EM_MEMSIZE EQU 88H ;memory size determination in KB
151
152BOOT_INT EQU 19H ;bootstrap DOS
153
154DOS EQU 21H ;DOS request INT
155DOS_PCHR EQU 02H ;print character function
156DOS_PSTR EQU 09H ;print string function
157DOS_VERS EQU 30H ;get DOS version
158
159TAB EQU 09H ;ASCII tab
160LF EQU 0AH ;ASCII line feed
161CR EQU 0DH ;ASCII carriage return
162BEL EQU 07H ;ASCII bell
163
164PARA_SIZE EQU 16 ;number of bytes in one 8088 paragraph
165DIR_ENTRY_SIZE EQU 32 ;number of bytes per directory entry
166MAX_FATE EQU 0FE0H ;largest number of FAT entries allowed
167
168;default values used if parameters are omitted
169
170DFLT_BSIZE EQU 64 ;default VDISK buffer size (KB)
171DFLT_SSZ EQU 128 ;default sector size
172DFLT_DIRN EQU 64 ;default number of directory entries
173DFLT_ESS EQU 8 ;default maximum sectors to transfer
174
175MIN_DIRN EQU 2 ;minimum number of directory entries
176MAX_DIRN EQU 512 ;maximum number of directory entries
177
178STACK_SIZE EQU 512 ;length of stack during initialization
179
180;-----------------------------------------------------------------------;
181; MSG invokes the console message subroutine ;
182;-----------------------------------------------------------------------;
183
184MSG MACRO TEXT
185 PUSH DX ;;save DX across call
186 MOV DX,OFFSET TEXT ;;point to message
187 CALL SHOW_MSG ;;issue message
188 POP DX
189 ENDM
190
191
192 SUBTTL Resident Data Area
193 PAGE
194;-----------------------------------------------------------------------;
195; Map INT 19H vector in low storage ;
196;-----------------------------------------------------------------------;
197INT_VEC SEGMENT AT 00H
198 ORG 4*BOOT_INT
199BOOT_VEC LABEL DWORD
200BOOT_VECO DW ? ;offset
201BOOT_VECS DW ? ;segment
202INT_VEC ENDS
203
204
205CSEG SEGMENT PARA PUBLIC 'CODE'
206 ASSUME CS:CSEG
207;-----------------------------------------------------------------------;
208; Resident data area. ;
209; ;
210; All variables and constants required after initialization ;
211; part one are defined here. ;
212;-----------------------------------------------------------------------;
213
214START EQU $ ;begin resident VDISK data & code
215
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
225
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
228;has been installed.
229
230VOL_LABEL DB 'VDISK ' ;00-10 volume name (shows program level)
231 DB 28H ;11-11 attribute (volume label)
232 DT 0 ;12-21 reserved
233 DW 6000H ;22-23 time=12:00 noon
234 DW 0986H ;24-25 date=12/06/84
235VOL_LABEL_LEN EQU $-VOL_LABEL ;length of volume label
236
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.
241
242AVAIL_LO DW 0 ;address of first free byte of
243AVAIL_HI DB 10H ;extended memory
244
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.
247
248INTV19 LABEL DWORD
249INTV19O DW ? ;offset
250INTV19S DW ? ;segment
251
252
253PARAS_PER_SECTOR DW ? ;number of 16-byte paragraphs in one sector
254
255START_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.
258
259EM_SW DB 0 ;non-zero if Extended Memory
260
261EM_STAT DW 0 ;AX from last unsuccessful extended memory I/O
262
263START_EM_LO DW ? ;24-bit address of start of VDISK buffer
264START_EM_HI DB ? ;(extended memory only)
265
266WPARA_SIZE DW PARA_SIZE ;number of bytes in one paragraph
267
268MAX_CNT DW ? ;(0FFFFH/BPB_SSZ) truncated, the maximum
269 ;number of sectors that can be transferred
270 ;without worrying about 64KB wrap
271
272SECT_LEFT DW ? ;sectors left to transfer
273
274IO_SRCA LABEL DWORD ;offset/segment of source
275IO_SRCO DW ? ;offset
276IO_SRCS DW ? ;segment
277
278IO_TGTA LABEL DWORD ;offset/segment of target
279IO_TGTO DW ? ;offset
280IO_TGTS DW ? ;segment
281
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.
289
290BPB LABEL BYTE ;BIOS Parameter Block (BPB)
291BPB_SSZ DW 0 ;number of bytes per disk sector
292BPB_AUSZ DB 1 ;sectors per allocation unit
293BPB_RES DW 1 ;number of reserved sectors (for boot record)
294BPB_FATN DB 1 ;number of File Allocation Table (FAT) copies
295BPB_DIRN DW 0 ;number of root directory entries
296BPB_SECN DW 1 ;total number of sectors
297 ;computed from buffer size and sector size
298 ;(this includes reserved, FAT, directory,
299 ;and data sectors)
300BPB_MCB DB 0FEH ;media descriptor byte
301BPB_FATSZ DW 1 ;number of sectors occupied by a single FAT
302 ;computed from BPBSSZ and BPBSECN
303BPB_LEN EQU $-BPB ;length of BIOS parameter block
304
305BPB_PTR DW BPB ;BIOS Parameter Block pointer array (1 entry)
306;-----------------------------------------------------------------------;
307; Request Header (RH) address, saved here by "strategy" routine ;
308;-----------------------------------------------------------------------;
309RH_PTRA LABEL DWORD
310RH_PTRO DW ? ;offset
311RH_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)
321
322GDT LABEL BYTE ;begin global descriptor table
323 DESC <> ;dummy descriptor
324 DESC <> ;descriptor for GDT itself
325SRC DESC <,,,93H,> ;source descriptor
326TGT DESC <,,,93H,> ;target descriptor
327 DESC <> ;BIOS CS descriptor
328 DESC <> ;stack segment descriptor
329
330 SUBTTL INT 19H (boot) interrupt handler
331 PAGE
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.
337
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.
341
342;This routine restores the original INT 19H vector's content, then jumps
343;to the original routine.
344
345;INT 19H, the "Boot" INT, is always altered when DOS is booted.
346
347;This routine is entered with interrupts disabled.
348
349VDISK_INT19 PROC ;INT 19H received
350 PUSH DS ;save registers we're going to alter
351 PUSH AX
352
353 XOR AX,AX
354 MOV DS,AX ;set DS = 0
355 ASSUME DS:INT_VEC
356
357 MOV AX,CS:INTV19O ;get offset of saved vector
358 MOV DS:BOOT_VECO,AX ;store offset in interrupt vector
359
360 MOV AX,CS:INTV19S ;get segment of saved vector
361 MOV DS:BOOT_VECS,AX ;store segment in interrupt vector
362
363 POP AX
364 POP DS
365
366 JMP CS:INTV19 ;go to original interrupt routine
367
368VDISK_INT19 ENDP
369
370 ASSUME DS:NOTHING
371
372 SUBTTL Device Strategy & interrupt entry points
373 PAGE
374;-----------------------------------------------------------------------;
375; Device "strategy" entry point ;
376; ;
377; Retain the Request Header address for use by Interrupt routine ;
378;-----------------------------------------------------------------------;
379STRATEGY PROC FAR
380 MOV CS:RH_PTRO,BX ;offset
381 MOV CS:RH_PTRS,ES ;segment
382 RET
383STRATEGY ENDP
384;-----------------------------------------------------------------------;
385; Table of command processing routine entry points ;
386;-----------------------------------------------------------------------;
387CMD_TABLE LABEL WORD
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
403MAX_CMD EQU ($-CMD_TABLE)/2 ;highest valid command follows
404 DW OFFSET REMOVABLE_MEDIA ;15 - Removable media
405
406;-----------------------------------------------------------------------;
407; Device "interrupt" entry point ;
408;-----------------------------------------------------------------------;
409IRPT PROC FAR ;device interrupt entry point
410 PUSH DS ;save all registers Revised
411 PUSH ES
412 PUSH AX
413 PUSH BX
414 PUSH CX
415 PUSH DX
416 PUSH DI
417 PUSH SI
418 ;BP isn't used, so it isn't saved
419 CLD ;all moves forward
420
421 LDS BX,CS:RH_PTRA ;get RH address passed to "strategy" into DS:BX
422
423 MOV AL,RH.RHC_CMD ;command code from Request Header
424 CBW ;zero AH (if AL > 7FH, next compare will
425 ;catch that error)
426
427 CMP AL,MAX_CMD ;if command code is too high
428 JA IRPT_CMD_HIGH ;jump to error routine
429
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"
433
434 ADD AX,AX ;double command code for table offset
435 MOV DI,AX ;put into index register for JMP
436
437 XOR AX,AX ;initialize return to "no error"
438
439;At entry to command processing routine:
440
441; DS:BX = Request Header address
442; CS = VDISK code segment address
443; AX = 0
444
445; top of stack is return address, IRPT_CMD_EXIT
446
447 JMP CS:CMD_TABLE[DI] ;call routine to handle the command
448
449
450IRPT_CMD_ERROR: ;CALLed for unsupported character mode commands
451
452INPUT_IOCTL: ;IOCTL input
453INPUT_NOWAIT: ;Non-destructive input no wait
454INPUT_STATUS: ;Input status
455INPUT_FLUSH: ;Input flush
456
457OUTPUT_IOCTL: ;IOCTL output
458OUTPUT_STATUS: ;Output status
459OUTPUT_FLUSH: ;Output flush
460
461 POP AX ;pop return address off stack
462
463IRPT_CMD_HIGH: ;JMPed to if RHC_CMD > MAX_CMD
464 MOV AX,STAT_CMDERR ;"invalid command" and error
465
466IRPT_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
472 POP DI
473 POP DX
474 POP CX
475 POP BX
476 POP AX
477 POP ES
478 POP DS
479 RET
480IRPT ENDP
481
482 SUBTTL Command Processing routines
483 PAGE
484;-----------------------------------------------------------------------;
485; Command Code 1 - Media Check ;
486; At entry, DS:BX point to request header, AX = 0 ;
487;-----------------------------------------------------------------------;
488MEDIA_CHECK PROC
489 MOV RH.RH1_RET,1 ;indicate media not changed
490 RET ;AX = zero, no error
491MEDIA_CHECK ENDP
492;-----------------------------------------------------------------------;
493; Command Code 2 - Build BPB ;
494; At entry, DS:BX point to request header, AX = 0 ;
495;-----------------------------------------------------------------------;
496BLD_BPB PROC
497 MOV RH.RH2_BPBO,OFFSET BPB ;return pointer to our BPB
498 MOV RH.RH2_BPBS,CS
499 RET ;AX = zero, no error
500BLD_BPB ENDP
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;-----------------------------------------------------------------------;
507REMOVABLE_MEDIA PROC
508 MOV AX,STAT_BUSY ;set status bit 9 (busy)
509 ;indicating non-removable media
510DEVICE_OPEN: ;NOP for device open
511DEVICE_CLOSE: ;NOP for device close
512 RET
513REMOVABLE_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;-----------------------------------------------------------------------;
520INOUT PROC
521INPUT:
522OUTPUT:
523OUTPUT_VERIFY:
524
525;Make sure I/O is entirely within the VDISK sector boundaries
526
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
531
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
535
536INOUT_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
540
541INOUT_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
544
545 CMP CS:EM_SW,0 ;extended memory mode?
546 JNE INOUT_EM ;jump to extended memory I/O code
547
548;Compute offset and segment of VDISK buffer for starting segment in CX:SI
549
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
555
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
558;sectors.
559
560 MOV AX,PARA_SIZE ;16
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
566 ;AX:DX = DTA address
567
568;AX:DX is caller's DTA segment:offset, CX:SI is VDISK buffer segment:offset
569
570;If this is an OUTPUT request, exchange the source and target addresses
571
572 CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
573 JE INOUT_B ;jump if INPUT operation
574
575 XCHG AX,CX ;swap source and target segment
576 XCHG DX,SI ;swap source and target offset
577
578INOUT_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
583
584 JMP SHORT INOUT_E ;AX := SECT_LEFT, test for zero
585INOUT_C: ;SECT_LEFT in AX, non-zero
586
587; Compute number of sectors to transfer in a single move,
588; AX = minimum of (SECT_LEFT, MAX_CNT)
589
590; MAX_CNT is the maximum number of sectors that can be moved without
591; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
592
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
596
597 MOV AX,CX ;else move MAX_CNT sectors
598INOUT_D:
599 SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move
600
601;Move AX sectors from source to target
602
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
607
608 LDS SI,CS:IO_SRCA ;source segment/offset to DS:SI
609 LES DI,CS:IO_TGTA ;target segment/offset to ES:DI
610
611 REP MOVSW ;move MOV_CNT sectors
612
613;Update source and target paragraph addresses
614;AX has number of words moved
615
616 SHR AX,1 ;words moved / 8 = paragraphs moved
617 SHR AX,1
618 SHR AX,1
619
620 ADD CS:IO_SRCS,AX ;add paragraphs moved to source segment
621 ADD CS:IO_TGTS,AX ;add paragraphs moved to target segment
622
623;Determine if more sectors need to be transferred
624
625INOUT_E: ;do while SECT_LEFT <> zero
626 MOV AX,CS:SECT_LEFT ;get sectors left to transfer
627 OR AX,AX ;set flags
628 JNZ INOUT_C ;go back to transfer some sectors
629 RET ;AX = zero, all sectors transferred
630
631 SUBTTL Extended Memory I/O routine
632 PAGE
633;-----------------------------------------------------------------------;
634; Extended Memory I/O routine ;
635;-----------------------------------------------------------------------;
636INOUT_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
641 MOV AX,CS
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
647
648 MOV SI,RH.RH4_DTAO ;caller's DTA offset
649
650;Compute 24-bit address of VDISK sector in CX (hi) and SI (low)
651
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
658
659;Compute 24-bit address of caller's DTA in DX (hi) and AX (low)
660
661 MOV AX,PARA_SIZE ;16
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
665
666;Caller's DTA address is in CX,SI, VDISK buffer address is in DX,AX.
667
668;If this is an OUTPUT request, exchange the source and target addresses
669
670 CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
671 JE INOUT_EM_B ;jump if INPUT operation
672
673 XCHG DX,CX ;swap source and target high byte
674 XCHG AX,SI ;swap source and target low word
675
676INOUT_EM_B: ;CX,SI is source, DX,AX is target
677
678 MOV SRC.DESC_BASEL,SI ;low 16 bits of source address
679 MOV SRC.DESC_BASEH,CL ;high 8 bits of source address
680
681 MOV TGT.DESC_BASEL,AX ;low 16 bits of target address
682 MOV TGT.DESC_BASEH,DL ;high 8 bits of target address
683
684 JMP SHORT INOUT_EM_E ;AX := SECT_LEFT, test for zero
685INOUT_EM_C: ;SECT_LEFT in AX, non-zero
686
687; Compute number of sectors to transfer in a single move,
688; AX = minimum of (SECT_LEFT, MAX_CNT)
689
690; MAX_CNT is the maximum number of sectors that can be moved without
691; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
692
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
696
697 MOV AX,CX ;else move MAX_CNT sectors
698INOUT_EM_D:
699 SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move
700
701;Move AX sectors from source to target
702
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)
706 MOV SRC.DESC_LMT,AX
707
708 PUSH AX ;preserve byte count on stack
709
710 SHR AX,1 ;/2 = word count
711 MOV CX,AX ;word count to CX
712
713 PUSH CS
714 POP ES ;set ES = CS
715 MOV SI,OFFSET GDT ;ES:SI point to GDT
716
717 MOV AH,EM_BLKMOVE ;function is block move
718 INT EM_INT ;move an even number of words
719
720 POP CX ;get byte count back from stack
721
722 OR AH,AH ;get error code
723
724 JNZ INOUT_EM_XE ;jump if I/O error encountered
725
726;Update source and target addresses
727
728 ADD SRC.DESC_BASEL,CX ;add bytes moved to source
729 ADC SRC.DESC_BASEH,0 ;pick up any carry
730
731 ADD TGT.DESC_BASEL,CX ;add bytes moved to target
732 ADC TGT.DESC_BASEH,0 ;pick up any carry
733
734;Determine if more sectors need to be transferred
735
736INOUT_EM_E: ;do while SECT_LEFT <> zero
737 MOV AX,CS:SECT_LEFT ;get sectors left to transfer
738 OR AX,AX ;set flags
739 JNZ INOUT_EM_C ;go back to transfer some sectors
740
741INOUT_EM_X2: ;revert to original stack
742 POP DI ;get old SP
743 POP SI ;get old SS
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
749
750INOUT_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
755INOUT ENDP
756
757 DW 40 DUP (?) ;stack for extended memory I/O
758EM_STACK LABEL WORD
759
760 SUBTTL Boot Record
761 PAGE
762;-----------------------------------------------------------------------;
763; Adjust the assembly-time instruction counter to a paragraph ;
764; boundary ;
765;-----------------------------------------------------------------------;
766
767 IF ($-START) MOD 16
768 ORG ($-START) + 16 - (($-START) MOD 16)
769 ENDIF
770
771VDISK EQU $ ;start of virtual disk buffer
772VDISKP 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. ;
776; ;
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;-----------------------------------------------------------------------;
782
783BOOT_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
787BOOT_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
797
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.
807
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.
812
813;For example, if two VDISKs are installed, one 320KB and one 64KB, the
814;address calculations are as follows:
815
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)
825
826;Content of BOOT_EM = 0580H
827
828BOOT_EM_OFF EQU $-BOOT_RECORD ;offset from 10 0000H of the following word
829BOOT_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.
834
835;INIT_P1 is overlaid by the virtual disk buffer
836
837;INIT_P1 is executed first, then jumps to INIT_P2. INIT_P2 returns to caller.
838
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.
846
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
854
855INIT_P2 PROC ;second part of initialization
856 ASSUME DS:CSEG ;DS set in INIT_P1
857
858;Initialize File Allocation Table(s) (FATs)
859
860INIT_P2_FAT: ;set up one FAT, sector number in AX
861
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
866 STOSW
867
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
871
872 POP CX ;get loop counter off stack
873 LOOP INIT_P2_FAT ;loop for all copies of the FAT
874
875;Put the volume label in the first directory entry
876
877 MOV CX,VOL_LABEL_LEN ;length of volume directory entry
878 REP MOVSB ;move volume id to directory
879
880;Zero the remainder of the directory
881
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
886 XOR AX,AX
887 REP STOSB ;clear directory to nulls
888 RET ;return with AX=0
889INIT_P2 ENDP
890
891PATCH_AREA DB 5 DUP ('PATCH AREA ')
892TEST_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 '
903 DB ' Support '
904
905MAXSEC_TRF DW 0 ;maximum number of sectors to transfer when
906 ;in extended memory
907
908BUFF_SIZE DW 0 ;desired VDISK buffer size in kilobytes
909
910MIN_MEMORY_LEFT DW 64 ;minimum amount of system memory (kilobytes)
911 ;that must remain after VDISK is installed
912
913FIRST_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
916 ;has been installed
917
918FIRST_VDISK DW ? ;segment address of 1st VDISK device driver
919PARA_PER_KB DW 1024/PARA_SIZE ;paragraphs in one kilobyte
920C1024 DW 1024 ;bytes in one kilobyte
921DIRE_SIZE DW DIR_ENTRY_SIZE ;bytes in one directory entry
922DIR_SECTORS DW ? ;number of sectors of directory
923
924ERR_FLAG DB 0 ;error indicators to condition messages
925ERR_BSIZE EQU 80H ;buffer size adjusted
926ERR_SSZ EQU 40H ;sector size adjusted
927ERR_DIRN EQU 20H ;number of directory entries adjusted
928ERR_PASS EQU 10H ;some adjustment made that requires
929 ;recomputation of values previously computed
930ERR_SSZB EQU ERR_SSZ+ERR_PASS ;sector size altered this pass
931ERR_SYSSZ EQU 08H ;system storage too small for VDISK
932ERR_SWTCH EQU 04H ;invalid switch character
933ERR_EXTSW EQU 02H ;extender card switches don't match memory size
934ERR_ESIZE EQU 01H ;Transfer size adjusted
935
936; additional errors added - kwc
937
938major_version equ 4 ;Major DOS version
939minor_version equ 00 ;Minor DOS Version
940
941expected_version equ (MINOR_VERSION SHL 8)+MAJOR_VERSION
942
943err_flag2 db 0
944err_baddos equ 01h ; Invalid DOS Version
945
946 SUBTTL Initialization, Part one
947 PAGE
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.
954
955INIT_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
960 MOV SS,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
966
967 push bx ;secure registers before DOS int
968 push cx ;secure registers before DOS int
969
970; add version check - kwc
971
972 mov ah,030h
973 int 21h
974 pop cx ;restore pointer values
975 pop bx ;restore pointer values
976 cmp ax,expected_version
977 je okdos
978
979 or cs:err_flag2,err_baddos
980
981okdos:
982 CALL GET_PARMS ;get parameters from CONFIG.SYS line
983
984 PUSH CS
985 POP DS ;set DS = CS
986 ASSUME DS:CSEG
987
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
992
993 CALL VERIFY_EXTENDER ;Verify that extender card switches are right
994
995 TEST ERR_FLAG,ERR_EXTSW ;are switches wrong?
996 JNZ INIT_P1_A ;if so, exit with messages
997
998 test CS:err_flag2,err_baddos
999 jnz init_p1_a
1000
1001 CMP EM_SW,0 ;extended memory requested?
1002 JE INIT_P1_A ;jump if not
1003
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
1006
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
1018
1019 CALL STEAL_INT19 ;point INT 19H to this VDISK
1020INIT_P1_A:
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. ;
1033; ;
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
1041
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
1045
1046 MOV BL,BPB_MCB ;media control byte
1047
1048 MOV CL,BPB_FATN ;number of FAT copies
1049 XOR CH,CH
1050
1051 MOV AX,BPB_FATSZ ;FAT size in sectors
1052 MUL BPB_SSZ ;* sector size = total FAT bytes
1053
1054 SUB AX,3 ;-3 (FEFFFF stored by code)
1055 MOV DX,AX
1056
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
1061
1062 SUBTTL GET_PARMS Parameter Line Scan
1063 PAGE
1064;-----------------------------------------------------------------------;
1065;GET_PARMS gets the parameters from the CONFIG.SYS statement ;
1066; ;
1067;Register usage: ;
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
1073GET_PARMS PROC ;get parameters from CONFIG.SYS line
1074 PUSH DS ;save DS
1075 LDS SI,RH.RH0_BPBA ;DS:SI point to all after DEVICE=
1076 ;in CONFIG.SYS line
1077 XOR AL,AL ;not at end of line
1078
1079;Skip until first delimiter is found. There may be digits in the path string.
1080
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.
1084
1085GET_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
1089 JZ GET_PARMS_C ;
1090 CMP AL,' '
1091 JE GET_PARMS_C ;
1092 CMP AL,','
1093 JE GET_PARMS_C ;
1094 CMP AL,';'
1095 JE GET_PARMS_C ;
1096 CMP AL,'+'
1097 JE GET_PARMS_C ;
1098 CMP AL,'='
1099 JE GET_PARMS_C ;
1100 CMP AL,TAB
1101 JNE GET_PARMS_A ;skip until delimiter or CR
1102
1103
1104
1105GET_PARMS_C:
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
1109
1110GET_PARMS_D: ;scan for /
1111 CALL GET_PCHAR
1112 JZ GET_PARMS_B ;exit if end of line
1113
1114GET_SLASH: ;check for slash
1115 CMP AL,'/' ;found slash?
1116 JNE GET_PARMS_D ;no, continue scan
1117
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
1123
1124 CALL GET_PCHAR ;get char following E
1125 CMP AL,':' ;is it a delimeter ?
1126 JNE GET_PARMS_D ;not a ':'
1127
1128
1129 CALL GET_MAXSIZE ;get maximum sector size
1130
1131
1132 JMP GET_PARMS_D ;continue forward scan
1133
1134GET_PARMS_E: ;/ found, not 'E'
1135 OR CS:ERR_FLAG,ERR_SWTCH ;indicate invalid switch character
1136 JMP GET_PARMS_D ;continue scan
1137
1138
1139
1140GET_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
1146
1147 CALL GET_NUMBER ;extract digits, convert to binary
1148 MOV CS:BUFF_SIZE,CX ;store buffer size
1149
1150 CALL SKIP_TO_DIGIT ;skip to next digit
1151 JZ GET_PARMS_X ;found EOL, no digits remain
1152
1153 CALL GET_NUMBER ;extract digits, convert to binary
1154 MOV CS:BPB_SSZ,CX ;store sector size
1155
1156 CALL SKIP_TO_DIGIT ;skip to next digit
1157 JZ GET_PARMS_X ;found EOL, no digits remain
1158
1159 CALL GET_NUMBER ;extract digits, convert to binary
1160 MOV CS:BPB_DIRN,CX ;store number of directory entries
1161
1162
1163
1164GET_PARMS_X: ;premature end of line
1165 POP DS ;restore DS
1166 RET
1167
1168
1169
1170GET_MAXSIZE PROC ;get maximum sector size
1171
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
1176 RET ;
1177GET_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
1180 RET
1181GET_MAXSIZE ENDP
1182
1183
1184
1185GET_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?
1194GET_PCHAR_X: ;attempted read past end of line
1195 RET
1196GET_PCHAR ENDP ;returns char in AL
1197
1198
1199CHECK_NUM PROC ;check AL for ASCII digit
1200 CMP AL,'0' ;< '0'?
1201 JB CHECK_NUM_X ;exit if it is
1202
1203 CMP AL,'9' ;> '9'?
1204 JA CHECK_NUM_X ;exit if it is
1205
1206 CMP AL,AL ;set Z flag to indicate numeric
1207CHECK_NUM_X:
1208 RET ;Z set if numeric, NZ if not numeric
1209CHECK_NUM ENDP
1210
1211
1212SKIP_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
1215
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
1219
1220SKIP_TO_DIGIT_X:
1221 CMP AL,0 ;digit found, force NZ
1222 RET
1223SKIP_TO_DIGIT ENDP
1224
1225C10 DW 10
1226GN_ERR DB ? ;zero if no overflow in accumulation
1227
1228GET_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
1231GET_NUMBER_A: ;accumulate next digit
1232 SUB AL,'0' ;convert ASCII to binary
1233 CBW ;clear AH
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
1241 MOV [SI],AL ;
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
1249GET_NUMBER_B:
1250 RET ;number in CX, next char in AL
1251GET_NUMBER ENDP
1252
1253GET_PARMS ENDP
1254
1255 SUBTTL APPLY_DEFAULTS
1256 PAGE
1257;-----------------------------------------------------------------------;
1258; APPLY_DEFAULTS supplies any parameter values that the user ;
1259; failed to specify ;
1260;-----------------------------------------------------------------------;
1261 ASSUME DS:CSEG
1262APPLY_DEFAULTS PROC
1263 XOR AX,AX
1264 CMP BUFF_SIZE,AX ;is buffer size zero?
1265 JNE APPLY_DEFAULTS_A ;no, user specified something
1266
1267 MOV BUFF_SIZE,DFLT_BSIZE ;supply default buffer size
1268 OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted
1269
1270APPLY_DEFAULTS_A:
1271 CMP BPB_SSZ,AX ;is sector size zero?
1272 JNE APPLY_DEFAULTS_B ;no, user specified something
1273
1274 MOV BPB_SSZ,DFLT_SSZ ;supply default sector size
1275 OR ERR_FLAG,ERR_SSZ ;indicate sector size adjusted
1276
1277APPLY_DEFAULTS_B:
1278 CMP BPB_DIRN,AX ;are directory entries zero?
1279 JNE APPLY_DEFAULTS_C ;no, user specified something
1280
1281 MOV BPB_DIRN,DFLT_DIRN ;supply default directory entries
1282 OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
1283
1284APPLY_DEFAULTS_C: ;
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
1289
1290 MOV MAXSEC_TRF,DFLT_ESS ;supply default maximum number of
1291 ;sector to transfer
1292 OR ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted
1293APPLY_DEFAULTS_D:
1294 RET
1295APPLY_DEFAULTS ENDP
1296
1297 SUBTTL DETERMINE_START address of VDISK buffer
1298 PAGE
1299;-----------------------------------------------------------------------;
1300; DETERMINE_START figures out the starting address of the VDISK ;
1301; buffer ;
1302;-----------------------------------------------------------------------;
1303 ASSUME DS:CSEG
1304DETERMINE_START PROC
1305
1306;If extended memory is NOT being used, the VDISK buffer immediately
1307;follows the resident code.
1308
1309;If extended memory IS being used, START_BUFFER_PARA becomes the
1310;end of device driver address passed back to DOS.
1311
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
1315
1316 CMP EM_SW,0 ;is extended memory requested?
1317 JE DETERMINE_START_X ;if not, we're done here
1318
1319;If this is the first extended memory VDISK device driver to be installed,
1320;the start address for I/O is 1 megabyte.
1321
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.
1325
1326;The first extended memory VDISK device driver is located by INT 19H's vector.
1327
1328 MOV FIRST_EM_SW,0FFH ;indicate first VDISK device driver
1329 MOV FIRST_VDISK,CS ;segment addr of first VDISK
1330
1331 PUSH DS ;preserve DS
1332 XOR AX,AX
1333 MOV DS,AX ;set DS = 0
1334 ASSUME DS:INT_VEC
1335
1336 MOV AX,DS:BOOT_VECS ;get segment addr of INT 19H routine
1337 MOV DS,AX ;to DS
1338 ASSUME DS:NOTHING
1339
1340 PUSH CS
1341 POP ES ;set ES = CS
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
1345 ;this VDISK
1346
1347 MOV CX,VOL_LABEL_LEN ;length of volume label
1348 REP CMPSB ;does INT 19H vector point to a VDISK
1349 ;device driver?
1350 JNE DETERMINE_START_A ;jump if this is the first VDISK
1351
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.
1354
1355 MOV CS:FIRST_EM_SW,0 ;indicate not first device driver
1356 MOV CS:FIRST_VDISK,DS ;save pointer to 1st device driver
1357
1358;Copy AVAIL_LO and AVAIL_HI from first VDISK to this VDISK
1359
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
1364
1365DETERMINE_START_A: ;copy AVAIL_LO and AVAIL_HI to START_EM
1366 POP DS ;set DS = CS
1367
1368 MOV SI,OFFSET AVAIL_LO ;source offset
1369 MOV DI,OFFSET START_EM_LO ;destination offset
1370
1371 MOVSW ;move AVAIL_LO to START_EM_LO
1372 MOVSB ;move AVAIL_HI to START_EM_HI
1373DETERMINE_START_X:
1374 RET
1375DETERMINE_START ENDP
1376
1377 SUBTTL VALIDATE parameters
1378 PAGE
1379;-----------------------------------------------------------------------;
1380; VALIDATE adjusts parameters as necessary ;
1381;-----------------------------------------------------------------------;
1382VAL_SSZ_TBL LABEL WORD ;table of valid sector sizes
1383VAL_SSZ_S DW 128 ;smallest valid sector size
1384 DW 256
1385VAL_SSZ_L DW 512 ;largest valid sector size
1386VAL_SSZ_N EQU ($-VAL_SSZ_TBL)/2 ;number of table entries
1387
1388 ASSUME DS:CSEG
1389VALIDATE PROC ;validate parameters
1390 MOV BPB_AUSZ,1 ;initial allocation unit is 1 sector
1391
1392 CALL VAL_BSIZE ;validate buffer size
1393
1394 CALL VAL_SSZ ;validate (adjust if necessary) BPB_SSZ
1395
1396VALIDATE_A:
1397 AND ERR_FLAG,255-ERR_PASS ;indicate nothing changed this pass
1398
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
1403
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
1408
1409 CALL VAL_DIRN ;validate number of directory entries
1410
1411 TEST ERR_FLAG,ERR_PASS ;may have reset sector size
1412 JNZ VALIDATE_A ;recompute directory & FAT sizes
1413
1414 CALL VAL_FAT ;compute FAT entries, validity test
1415
1416 TEST ERR_FLAG,ERR_PASS ;if cluster size altered this pass
1417 JNZ VALIDATE_A ;recompute directory & FAT sizes
1418
1419;Make certain buffer size is large enough to contain:
1420; boot sector(s)
1421; FAT sector(s)
1422; directory sector(s)
1423; at least 1 data cluster
1424
1425 MOV AL,BPB_FATN ;number of FAT copies
1426 CBW ;clear AH
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
1435
1436 CMP DIR_SECTORS,1 ;down to 1 directory sector?
1437 JBE VALIDATE_C ;can't let it go below 1
1438
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
1443
1444 OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
1445 JMP VALIDATE_A ;retry with new directory entries number
1446
1447VALIDATE_C: ;not enough space for any VDISK
1448 OR ERR_FLAG,ERR_SYSSZ
1449VALIDATE_X:
1450 RET
1451
1452 SUBTTL VAL_BSIZE Validate buffer size
1453 PAGE
1454;-----------------------------------------------------------------------;
1455; VAL_BSIZE adjusts the buffer size as necessary ;
1456;-----------------------------------------------------------------------;
1457VAL_BSIZE PROC
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
1462
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
1466 MOV BPB_SSZ,AX
1467 MOV BPB_DIRN,4 ;4 directory entries
1468 RET
1469
1470VAL_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
1473
1474 MOV BUFF_SIZE,AX ;give all available memory
1475 OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted
1476VAL_BSIZE_C:
1477 RET
1478
1479
1480GET_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
1484
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
1488
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
1494 RET
1495
1496GET_MSIZE_2: ;non-extended memory size determination
1497
1498;Compute AX = total system size, - (VDISK end address + 64KB)
1499
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
1510
1511GET_MSIZE_Z:
1512 XOR AX,AX ;indicate no memory available
1513GET_MSIZE_X: ;exit from memory size determination
1514 RET
1515GET_MSIZE ENDP
1516
1517VAL_BSIZE ENDP
1518
1519 SUBTTL VAL_SSZ Validate Sector Size
1520 PAGE
1521;-----------------------------------------------------------------------;
1522; VAL_SSZ validates sector size, adjusting if necessary ;
1523;-----------------------------------------------------------------------;
1524VAL_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
1533
1534DFLT_TRF: ;set default
1535 MOV MAXSEC_TRF,DFLT_ESS ;
1536 MOV BX,MAXSEC_TRF ;
1537 OR CS:ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted
1538
1539VAL_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
1545VAL_SSZ_A:
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
1550
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
1554VAL_SSZ_X:
1555
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.
1558
1559 CMP CS:EM_SW,0 ;extended memory ?
1560 JNE SIZE_DONE ;yes, we are done
1561 XOR DX,DX
1562 MOV AX,0FFFFH ;64KB - 1
1563 DIV BX ;/sector size
1564 MOV MAX_CNT,AX ;max sectors in one move
1565SIZE_DONE:
1566 RET
1567VAL_SSZ ENDP
1568
1569 SUBTTL VAL_DIRN Validate number of directory entries
1570 PAGE
1571;-----------------------------------------------------------------------;
1572; VAL_DIRN validates and adjusts the number of directory entries. ;
1573; ;
1574; Minimum is MIN_DIRN, maximum is MAX_DIRN. If outside these ;
1575; limits, DFLT_DIRN is used. ;
1576; ;
1577; The number of directory entries is rounded upward to fill ;
1578; a sector ;
1579;-----------------------------------------------------------------------;
1580VAL_DIRN PROC
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
1584
1585 CMP AX,MAX_DIRN ;if <= maximum
1586 JBE VAL_DIRN_B ;accept value as provided
1587
1588VAL_DIRN_A:
1589 MOV AX,DFLT_DIRN ;use default directory entries
1590 OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
1591VAL_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
1596
1597 INC AX ;increment directory sectors
1598 OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
1599VAL_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
1605
1606 MOV AX,1 ;use only one directory sector
1607 OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
1608VAL_DIRN_D:
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
1613 RET
1614VAL_DIRN ENDP
1615
1616 SUBTTL VAL_FAT Validate File Allocation Table (FAT)
1617 PAGE
1618;-----------------------------------------------------------------------;
1619;VAL_FAT computes: ;
1620;BPB_FATSZ, the number of sectors required per FAT copy ;
1621; ;
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. ;
1630; ;
1631;This computation is slightly conservative in that the FAT entries ;
1632;necessary to describe the FAT sectors are included in the computation. ;
1633;-----------------------------------------------------------------------;
1634VAL_FAT PROC
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
1642VAL_FAT_A:
1643 XOR DX,DX ;clear DX for division
1644 MOV CL,BPB_AUSZ ;CX = sectors/cluster
1645 XOR CH,CH
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
1651
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
1655
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
1659
1660VAL_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
1664
1665VAL_FAT_C: ;FAT size = 1.5 * number of clusters
1666 MOV CX,AX ;number of clusters
1667 SHL AX,1 ;* 2
1668 ADD AX,CX ;* 3
1669 SHR AX,1 ;* 1.5
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
1677VAL_FAT_X:
1678 RET
1679VAL_FAT ENDP
1680
1681
1682VALIDATE ENDP
1683
1684 SUBTTL COPY_BPB Copy BPB to Boot Record
1685 PAGE
1686;-----------------------------------------------------------------------;
1687; COPY_BPB copies the BIOS Parameter Block (BPB) ;
1688; to the VDISK Boot Record ;
1689;-----------------------------------------------------------------------;
1690 ASSUME DS:CSEG
1691COPY_BPB PROC ;Copy BBP to Boot Record
1692 PUSH DS
1693 POP ES ;set ES = DS
1694
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
1699 RET
1700COPY_BPB ENDP
1701
1702 SUBTTL VERIFY_EXTENDER
1703 PAGE
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;-----------------------------------------------------------------------;
1709
1710
1711 ASSUME DS:CSEG
1712EXT_P210 EQU 0210H ;write to latch expansion bus data
1713 ;read to verify expansion bus data
1714EXT_P213 EQU 0213H ;Expansion Unit status
1715
1716VERIFY_EXTENDER PROC
1717
1718 NOP
1719
1720 MOV DX,EXT_P210 ;Expansion bus data port address
1721
1722 MOV AX,5555H ;set data pattern
1723 OUT DX,AL ;write 55H to control port
1724 PUSH DX
1725 POP DX
1726
1727 JMP SHORT $+2 ;Let the I/O circuits catch up
1728 IN AL,020h ;Clear the CMOS bus drivers!
1729
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
1733
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
1738
1739 JMP SHORT $+2 ;Let the I/O circuits catch up
1740 IN AL,020h ;Clear the CMOS bus drivers!
1741
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
1745
1746;Expansion Unit is present.
1747
1748;Determine what the switch settings should be on the Extender Card
1749
1750 INT MEM_SIZE ;get system memory size in KB in AX
1751 ADD AX,63D ;memory size + 63K
1752 MOV CL,6 ;2^6 = 64
1753 SHR AX,CL ;divide by 64
1754 ;AX is highest segment address
1755 MOV AH,AL ;save number of segments
1756
1757;Read Expander card switch settings
1758
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
1764
1765 CMP AH,AL ;do switches match memory size?
1766 JE VERIFY_EXTENDER_X ;yes, exit normally
1767
1768 OR ERR_FLAG,ERR_EXTSW ;indicate switch settings are wrong
1769
1770VERIFY_EXTENDER_X:
1771 RET
1772VERIFY_EXTENDER ENDP
1773
1774 SUBTTL UPDATE_AVAIL
1775 PAGE
1776;-----------------------------------------------------------------------;
1777; UPDATE_AVAIL updates the address of the first byte in extended ;
1778; memory not used by any VDISK buffer ;
1779;-----------------------------------------------------------------------;
1780UPDATE_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
1783
1784 PUSH DS
1785 MOV DS,FIRST_VDISK ;set DS to first VDISK
1786 ADD DS:AVAIL_LO,AX ;update first available byte location
1787 ADC DS:AVAIL_HI,DL
1788 POP DS
1789 RET
1790UPDATE_AVAIL ENDP
1791
1792 SUBTTL FORMAT_VDISK
1793 PAGE
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;-----------------------------------------------------------------------;
1799
1800MOVE_RH DB MOVE_RH_L ;length of request header
1801 DB 0 ;sub unit
1802 DB 8 ;output operation
1803 DW 0 ;status
1804 DQ ? ;reserved for DOS
1805 DB ? ;media descriptor byte
1806MOVE_RHO DW ? ;offset of data transfer address
1807MOVE_RHS DW ? ;segment of data transfer address
1808MOVE_RHCNT DW ? ;count of sectors to transfer
1809 DW 0 ;starting sector number
1810MOVE_RH_L EQU $-MOVE_RH ;length of request header
1811
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;-----------------------------------------------------------------------;
1817FORMAT_VDISK PROC ;format boot record, FATs and directory
1818
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
1825
1826 DIV WPARA_SIZE ;split into segment(AX)&offset(DX)
1827 MOV MOVE_RHS,AX ;save in Request Header for move
1828 MOV MOVE_RHO,DX
1829
1830 MOV DI,DX ;offset to DI
1831 MOV ES,AX ;segment to ES
1832
1833;copy the boot record
1834
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)
1840
1841;format the FAT(s)
1842
1843 MOV CL,BPB_FATN ;number of FATs
1844 XOR CH,CH
1845FORMAT_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
1850 STOSW
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
1855 XOR AX,AX
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
1859
1860;Format the directory
1861
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
1869 XOR AX,AX
1870 REP STOSB ;clear directory to nulls
1871 RET
1872FORMAT_VDISK ENDP
1873
1874 SUBTTL MOVE_VDISK
1875 PAGE
1876;-----------------------------------------------------------------------;
1877; MOVE_VDISK moves the formatted boot sector, FAT, and directory ;
1878; into extended memory. ;
1879;-----------------------------------------------------------------------;
1880
1881MOVE_VDISK PROC
1882 MOV AL,BPB_FATN ;number of FAT copies
1883 CBW ;clear AH
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
1888
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
1892 POP DS
1893 RET
1894MOVE_VDISK ENDP
1895
1896 SUBTTL UPDATE_BOOT
1897 PAGE
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;-----------------------------------------------------------------------;
1903UPDATE_BOOT PROC
1904 PUSH DS
1905 MOV DS,FIRST_VDISK ;set DS to first VDISK
1906 MOV AX,DS:AVAIL_LO ;24-bit end address of all VDISKs
1907 MOV DL,DS:AVAIL_HI
1908 XOR DH,DH
1909 POP DS
1910 DIV C1024 ;address / 1024
1911 MOV BOOT_EM,AX ;store in temporary location
1912
1913 MOV AX,2 ;length of block move is 2 bytes
1914 MOV TGT.DESC_LMT,AX
1915 MOV SRC.DESC_LMT,AX
1916
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
1922
1923 MOV SRC.DESC_BASEL,AX ;store source base address
1924 MOV SRC.DESC_BASEH,DL
1925
1926 MOV TGT.DESC_BASEL,BOOT_EM_OFF ;offset of BOOT_EM
1927 MOV TGT.DESC_BASEH,10H ;1 megabyte
1928
1929 MOV CX,1 ;move 1 word
1930
1931 PUSH CS
1932 POP ES
1933 MOV SI,OFFSET GDT ;ES:DI point to global descriptor table
1934
1935 MOV AH,EM_BLKMOVE ;function code
1936 INT EM_INT ;move BOOT_EM to 10 001EH
1937 RET
1938UPDATE_BOOT ENDP
1939
1940 SUBTTL STEAL_INT19
1941 PAGE
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 ;
1946; addresses. ;
1947;-----------------------------------------------------------------------;
1948STEAL_INT19 PROC
1949 PUSH DS
1950 XOR AX,AX
1951 MOV DS,AX ;set DS = 0
1952 ASSUME DS:INT_VEC
1953 CLI ;disable interrupts
1954 LES DI,DS:BOOT_VEC ;get original vector's content
1955 MOV CS:INTV19O,DI ;save original vector
1956 MOV CS:INTV19S,ES
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
1960 POP DS ;restore DS
1961 RET
1962STEAL_INT19 ENDP
1963
1964 SUBTTL FILL_RH Fill in Request Header
1965 PAGE
1966;-----------------------------------------------------------------------;
1967; FILL_RH fills in the Request Header returned to DOS ;
1968;-----------------------------------------------------------------------;
1969 ASSUME DS:CSEG
1970FILL_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
1978
1979 MOV DX,CX ;end of code segment addr
1980FILL_RH_A: ;DX is proper ending segment address
1981 MOV AL,1 ;number of units
1982 test CS:err_flag2,err_baddos
1983 jnz dont_install
1984
1985 TEST ERR_FLAG,ERR_SYSSZ+ERR_EXTSW ;if bypassing install
1986 JZ FILL_RH_B ;jump if installing driver
1987
1988dont_install:
1989 MOV DX,CS ;segment of end address
1990 XOR AL,AL ;number of units is zero
1991FILL_RH_B:
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
1999 POP DS ;restore DS
2000 RET
2001FILL_RH ENDP
2002
2003 SUBTTL WRITE_MESSAGES and associated routines
2004 PAGE
2005;-----------------------------------------------------------------------;
2006; WRITE_MESSAGE writes a series of messages to the standard ;
2007; output device showing the VDISK parameter values actually used. ;
2008;-----------------------------------------------------------------------;
2009
2010CHAR4 DB 'nnnn$' ;build 4 ASCII decimal digits
2011
2012 ASSUME DS:CSEG
2013WRITE_MESSAGES PROC ;display all messages
2014
2015 MSG IMSG ;'VDISK virtual disk $'
2016
2017 test CS:err_flag2,err_baddos
2018 jz check_dos_version
2019
2020 msg errm8
2021 ret
2022
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.
2026
2027check_dos_version:
2028 MOV AH,DOS_VERS ;get DOS version call
2029 INT DOS ;invoke DOS
2030
2031 CMP AL,3 ;DOS Version 3 or greater?
2032 JB WRITE_MESSAGES_A ;no, bypass drive letter
2033
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
2038 POP DS ;restore DS
2039
2040 MOV AH,DOS_PCHR ;function code to write character in DL
2041 INT DOS ;display drive letter
2042
2043 MOV DL,':' ;display trailing colon
2044 INT DOS
2045
2046WRITE_MESSAGES_A:
2047 MSG MSGCRLF ;end the first line
2048
2049;If any of the user specified values has been adjusted, issue an
2050;appropriate message
2051
2052 TEST ERR_FLAG,ERR_BSIZE ;was buffersize adjusted?
2053 JZ WRITE_MESSAGES_B ;if not, skip message
2054
2055 MSG ERRM1 ;buffer size adjusted
2056
2057WRITE_MESSAGES_B:
2058 TEST ERR_FLAG,ERR_SSZ ;was sector size adjusted?
2059 JZ WRITE_MESSAGES_C ;if not, skip message
2060
2061 MSG ERRM2 ;sector size adjusted
2062
2063WRITE_MESSAGES_C:
2064 TEST ERR_FLAG,ERR_DIRN ;were directory entries adjusted?
2065 JZ WRITE_MESSAGES_D0 ;if not, skip message
2066
2067 MSG ERRM3 ;directory entries adjusted
2068
2069WRITE_MESSAGES_D0:
2070 TEST ERR_FLAG,ERR_ESIZE ;was transfer size adjusted?
2071 JZ WRITE_MESSAGES_D ;if not, skip message
2072
2073 MSG ERRM7 ;transfer size adjusted
2074
2075WRITE_MESSAGES_D:
2076 TEST ERR_FLAG,ERR_SWTCH ;was an invalid switch character found?
2077 JZ WRITE_MESSAGES_E ;if not, skip message
2078
2079 MSG ERRM5 ;invalid switch character
2080
2081WRITE_MESSAGES_E:
2082 TEST ERR_FLAG,ERR_SYSSZ ;is system size too small to install?
2083 JZ WRITE_MESSAGES_F ;if not, bypass error message
2084
2085 MSG ERRM4 ;too large for system storage
2086 RET ;skip messages showing adjusted sizes
2087
2088WRITE_MESSAGES_F:
2089 TEST ERR_FLAG,ERR_EXTSW ;extender card switches wrong?
2090 JZ WRITE_MESSAGES_G ;if not, bypass error message
2091
2092 MSG ERRM6 ;extender card switches wrong msg
2093 RET ;skip remaining messages
2094
2095WRITE_MESSAGES_G: ;display adjusted size messages
2096 MSG MSG1 ;buffer size:
2097
2098 MOV DX,BUFF_SIZE ;buffer size in binary
2099 CALL STOR_SIZE ;convert binary to ASCII decimal
2100 MSG CHAR4 ;print 4 decimals
2101 MSG MSG2 ;KB,CR,LF
2102
2103 MSG MSG3 ;sector size:
2104 MOV DX,BPB_SSZ
2105 CALL STOR_SIZE ;convert binary to ASCII decimal
2106 MSG CHAR4 ;print 4 decimals
2107 MSG MSGCRLF ;finish off line
2108
2109 MSG MSG4 ;directory entries:
2110 MOV DX,BPB_DIRN ;number of directory entries
2111 CALL STOR_SIZE
2112 MSG CHAR4 ;print 4 decimals
2113 MSG MSGCRLF ;finish off the line
2114
2115 CMP CS:EM_SW,0 ;extended memory ?
2116 JE END_LINE ;
2117 MSG MSG5 ;transfer size:
2118 MOV DX,MAXSEC_TRF
2119 CALL STOR_SIZE ;convert binary to ASCII decimal
2120 MSG CHAR4 ;print 4 decimals
2121 MSG MSGCRLF ;finish off line
2122
2123END_LINE:
2124 MSG MSGCRLF ;one more blank line to set it off
2125 RET ;return to INIT_P1
2126
2127;SHOW_MSG displays a string at DS:DX on the standard output device
2128;String is terminated by a $
2129
2130SHOW_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
2134 POP AX ;restore AX
2135 RET
2136SHOW_MSG ENDP
2137
2138;STOR_SIZE converts the content of DX to 4 decimal characters in CHAR4
2139;(DX must be <= 9999)
2140
2141STOR_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
2145STOR_SIZE_B:
2146 SHL DX,1 ;shift high bit into carry
2147 ADC AL,AL ;double AL, carry in
2148 DAA ;adjust for packed decimal
2149 XCHG AL,AH
2150 ADC AL,AL ;double high byte, carry in
2151 DAA
2152 XCHG AL,AH
2153 LOOP STOR_SIZE_B ;AX contains 4 packed decimal digits
2154
2155 PUSH CS
2156 POP ES ;point ES:DI to output string
2157 MOV DI,OFFSET CHAR4
2158
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
2166STOR_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
2169 SHR DL,1
2170 SHR DL,1
2171 SHR DL,1
2172 CALL STOR_SIZE_1 ;display low nibble of DL
2173 MOV DL,DH ;get low decimal from pair
2174STOR_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
2178STOR_SIZE_Z:
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
2185 RET
2186STOR_SIZE ENDP
2187
2188WRITE_MESSAGES ENDP
2189
2190INIT_P1 ENDP ;end of INIT part one
2191
2192;-----------------------------------------------------------------------;
2193; VDISK Message definitions ;
2194;-----------------------------------------------------------------------;
2195
2196IMSG DB 'VDISK virtual disk ','$'
2197
2198ERRM1 DB ' Buffer size adjusted',CR,LF,'$'
2199ERRM2 DB ' Sector size adjusted',CR,LF,'$'
2200ERRM3 DB ' Directory entries adjusted',CR,LF,'$'
2201ERRM4 DB ' VDISK not installed - insufficient memory'
2202 DB CR,LF,CR,LF,BEL,'$'
2203ERRM5 DB ' Invalid switch character',CR,LF,'$'
2204ERRM6 DB ' VDISK not installed - Extender Card switches'
2205 DB CR,LF
2206 DB ' do not match system memory size'
2207 DB CR,LF,CR,LF,BEL,'$'
2208ERRM7 DB ' Transfer size adjusted',CR,LF,'$'
2209ERRM8 DB ' VDISK not installed - Incorrect DOS version'
2210 DB CR,LF,CR,LF,BEL,'$'
2211
2212MSG1 DB ' Buffer size: $'
2213MSG2 DB ' KB'
2214MSGCRLF DB CR,LF,'$'
2215MSG3 DB ' Sector size: $'
2216MSG4 DB ' Directory entries: $'
2217MSG5 DB ' Transfer size: $'
2218MSGEND LABEL BYTE ; End of message text
2219
2220CSEG ENDS
2221 END