1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
|
PAGE ,132
TITLE VDISK - Virtual Disk Device Driver
;VDISK simulates a disk drive, using Random Access Memory as the storage medium.
;This program is meant to serve as an example of a device driver. It does not
;reflect the current level of VDISK.SYS.
;(C) Copyright 1988 Microsoft
;Licensed Material - Program Property of Microsoft
;Add the following statement to CONFIG.SYS
; DEVICE=[d:][path]VDISK.SYS bbb sss ddd [/E:m]
; where: bbb is the desired buffer size (in kilobytes)
; minimum 1KB, maximum is size of available memory,
; default is 64KB.
; VDISK will leave at least 64KB of available memory,
; although subsequent device drivers (other than VDISK)
; other programs that make themselves resident, and
; COMMAND.COM will result in less than 64KB as shown
; by CHKDSK.
; Must be large enough for 1 boot sector + FAT sectors
; + 1 directory sector + at least 1 data cluster,
; or the device driver won't be installed.
; sss is the desired sector size (in bytes)
; 128, 256, or 512, default is 128.
; Will be adjusted if number of FAT entries > 0FE0H
; ddd is the desired number of directory entries
; Minimum 2, maximum 512, default 64.
; Will be rounded upward to sector size boundary.
; /E may only be used if extended memory above 1 megabyte
; is to be used. INT 15H functions 87H and 88H are used
; to read and write this extended memory.
; The m parameter in the /E option specifies the maximum
; number of sectors that the VDISK will transfer at a time.
; Optional values are 1,2,3,4,5,6,7 or 8 sectors, the default
; is 8 sectors.
; Brackets indicate optional operands.
; Samples:
; DEVICE=\path\VDISK.SYS 160 512 64
; results in a 160KB VDISK, with 512 byte sectors, 64 directory entries
; DEVICE=VDISK.SYS Buffersize 60 Sectorsize 128 Directory entries 32
; (since only numbers are interpreted, you may comment the line with
; non-numeric characters)
SUBTTL Structure Definitions
PAGE
;-----------------------------------------------------------------------;
; Request Header (Common portion) ;
;-----------------------------------------------------------------------;
RH EQU DS:[BX] ;addressability to Request Header structure
RHC STRUC ;fields common to all request types
DB ? ;length of Request Header (including data)
DB ? ;unit code (subunit)
RHC_CMD DB ? ;command code
RHC_STA DW ? ;status
DQ ? ;reserved for DOS
RHC ENDS ;end of common portion
CMD_INPUT EQU 4 ;RHC_CMD is INPUT request
;status values for RHC_STA
STAT_DONE EQU 01H ;function complete status (high order byte)
STAT_CMDERR EQU 8003H ;invalid command code error
STAT_CRC EQU 8004H ;CRC error
STAT_SNF EQU 8008H ;sector not found error
STAT_BUSY EQU 0200H ;busy bit (9) for Removable Media call
;-----------------------------------------------------------------------;
; Request Header for INIT command ;
;-----------------------------------------------------------------------;
RH0 STRUC
DB (TYPE RHC) DUP (?) ;common portion
RH0_NUN DB ? ;number of units
;set to 1 if installation succeeds,
;set to 0 to cause installation failure
RH0_ENDO DW ? ;offset of ending address
RH0_ENDS DW ? ;segment of ending address
RH0_BPBO DW ? ;offset of BPB array address
RH0_BPBS DW ? ;segment of BPB array address
RH0_DRIV DB ? ;drive code (DOS 3 only)
RH0 ENDS
RH0_BPBA EQU DWORD PTR RH0_BPBO ;offset/segment of BPB array address
;Note: RH0_BPBA at entry to INIT points to all after DEVICE= on CONFIG.SYS stmt
;-----------------------------------------------------------------------;
; Request Header for MEDIA CHECK Command ;
;-----------------------------------------------------------------------;
RH1 STRUC
DB (TYPE RHC) DUP (?) ;common portion
DB ? ;media descriptor
RH1_RET DB ? ;return information
RH1 ENDS
;-----------------------------------------------------------------------;
; Request Header for BUILD BPB Command ;
;-----------------------------------------------------------------------;
RH2 STRUC
DB (TYPE RHC) DUP(?) ;common portion
DB ? ;media descriptor
DW ? ;offset of transfer address
DW ? ;segment of transfer address
RH2_BPBO DW ? ;offset of BPB table address
RH2_BPBS DW ? ;segment of BPB table address
RH2 ENDS
;-----------------------------------------------------------------------;
; Request Header for INPUT, OUTPUT, and OUTPUT with verify ;
;-----------------------------------------------------------------------;
RH4 STRUC
DB (TYPE RHC) DUP (?) ;common portion
DB ? ;media descriptor
RH4_DTAO DW ? ;offset of transfer address
RH4_DTAS DW ? ;segment of transfer address
RH4_CNT DW ? ;sector count
RH4_SSN DW ? ;starting sector number
RH4 ENDS
RH4_DTAA EQU DWORD PTR RH4_DTAO ;offset/segment of transfer address
;-----------------------------------------------------------------------;
; Segment Descriptor (part of Global Descriptor Table) ;
;-----------------------------------------------------------------------;
DESC STRUC ;data segment descriptor
DESC_LMT DW 0 ;segment limit (length)
DESC_BASEL DW 0 ;bits 15-0 of physical address
DESC_BASEH DB 0 ;bits 23-16 of physical address
DB 0 ;access rights byte
DW 0 ;reserved
DESC ENDS
SUBTTL Equates and Macro Definitions
PAGE
MEM_SIZE EQU 12H ;BIOS memory size determination INT
;returns system size in KB in AX
EM_INT EQU 15H ;extended memory BIOS interrupt INT
EM_BLKMOVE EQU 87H ;block move function
EM_MEMSIZE EQU 88H ;memory size determination in KB
BOOT_INT EQU 19H ;bootstrap DOS
DOS EQU 21H ;DOS request INT
DOS_PCHR EQU 02H ;print character function
DOS_PSTR EQU 09H ;print string function
DOS_VERS EQU 30H ;get DOS version
TAB EQU 09H ;ASCII tab
LF EQU 0AH ;ASCII line feed
CR EQU 0DH ;ASCII carriage return
BEL EQU 07H ;ASCII bell
PARA_SIZE EQU 16 ;number of bytes in one 8088 paragraph
DIR_ENTRY_SIZE EQU 32 ;number of bytes per directory entry
MAX_FATE EQU 0FE0H ;largest number of FAT entries allowed
;default values used if parameters are omitted
DFLT_BSIZE EQU 64 ;default VDISK buffer size (KB)
DFLT_SSZ EQU 128 ;default sector size
DFLT_DIRN EQU 64 ;default number of directory entries
DFLT_ESS EQU 8 ;default maximum sectors to transfer
MIN_DIRN EQU 2 ;minimum number of directory entries
MAX_DIRN EQU 512 ;maximum number of directory entries
STACK_SIZE EQU 512 ;length of stack during initialization
;-----------------------------------------------------------------------;
; MSG invokes the console message subroutine ;
;-----------------------------------------------------------------------;
MSG MACRO TEXT
PUSH DX ;;save DX across call
MOV DX,OFFSET TEXT ;;point to message
CALL SHOW_MSG ;;issue message
POP DX
ENDM
SUBTTL Resident Data Area
PAGE
;-----------------------------------------------------------------------;
; Map INT 19H vector in low storage ;
;-----------------------------------------------------------------------;
INT_VEC SEGMENT AT 00H
ORG 4*BOOT_INT
BOOT_VEC LABEL DWORD
BOOT_VECO DW ? ;offset
BOOT_VECS DW ? ;segment
INT_VEC ENDS
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG
;-----------------------------------------------------------------------;
; Resident data area. ;
; ;
; All variables and constants required after initialization ;
; part one are defined here. ;
;-----------------------------------------------------------------------;
START EQU $ ;begin resident VDISK data & code
;DEVICE HEADER - must be at offset zero within device driver
DD -1 ;becomes pointer to next device header
DW 0800H ;attribute (IBM format block device)
;supports OPEN/CLOSE/RM calls
DW OFFSET STRATEGY ;pointer to device "strategy" routine
DW OFFSET IRPT ;pointer to device "interrupt handler"
DB 1 ;number of block devices
DB 7 DUP (?) ;7 byte filler (remainder of 8-byte name)
;END OF DEVICE HEADER
;This volume label is placed into the directory of the new VDISK
;This constant is also used to determine if a previous extended memory VDISK
;has been installed.
VOL_LABEL DB 'VDISK ' ;00-10 volume name (shows program level)
DB 28H ;11-11 attribute (volume label)
DT 0 ;12-21 reserved
DW 6000H ;22-23 time=12:00 noon
DW 0986H ;24-25 date=12/06/84
VOL_LABEL_LEN EQU $-VOL_LABEL ;length of volume label
;The following field, in the first extended memory VDISK device driver,
;is the 24-bit address of the first free byte of extended memory.
;This address is not in the common offset/segment format.
;The initial value, 10 0000H, is 1 megabyte.
AVAIL_LO DW 0 ;address of first free byte of
AVAIL_HI DB 10H ;extended memory
;The INT 19H vector is "stolen" by the first VDISK installed in extended memory.
;The original content of the interrupt vector is saved here.
INTV19 LABEL DWORD
INTV19O DW ? ;offset
INTV19S DW ? ;segment
PARAS_PER_SECTOR DW ? ;number of 16-byte paragraphs in one sector
START_BUFFER_PARA DW ? ;segment address of start of VDISK buffer
;for extended memory, this segment address
;is the end of the VDISK device driver.
EM_SW DB 0 ;non-zero if Extended Memory
EM_STAT DW 0 ;AX from last unsuccessful extended memory I/O
START_EM_LO DW ? ;24-bit address of start of VDISK buffer
START_EM_HI DB ? ;(extended memory only)
WPARA_SIZE DW PARA_SIZE ;number of bytes in one paragraph
MAX_CNT DW ? ;(0FFFFH/BPB_SSZ) truncated, the maximum
;number of sectors that can be transferred
;without worrying about 64KB wrap
SECT_LEFT DW ? ;sectors left to transfer
IO_SRCA LABEL DWORD ;offset/segment of source
IO_SRCO DW ? ;offset
IO_SRCS DW ? ;segment
IO_TGTA LABEL DWORD ;offset/segment of target
IO_TGTO DW ? ;offset
IO_TGTS DW ? ;segment
;-----------------------------------------------------------------------;
; BIOS Parameter Block (BPB) ;
;-----------------------------------------------------------------------;
;This is where the characteristics of the virtual disk are established.
;A copy of this block is moved into the boot record of the virtual disk.
;DEBUG can be used to read sector zero of the virtual disk to examine the
;boot record copy of this block.
BPB LABEL BYTE ;BIOS Parameter Block (BPB)
BPB_SSZ DW 0 ;number of bytes per disk sector
BPB_AUSZ DB 1 ;sectors per allocation unit
BPB_RES DW 1 ;number of reserved sectors (for boot record)
BPB_FATN DB 1 ;number of File Allocation Table (FAT) copies
BPB_DIRN DW 0 ;number of root directory entries
BPB_SECN DW 1 ;total number of sectors
;computed from buffer size and sector size
;(this includes reserved, FAT, directory,
;and data sectors)
BPB_MCB DB 0FEH ;media descriptor byte
BPB_FATSZ DW 1 ;number of sectors occupied by a single FAT
;computed from BPBSSZ and BPBSECN
BPB_LEN EQU $-BPB ;length of BIOS parameter block
BPB_PTR DW BPB ;BIOS Parameter Block pointer array (1 entry)
;-----------------------------------------------------------------------;
; Request Header (RH) address, saved here by "strategy" routine ;
;-----------------------------------------------------------------------;
RH_PTRA LABEL DWORD
RH_PTRO DW ? ;offset
RH_PTRS DW ? ;segment
;-----------------------------------------------------------------------;
; Global Descriptor Table (GDT), used for extended memory moves ;
;-----------------------------------------------------------------------;
;Access Rights Byte (93H) is
; P=1 (segment is mapped into physical memory)
; E=0 (data segment descriptor)
; D=0 (grow up segment, offsets must be <= limit)
; W=1 (data segment may be written into)
; DPL=0 (privilege level 0)
GDT LABEL BYTE ;begin global descriptor table
DESC <> ;dummy descriptor
DESC <> ;descriptor for GDT itself
SRC DESC <,,,93H,> ;source descriptor
TGT DESC <,,,93H,> ;target descriptor
DESC <> ;BIOS CS descriptor
DESC <> ;stack segment descriptor
SUBTTL INT 19H (boot) interrupt handler
PAGE
;-----------------------------------------------------------------------;
; INT 19H Interrupt Handler routine ;
;-----------------------------------------------------------------------;
;The INT 19H vector is altered by VDISK initialization to point to this
;routine within the first extended memory VDISK device driver.
;The vector points to the device driver so that subsequent VDISKs installed
;in extended memory can find the first one to determine what memory has
;already been allocated to VDISKs.
;This routine restores the original INT 19H vector's content, then jumps
;to the original routine.
;INT 19H, the "Boot" INT, is always altered when DOS is booted.
;This routine is entered with interrupts disabled.
VDISK_INT19 PROC ;INT 19H received
PUSH DS ;save registers we're going to alter
PUSH AX
XOR AX,AX
MOV DS,AX ;set DS = 0
ASSUME DS:INT_VEC
MOV AX,CS:INTV19O ;get offset of saved vector
MOV DS:BOOT_VECO,AX ;store offset in interrupt vector
MOV AX,CS:INTV19S ;get segment of saved vector
MOV DS:BOOT_VECS,AX ;store segment in interrupt vector
POP AX
POP DS
JMP CS:INTV19 ;go to original interrupt routine
VDISK_INT19 ENDP
ASSUME DS:NOTHING
SUBTTL Device Strategy & interrupt entry points
PAGE
;-----------------------------------------------------------------------;
; Device "strategy" entry point ;
; ;
; Retain the Request Header address for use by Interrupt routine ;
;-----------------------------------------------------------------------;
STRATEGY PROC FAR
MOV CS:RH_PTRO,BX ;offset
MOV CS:RH_PTRS,ES ;segment
RET
STRATEGY ENDP
;-----------------------------------------------------------------------;
; Table of command processing routine entry points ;
;-----------------------------------------------------------------------;
CMD_TABLE LABEL WORD
DW OFFSET INIT_P1 ; 0 - Initialization
DW OFFSET MEDIA_CHECK ; 1 - Media check
DW OFFSET BLD_BPB ; 2 - Build BPB
DW OFFSET INPUT_IOCTL ; 3 - IOCTL input
DW OFFSET INPUT ; 4 - Input
DW OFFSET INPUT_NOWAIT ; 5 - Non destructive input no wait
DW OFFSET INPUT_STATUS ; 6 - Input status
DW OFFSET INPUT_FLUSH ; 7 - Input flush
DW OFFSET OUTPUT ; 8 - Output
DW OFFSET OUTPUT_VERIFY ; 9 - Output with verify
DW OFFSET OUTPUT_STATUS ;10 - Output status
DW OFFSET OUTPUT_FLUSH ;11 - Output flush
DW OFFSET OUTPUT_IOCTL ;12 - IOCTL output
DW OFFSET DEVICE_OPEN ;13 - Device OPEN
DW OFFSET DEVICE_CLOSE ;14 - Device CLOSE
MAX_CMD EQU ($-CMD_TABLE)/2 ;highest valid command follows
DW OFFSET REMOVABLE_MEDIA ;15 - Removable media
;-----------------------------------------------------------------------;
; Device "interrupt" entry point ;
;-----------------------------------------------------------------------;
IRPT PROC FAR ;device interrupt entry point
PUSH DS ;save all registers Revised
PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DI
PUSH SI
;BP isn't used, so it isn't saved
CLD ;all moves forward
LDS BX,CS:RH_PTRA ;get RH address passed to "strategy" into DS:BX
MOV AL,RH.RHC_CMD ;command code from Request Header
CBW ;zero AH (if AL > 7FH, next compare will
;catch that error)
CMP AL,MAX_CMD ;if command code is too high
JA IRPT_CMD_HIGH ;jump to error routine
MOV DI,OFFSET IRPT_CMD_EXIT ;return addr from command processor
PUSH DI ;push return address onto stack
;command routine issues "RET"
ADD AX,AX ;double command code for table offset
MOV DI,AX ;put into index register for JMP
XOR AX,AX ;initialize return to "no error"
;At entry to command processing routine:
; DS:BX = Request Header address
; CS = VDISK code segment address
; AX = 0
; top of stack is return address, IRPT_CMD_EXIT
JMP CS:CMD_TABLE[DI] ;call routine to handle the command
IRPT_CMD_ERROR: ;CALLed for unsupported character mode commands
INPUT_IOCTL: ;IOCTL input
INPUT_NOWAIT: ;Non-destructive input no wait
INPUT_STATUS: ;Input status
INPUT_FLUSH: ;Input flush
OUTPUT_IOCTL: ;IOCTL output
OUTPUT_STATUS: ;Output status
OUTPUT_FLUSH: ;Output flush
POP AX ;pop return address off stack
IRPT_CMD_HIGH: ;JMPed to if RHC_CMD > MAX_CMD
MOV AX,STAT_CMDERR ;"invalid command" and error
IRPT_CMD_EXIT: ;return from command routine
;AX = value to OR into status word
LDS BX,CS:RH_PTRA ;restore DS:BX as Request Header pointer
OR AH,STAT_DONE ;add "done" bit to status word
MOV RH.RHC_STA,AX ;store status into request header
POP SI ;restore registers
POP DI
POP DX
POP CX
POP BX
POP AX
POP ES
POP DS
RET
IRPT ENDP
SUBTTL Command Processing routines
PAGE
;-----------------------------------------------------------------------;
; Command Code 1 - Media Check ;
; At entry, DS:BX point to request header, AX = 0 ;
;-----------------------------------------------------------------------;
MEDIA_CHECK PROC
MOV RH.RH1_RET,1 ;indicate media not changed
RET ;AX = zero, no error
MEDIA_CHECK ENDP
;-----------------------------------------------------------------------;
; Command Code 2 - Build BPB ;
; At entry, DS:BX point to request header, AX = 0 ;
;-----------------------------------------------------------------------;
BLD_BPB PROC
MOV RH.RH2_BPBO,OFFSET BPB ;return pointer to our BPB
MOV RH.RH2_BPBS,CS
RET ;AX = zero, no error
BLD_BPB ENDP
;-----------------------------------------------------------------------;
; Command Code 13 - Device Open ;
; Command Code 14 - Device Close ;
; Command Code 15 - Removable media ;
; At entry, DS:BX point to request header, AX = 0 ;
;-----------------------------------------------------------------------;
REMOVABLE_MEDIA PROC
MOV AX,STAT_BUSY ;set status bit 9 (busy)
;indicating non-removable media
DEVICE_OPEN: ;NOP for device open
DEVICE_CLOSE: ;NOP for device close
RET
REMOVABLE_MEDIA ENDP ;fall thru to return
;-----------------------------------------------------------------------;
; Command Code 4 - Input ;
; Command Code 8 - Output ;
; Command Code 9 - Output with verify ;
; At entry, DS:BX point to request header, AX = 0 ;
;-----------------------------------------------------------------------;
INOUT PROC
INPUT:
OUTPUT:
OUTPUT_VERIFY:
;Make sure I/O is entirely within the VDISK sector boundaries
MOV CX,CS:BPB_SECN ;get total sector count
MOV AX,RH.RH4_SSN ;starting sector number
CMP AX,CX ;can't exceed total count
JA INOUT_E1 ;jump if start > total
ADD AX,RH.RH4_CNT ;start + sector count
CMP AX,CX ;can't exceed total count
JNA INOUT_A ;jump if start + count <= total
INOUT_E1: ;I/O not within VDISK sector boundaries
MOV RH.RH4_CNT,0 ;set sectors transferred to zero
MOV AX,STAT_SNF ;indicate 'Sector not found' error
RET ;return with error status in AX
INOUT_A: ;I/O within VDISK bounds
MOV AX,RH.RH4_CNT ;get sector count
MOV CS:SECT_LEFT,AX ;save as sectors left to process
CMP CS:EM_SW,0 ;extended memory mode?
JNE INOUT_EM ;jump to extended memory I/O code
;Compute offset and segment of VDISK buffer for starting segment in CX:SI
MOV AX,RH.RH4_SSN ;starting sector number
MUL CS:PARAS_PER_SECTOR ;* length of one sector in paragraphs
ADD AX,CS:START_BUFFER_PARA ;+ segment of VDISK buffer sector 0
MOV CX,AX ;segment address to CX
XOR SI,SI ;offset is zero
;Compute address of caller's Data Transfer Addr in DX:AX with smallest offset,
;so that there is no possibility of overflowing a 64KB boundary moving MAX_CNT
;sectors.
MOV AX,PARA_SIZE ;16
MUL RH.RH4_DTAS ;* segment of caller's DTA in DX,AX
ADD AX,RH.RH4_DTAO ;+ offset of caller's DTA
ADC DL,0 ;carry in from addition
DIV CS:WPARA_SIZE ;AX is segment of caller's DTA
;DX is smallest offset possible
;AX:DX = DTA address
;AX:DX is caller's DTA segment:offset, CX:SI is VDISK buffer segment:offset
;If this is an OUTPUT request, exchange the source and target addresses
CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
JE INOUT_B ;jump if INPUT operation
XCHG AX,CX ;swap source and target segment
XCHG DX,SI ;swap source and target offset
INOUT_B: ;CX:SI is source, AX:DX is target
MOV CS:IO_SRCS,CX ;save source segment
MOV CS:IO_SRCO,SI ;save source offset
MOV CS:IO_TGTS,AX ;save target segment
MOV CS:IO_TGTO,DX ;save target offset
JMP SHORT INOUT_E ;AX := SECT_LEFT, test for zero
INOUT_C: ;SECT_LEFT in AX, non-zero
; Compute number of sectors to transfer in a single move,
; AX = minimum of (SECT_LEFT, MAX_CNT)
; MAX_CNT is the maximum number of sectors that can be moved without
; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
MOV CX,CS:MAX_CNT ;MAX sectors with one move
CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
JBE INOUT_D ;then move SECT_LEFT sectors
MOV AX,CX ;else move MAX_CNT sectors
INOUT_D:
SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move
;Move AX sectors from source to target
MUL CS:BPB_SSZ ;sectors * sector size = byte count
;(cannot overflow into DX)
SHR AX,1 ;/2 = word count
MOV CX,AX ;word count to CX for REP MOVSW
LDS SI,CS:IO_SRCA ;source segment/offset to DS:SI
LES DI,CS:IO_TGTA ;target segment/offset to ES:DI
REP MOVSW ;move MOV_CNT sectors
;Update source and target paragraph addresses
;AX has number of words moved
SHR AX,1 ;words moved / 8 = paragraphs moved
SHR AX,1
SHR AX,1
ADD CS:IO_SRCS,AX ;add paragraphs moved to source segment
ADD CS:IO_TGTS,AX ;add paragraphs moved to target segment
;Determine if more sectors need to be transferred
INOUT_E: ;do while SECT_LEFT <> zero
MOV AX,CS:SECT_LEFT ;get sectors left to transfer
OR AX,AX ;set flags
JNZ INOUT_C ;go back to transfer some sectors
RET ;AX = zero, all sectors transferred
SUBTTL Extended Memory I/O routine
PAGE
;-----------------------------------------------------------------------;
; Extended Memory I/O routine ;
;-----------------------------------------------------------------------;
INOUT_EM: ;Extended memory I/O routine
;change to larger stack
MOV SI,SS ;save old SS in SI
MOV DX,SP ;save old SP in DX
CLI ;disable interrupts
MOV AX,CS
MOV SS,AX ;set SS = CS
MOV SP,OFFSET EM_STACK ;point to new stack
STI ;enable interrupts
PUSH SI ;save old SS at top of new stack
PUSH DX ;save old SP on new stack
MOV SI,RH.RH4_DTAO ;caller's DTA offset
;Compute 24-bit address of VDISK sector in CX (hi) and SI (low)
MOV AX,RH.RH4_SSN ;starting sector number
MUL CS:BPB_SSZ ;* sector size = offset within buffer
ADD AX,CS:START_EM_LO ;+ base address of this VDISK buffer
ADC DL,CS:START_EM_HI
MOV CX,DX ;save high byte
MOV SI,AX ;save low word
;Compute 24-bit address of caller's DTA in DX (hi) and AX (low)
MOV AX,PARA_SIZE ;16
MUL RH.RH4_DTAS ;* segment of caller's DTA
ADD AX,RH.RH4_DTAO ;+ offset of caller's DTA
ADC DL,0 ;carry in from addition
;Caller's DTA address is in CX,SI, VDISK buffer address is in DX,AX.
;If this is an OUTPUT request, exchange the source and target addresses
CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
JE INOUT_EM_B ;jump if INPUT operation
XCHG DX,CX ;swap source and target high byte
XCHG AX,SI ;swap source and target low word
INOUT_EM_B: ;CX,SI is source, DX,AX is target
MOV SRC.DESC_BASEL,SI ;low 16 bits of source address
MOV SRC.DESC_BASEH,CL ;high 8 bits of source address
MOV TGT.DESC_BASEL,AX ;low 16 bits of target address
MOV TGT.DESC_BASEH,DL ;high 8 bits of target address
JMP SHORT INOUT_EM_E ;AX := SECT_LEFT, test for zero
INOUT_EM_C: ;SECT_LEFT in AX, non-zero
; Compute number of sectors to transfer in a single move,
; AX = minimum of (SECT_LEFT, MAX_CNT)
; MAX_CNT is the maximum number of sectors that can be moved without
; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
MOV CX,CS:MAX_CNT ;MAX sectors with one move
CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
JBE INOUT_EM_D ;then move SECT_LEFT sectors
MOV AX,CX ;else move MAX_CNT sectors
INOUT_EM_D:
SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move
;Move AX sectors from source to target
MUL CS:BPB_SSZ ;sectors * sector size = byte count
;(cannot overflow into DX)
MOV TGT.DESC_LMT,AX ;store segment limit (byte count)
MOV SRC.DESC_LMT,AX
PUSH AX ;preserve byte count on stack
SHR AX,1 ;/2 = word count
MOV CX,AX ;word count to CX
PUSH CS
POP ES ;set ES = CS
MOV SI,OFFSET GDT ;ES:SI point to GDT
MOV AH,EM_BLKMOVE ;function is block move
INT EM_INT ;move an even number of words
POP CX ;get byte count back from stack
OR AH,AH ;get error code
JNZ INOUT_EM_XE ;jump if I/O error encountered
;Update source and target addresses
ADD SRC.DESC_BASEL,CX ;add bytes moved to source
ADC SRC.DESC_BASEH,0 ;pick up any carry
ADD TGT.DESC_BASEL,CX ;add bytes moved to target
ADC TGT.DESC_BASEH,0 ;pick up any carry
;Determine if more sectors need to be transferred
INOUT_EM_E: ;do while SECT_LEFT <> zero
MOV AX,CS:SECT_LEFT ;get sectors left to transfer
OR AX,AX ;set flags
JNZ INOUT_EM_C ;go back to transfer some sectors
INOUT_EM_X2: ;revert to original stack
POP DI ;get old SP
POP SI ;get old SS
CLI ;disable interrupts
MOV SS,SI ;restore old SS
MOV SP,DI ;restore old SP
STI ;enable interrupts
RET ;return to IRPT_EXIT
INOUT_EM_XE: ;some error with INT 15H
MOV CS:EM_STAT,AX ;save error status for debugging
MOV RH.RH4_CNT,0 ;indicate no sectors transferred
MOV AX,STAT_CRC ;indicate CRC error
JMP INOUT_EM_X2 ;fix stack and exit
INOUT ENDP
DW 40 DUP (?) ;stack for extended memory I/O
EM_STACK LABEL WORD
SUBTTL Boot Record
PAGE
;-----------------------------------------------------------------------;
; Adjust the assembly-time instruction counter to a paragraph ;
; boundary ;
;-----------------------------------------------------------------------;
IF ($-START) MOD 16
ORG ($-START) + 16 - (($-START) MOD 16)
ENDIF
VDISK EQU $ ;start of virtual disk buffer
VDISKP EQU ($-START) / PARA_SIZE ;length of program in paragraphs
;-----------------------------------------------------------------------;
; If this VDISK is in extended memory, this address is passed ;
; back to DOS as the end address that is to remain resident. ;
; ;
; It this VDISK is not in extended memory, the VDISK buffer ;
; begins at this address, and the address passed back to DOS ;
; as the end address that is to remain resident is this address ;
; plus the length of the VDISK buffer. ;
;-----------------------------------------------------------------------;
BOOT_RECORD LABEL BYTE ;Format of Boot Record documented in
;DOS Technical Reference Manual
DB 0,0,0 ;3-byte jump to boot code (not bootable)
DB 'VDISK ' ;8-byte vendor identification
BOOT_BPB LABEL BYTE ;boot record copy of BIOS parameter block
DW ? ;number of bytes per disk sector
DB ? ;sectors per allocation unit
DW ? ;number of reserved sectors (for boot record)
DB ? ;number of File Allocation Table (FAT) copies
DW ? ;number of root directory entries
DW ? ;total number of sectors
DB ? ;media descriptor byte
DW ? ;number of sectors occupied by a single FAT
;end of boot record BIOS Parameter block
;The following three words mean nothing to VDISK, they are placed here
;to conform to the DOS standard for boot records.
DW 8 ;sectors per track
DW 1 ;number of heads
DW 0 ;number of hidden sectors
;The following word is the 16-bit kilobyte address of the first byte in
;extended memory that is not occupied by a VDISK buffer
;It is placed into this location so that other users of extended memory
;may find where all the VDISKs end.
;This field may be accessed by moving the boot record of the First extended
;memory VDISK from absolute location 10 0000H. Before assuming that the
;value below is valid, the vendor ID (constant VDISK) should be verified
;to make sure that SOME VDISK has been installed.
;For example, if two VDISKs are installed, one 320KB and one 64KB, the
;address calculations are as follows:
;Extended memory start address = 100000H (1024KB)
;Start addr of 1st VDISK buffer = 100000H (1024KB)
;Length of 1st VDISK buffer = 050000H ( 320KB)
;End addr of 1st VDISK buffer = 14FFFFH
;Start addr of 2nd VDISK buffer = 150000H (1344KB)
;Length of 2nd VDISK buffer = 010000H ( 64KB)
;End addr of 2nd VDISK buffer = 15FFFFH
;First byte after all VDISKs = 160000H (1408KB)
;Divide by 1024 = 0580H (1408D)
;Content of BOOT_EM = 0580H
BOOT_EM_OFF EQU $-BOOT_RECORD ;offset from 10 0000H of the following word
BOOT_EM DW 1024 ;KB addr of first free byte of extended memory
;-----------------------------------------------------------------------;
; Part 2 of Initialization (executed last) ;
;-----------------------------------------------------------------------;
;Initialization is divided into two parts.
;INIT_P1 is overlaid by the virtual disk buffer
;INIT_P1 is executed first, then jumps to INIT_P2. INIT_P2 returns to caller.
;Exercise caution if extending the initialization part 2 code.
;It overlays the area immediately following the boot sector.
;If this section of code must be expanded, make sure it fits into the minimum
;sector size of 128 bytes.
;Label TEST_LENGTH must equate to a non-negative value (TEST_LENGTH >= 0).
;If this code it must be extended beyond the 128 byte length of the boot sector,
;move all of INIT_P2 before label VDISK.
;Registers at entry to INIT_P2 (set up at end of INIT_P1):
; BL = media control byte from BPB (for FAT)
; CX = number of FAT copies
; DX = number of bytes in one FAT - 3
; SI = OFFSET of Volume Label field
; ES:DI = VDISK buffer address of first FAT sector
; CS = DS = VDISK code segment
INIT_P2 PROC ;second part of initialization
ASSUME DS:CSEG ;DS set in INIT_P1
;Initialize File Allocation Table(s) (FATs)
INIT_P2_FAT: ;set up one FAT, sector number in AX
PUSH CX ;save loop counter on stack
MOV AL,BL ;media control byte
STOSB ;store media control byte, increment DI
MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH
STOSW
MOV CX,DX ;FAT size in bytes - 3
XOR AX,AX ;value to store in remainder of FAT
REP STOSB ;clear remainder of FAT
POP CX ;get loop counter off stack
LOOP INIT_P2_FAT ;loop for all copies of the FAT
;Put the volume label in the first directory entry
MOV CX,VOL_LABEL_LEN ;length of volume directory entry
REP MOVSB ;move volume id to directory
;Zero the remainder of the directory
MOV AX,DIR_ENTRY_SIZE ;length of 1 directory entry
MUL BPB_DIRN ;* number entries = bytes of directory
SUB AX,VOL_LABEL_LEN ;less length of volume label
MOV CX,AX ;length of rest of directory
XOR AX,AX
REP STOSB ;clear directory to nulls
RET ;return with AX=0
INIT_P2 ENDP
PATCH_AREA DB 5 DUP ('PATCH AREA ')
TEST_LENGTH EQU 128-($-VDISK) ;if negative, boot record has too much
;data area, move some fields below VDISK
;-----------------------------------------------------------------------;
; All fields that must remain resident after device driver ;
; initialization must be defined before this point. ;
;-----------------------------------------------------------------------;
DB 'MS DOS Version 4.00 - Virtual Disk Device Driver'
DB '-------- Licensed Material ---------'
DB 'Program Property of Microsoft Corporation. '
DB '(C)Copyright 1988 Microsoft'
DB 'Thank You For Your '
DB ' Support '
MAXSEC_TRF DW 0 ;maximum number of sectors to transfer when
;in extended memory
BUFF_SIZE DW 0 ;desired VDISK buffer size in kilobytes
MIN_MEMORY_LEFT DW 64 ;minimum amount of system memory (kilobytes)
;that must remain after VDISK is installed
FIRST_EM_SW DB ? ;0FFH if this is the first device driver
;to be installed in extended memory
;00H if another VDISK extended memory driver
;has been installed
FIRST_VDISK DW ? ;segment address of 1st VDISK device driver
PARA_PER_KB DW 1024/PARA_SIZE ;paragraphs in one kilobyte
C1024 DW 1024 ;bytes in one kilobyte
DIRE_SIZE DW DIR_ENTRY_SIZE ;bytes in one directory entry
DIR_SECTORS DW ? ;number of sectors of directory
ERR_FLAG DB 0 ;error indicators to condition messages
ERR_BSIZE EQU 80H ;buffer size adjusted
ERR_SSZ EQU 40H ;sector size adjusted
ERR_DIRN EQU 20H ;number of directory entries adjusted
ERR_PASS EQU 10H ;some adjustment made that requires
;recomputation of values previously computed
ERR_SSZB EQU ERR_SSZ+ERR_PASS ;sector size altered this pass
ERR_SYSSZ EQU 08H ;system storage too small for VDISK
ERR_SWTCH EQU 04H ;invalid switch character
ERR_EXTSW EQU 02H ;extender card switches don't match memory size
ERR_ESIZE EQU 01H ;Transfer size adjusted
; additional errors added - kwc
major_version equ 4 ;Major DOS version
minor_version equ 00 ;Minor DOS Version
expected_version equ (MINOR_VERSION SHL 8)+MAJOR_VERSION
err_flag2 db 0
err_baddos equ 01h ; Invalid DOS Version
SUBTTL Initialization, Part one
PAGE
;-----------------------------------------------------------------------;
; Command Code 0 - Initialization ;
; At entry, DS:BX point to request header, AX = 0 ;
;-----------------------------------------------------------------------;
;Initialization is divided into two parts.
;This part, executed first, is later overlaid by the VDISK buffer.
INIT_P1 PROC ;first part of initialization
MOV DX,SS ;save stack segment register
MOV CX,SP ;save stack pointer register
CLI ;inhibit interrupts while changing SS:SP
MOV AX,CS ;move CS to SS through AX
MOV SS,AX
MOV SP,OFFSET MSGEND ;end of VDISKMSG
ADD SP,STACK_SIZE ;+ length of our stack
STI ;allow interrupts
PUSH DX ;save old SS register on new stack
PUSH CX ;save old SP register on new stack
push bx ;secure registers before DOS int
push cx ;secure registers before DOS int
; add version check - kwc
mov ah,030h
int 21h
pop cx ;restore pointer values
pop bx ;restore pointer values
cmp ax,expected_version
je okdos
or cs:err_flag2,err_baddos
okdos:
CALL GET_PARMS ;get parameters from CONFIG.SYS line
PUSH CS
POP DS ;set DS = CS
ASSUME DS:CSEG
CALL APPLY_DEFAULTS ;supply any values not specified
CALL DETERMINE_START ;compute start address of VDISK buffer
CALL VALIDATE ;validate parameters
CALL COPY_BPB ;Copy BIOS Parameter Block to boot record
CALL VERIFY_EXTENDER ;Verify that extender card switches are right
TEST ERR_FLAG,ERR_EXTSW ;are switches wrong?
JNZ INIT_P1_A ;if so, exit with messages
test CS:err_flag2,err_baddos
jnz init_p1_a
CMP EM_SW,0 ;extended memory requested?
JE INIT_P1_A ;jump if not
TEST ERR_FLAG,ERR_SYSSZ ;is system too small for VDISK?
JNZ INIT_P1_A ;if so, don't do extended memory init
CALL UPDATE_AVAIL ;update AVAIL_HI and AVAIL_LO to reflect
;addition of extended memory VDISK
CALL FORMAT_VDISK ;construct a boot record, FATs and
;directory in storage immediately
;following this device driver
CALL MOVE_VDISK ;move formatted boot record, FATs,
;and directory to extended memory
CALL UPDATE_BOOT ;place the end address of ALL VDISKs
;in the boot record of the first VDISK
CMP FIRST_EM_SW,0 ;is this the first extended memory VDISK?
JE INIT_P1_A ;no, exit
CALL STEAL_INT19 ;point INT 19H to this VDISK
INIT_P1_A:
CALL FILL_RH ;fill in INIT request header
CALL WRITE_MESSAGES ;display all messages
POP CX ;get old SP from stack
POP DX ;get old SS from stack
CLI ;disable interrupts while changing SS:SP
MOV SS,DX ;restore stack segment register
MOV SP,CX ;restore stack pointer register
STI ;enable interrupts
;-----------------------------------------------------------------------;
; INIT_P2 must be short enough to fit into the boot sector ;
; (minimum size of boot sector is 128 bytes), so we set up ;
; as many pointers as we can to help keep INIT_P2 short. ;
; ;
; ES:DI = storage address of first FAT sector ;
; BL = media control byte ;
; CX = number of FAT copies ;
; DX = number of bytes in one FAT, less 3 ;
; SI = offset of VOL label field ;
;-----------------------------------------------------------------------;
MOV ES,START_BUFFER_PARA ;start paragraph of VDISK buffer
MOV AX,BPB_RES ;number of reserved sectors
MUL BPB_SSZ ;* sector size
MOV DI,AX ;ES:DI point to FAT start
MOV BL,BPB_MCB ;media control byte
MOV CL,BPB_FATN ;number of FAT copies
XOR CH,CH
MOV AX,BPB_FATSZ ;FAT size in sectors
MUL BPB_SSZ ;* sector size = total FAT bytes
SUB AX,3 ;-3 (FEFFFF stored by code)
MOV DX,AX
MOV SI,OFFSET VOL_LABEL ;point to VOL label directory entry
JMP INIT_P2 ;jump to second part of initialization
;this is redundant if the VDISK is in
;extended memory, but is executed anyway
SUBTTL GET_PARMS Parameter Line Scan
PAGE
;-----------------------------------------------------------------------;
;GET_PARMS gets the parameters from the CONFIG.SYS statement ;
; ;
;Register usage: ;
; DS:SI indexes parameter string ;
; AL contains character from parameter string ;
; CX value from GET_NUMBER ;
;-----------------------------------------------------------------------;
ASSUME DS:NOTHING ;DS:BX point to Request Header
GET_PARMS PROC ;get parameters from CONFIG.SYS line
PUSH DS ;save DS
LDS SI,RH.RH0_BPBA ;DS:SI point to all after DEVICE=
;in CONFIG.SYS line
XOR AL,AL ;not at end of line
;Skip until first delimiter is found. There may be digits in the path string.
;DS:SI points to \pathstring\VDISK.SYS nn nn nn
;The character following VDISK.SYS may have been changed to a null (00H).
;All letters have been changed to uppercase.
GET_PARMS_A: ;skip to DOS delimiter character
CALL GET_PCHAR ;get parameter character into AL
JZ GET_PARMS_X ;get out if end of line encountered
OR AL,AL ;test for null
JZ GET_PARMS_C ;
CMP AL,' '
JE GET_PARMS_C ;
CMP AL,','
JE GET_PARMS_C ;
CMP AL,';'
JE GET_PARMS_C ;
CMP AL,'+'
JE GET_PARMS_C ;
CMP AL,'='
JE GET_PARMS_C ;
CMP AL,TAB
JNE GET_PARMS_A ;skip until delimiter or CR
GET_PARMS_C:
PUSH SI ;save to rescan
MOV CS:EM_SW,0 ;indicate no /E found
JMP GET_SLASH ;see if current character is an slash
GET_PARMS_D: ;scan for /
CALL GET_PCHAR
JZ GET_PARMS_B ;exit if end of line
GET_SLASH: ;check for slash
CMP AL,'/' ;found slash?
JNE GET_PARMS_D ;no, continue scan
CALL GET_PCHAR ;get char following slash
CMP AL,'E' ;don't have to test for lower case E,
;letters have been changed to upper case
JNE GET_PARMS_E ;not 'E'
MOV CS:EM_SW,AL ;indicate /E found
CALL GET_PCHAR ;get char following E
CMP AL,':' ;is it a delimeter ?
JNE GET_PARMS_D ;not a ':'
CALL GET_MAXSIZE ;get maximum sector size
JMP GET_PARMS_D ;continue forward scan
GET_PARMS_E: ;/ found, not 'E'
OR CS:ERR_FLAG,ERR_SWTCH ;indicate invalid switch character
JMP GET_PARMS_D ;continue scan
GET_PARMS_B: ;now pointing to first delimiter
POP SI ;get pointer, used to rescan for /E
XOR AL,AL ;not at EOL now
CALL GET_PCHAR ;get first character
CALL SKIP_TO_DIGIT ;skip to first digit
JZ GET_PARMS_X ;found EOL, no digits remain
CALL GET_NUMBER ;extract digits, convert to binary
MOV CS:BUFF_SIZE,CX ;store buffer size
CALL SKIP_TO_DIGIT ;skip to next digit
JZ GET_PARMS_X ;found EOL, no digits remain
CALL GET_NUMBER ;extract digits, convert to binary
MOV CS:BPB_SSZ,CX ;store sector size
CALL SKIP_TO_DIGIT ;skip to next digit
JZ GET_PARMS_X ;found EOL, no digits remain
CALL GET_NUMBER ;extract digits, convert to binary
MOV CS:BPB_DIRN,CX ;store number of directory entries
GET_PARMS_X: ;premature end of line
POP DS ;restore DS
RET
GET_MAXSIZE PROC ;get maximum sector size
CALL GET_PCHAR ;get next character
CALL CHECK_NUM ;is it a number ?
JZ GET_NEXTNUM ;yes, go get next number
OR CS:ERR_FLAG,ERR_ESIZE ;indicate invalid sector size
RET ;
GET_NEXTNUM: ;get next number
CALL GET_NUMBER ;extract digits and convert to binary
MOV CS:MAXSEC_TRF,CX ;save maximum sector size to transfer
RET
GET_MAXSIZE ENDP
GET_PCHAR PROC ;internal proc to get next character into AL
CMP AL,CR ;carriage return already encountered?
JE GET_PCHAR_X ;don't read past end of line
CMP AL,LF ;line feed already encountered?
JE GET_PCHAR_X ;don't read past end of line
LODSB ;get char from DS:SI, increment SI
CMP AL,CR ;is the char a carriage return?
JE GET_PCHAR_X ;yes, set Z flag at end of line
CMP AL,LF ;no, is it a line feed?
GET_PCHAR_X: ;attempted read past end of line
RET
GET_PCHAR ENDP ;returns char in AL
CHECK_NUM PROC ;check AL for ASCII digit
CMP AL,'0' ;< '0'?
JB CHECK_NUM_X ;exit if it is
CMP AL,'9' ;> '9'?
JA CHECK_NUM_X ;exit if it is
CMP AL,AL ;set Z flag to indicate numeric
CHECK_NUM_X:
RET ;Z set if numeric, NZ if not numeric
CHECK_NUM ENDP
SKIP_TO_DIGIT PROC ;skip to first numeric character
CALL CHECK_NUM ;is current char a digit?
JZ SKIP_TO_DIGIT_X ;if so, skip is complete
CALL GET_PCHAR ;get next character from line
JNZ SKIP_TO_DIGIT ;loop until first digit or CR or LF
RET ;character is CR or LF
SKIP_TO_DIGIT_X:
CMP AL,0 ;digit found, force NZ
RET
SKIP_TO_DIGIT ENDP
C10 DW 10
GN_ERR DB ? ;zero if no overflow in accumulation
GET_NUMBER PROC ;convert string of digits to binary value
XOR CX,CX ;accumulate number in CX
MOV CS:GN_ERR,CL ;no overflow yet
GET_NUMBER_A: ;accumulate next digit
SUB AL,'0' ;convert ASCII to binary
CBW ;clear AH
XCHG AX,CX ;previous accumulation in AX, new digit in CL
MUL CS:C10 ;DX:AX := AX*10
OR CS:GN_ERR,DL ;set GN_ERR <> 0 if overflow
ADD AX,CX ;add new digit from
XCHG AX,CX ;number now in CX
DEC SI ;back up to prior entry
MOV AL,' ' ;blank out prior entry
MOV [SI],AL ;
INC SI ;set to current entry
CALL GET_PCHAR ;get next character
CALL CHECK_NUM ;see if it was numeric
JZ GET_NUMBER_A ;continue accumulating
CMP CS:GN_ERR,0 ;did we overflow?
JE GET_NUMBER_B ;if not, we're done
XOR CX,CX ;return zero (always invalid) if overflow
GET_NUMBER_B:
RET ;number in CX, next char in AL
GET_NUMBER ENDP
GET_PARMS ENDP
SUBTTL APPLY_DEFAULTS
PAGE
;-----------------------------------------------------------------------;
; APPLY_DEFAULTS supplies any parameter values that the user ;
; failed to specify ;
;-----------------------------------------------------------------------;
ASSUME DS:CSEG
APPLY_DEFAULTS PROC
XOR AX,AX
CMP BUFF_SIZE,AX ;is buffer size zero?
JNE APPLY_DEFAULTS_A ;no, user specified something
MOV BUFF_SIZE,DFLT_BSIZE ;supply default buffer size
OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted
APPLY_DEFAULTS_A:
CMP BPB_SSZ,AX ;is sector size zero?
JNE APPLY_DEFAULTS_B ;no, user specified something
MOV BPB_SSZ,DFLT_SSZ ;supply default sector size
OR ERR_FLAG,ERR_SSZ ;indicate sector size adjusted
APPLY_DEFAULTS_B:
CMP BPB_DIRN,AX ;are directory entries zero?
JNE APPLY_DEFAULTS_C ;no, user specified something
MOV BPB_DIRN,DFLT_DIRN ;supply default directory entries
OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
APPLY_DEFAULTS_C: ;
CMP EM_SW,0 ;extended memory ?
JE APPLY_DEFAULTS_D ;no, jump around
CMP MAXSEC_TRF,AX ;is maximum sectors zero?
JNE APPLY_DEFAULTS_D ;no, user specified something
MOV MAXSEC_TRF,DFLT_ESS ;supply default maximum number of
;sector to transfer
OR ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted
APPLY_DEFAULTS_D:
RET
APPLY_DEFAULTS ENDP
SUBTTL DETERMINE_START address of VDISK buffer
PAGE
;-----------------------------------------------------------------------;
; DETERMINE_START figures out the starting address of the VDISK ;
; buffer ;
;-----------------------------------------------------------------------;
ASSUME DS:CSEG
DETERMINE_START PROC
;If extended memory is NOT being used, the VDISK buffer immediately
;follows the resident code.
;If extended memory IS being used, START_BUFFER_PARA becomes the
;end of device driver address passed back to DOS.
MOV AX,CS ;start para of VDISK code
ADD AX,VDISKP ;+ length of resident code
MOV START_BUFFER_PARA,AX ;save as buffer start para
CMP EM_SW,0 ;is extended memory requested?
JE DETERMINE_START_X ;if not, we're done here
;If this is the first extended memory VDISK device driver to be installed,
;the start address for I/O is 1 megabyte.
;If one or more extended memory VDISK device drivers have been installed,
;the start address for I/O for THIS device driver is acquired from the
;fields AVAIL_LO and AVAIL_HI in the FIRST VDISK device driver.
;The first extended memory VDISK device driver is located by INT 19H's vector.
MOV FIRST_EM_SW,0FFH ;indicate first VDISK device driver
MOV FIRST_VDISK,CS ;segment addr of first VDISK
PUSH DS ;preserve DS
XOR AX,AX
MOV DS,AX ;set DS = 0
ASSUME DS:INT_VEC
MOV AX,DS:BOOT_VECS ;get segment addr of INT 19H routine
MOV DS,AX ;to DS
ASSUME DS:NOTHING
PUSH CS
POP ES ;set ES = CS
MOV SI,OFFSET VOL_LABEL ;DS:SI point to VOL label field
;in first VDISK (if present)
MOV DI,SI ;ES:DI point to VOL label field of
;this VDISK
MOV CX,VOL_LABEL_LEN ;length of volume label
REP CMPSB ;does INT 19H vector point to a VDISK
;device driver?
JNE DETERMINE_START_A ;jump if this is the first VDISK
;Another extended memory VDISK device driver has been installed.
;Its AVAIL_LO and AVAIL_HI are the first free byte of extended memory.
MOV CS:FIRST_EM_SW,0 ;indicate not first device driver
MOV CS:FIRST_VDISK,DS ;save pointer to 1st device driver
;Copy AVAIL_LO and AVAIL_HI from first VDISK to this VDISK
MOV SI,OFFSET AVAIL_LO ;DS:SI point to AVAIL_LO in first VDISK
MOV DI,SI ;ES:DI point to AVAIL_LO in this VDISK
MOVSW ;copy AVAIL_LO from first to this VDISK
MOVSB ;copy AVAIL_HI
DETERMINE_START_A: ;copy AVAIL_LO and AVAIL_HI to START_EM
POP DS ;set DS = CS
MOV SI,OFFSET AVAIL_LO ;source offset
MOV DI,OFFSET START_EM_LO ;destination offset
MOVSW ;move AVAIL_LO to START_EM_LO
MOVSB ;move AVAIL_HI to START_EM_HI
DETERMINE_START_X:
RET
DETERMINE_START ENDP
SUBTTL VALIDATE parameters
PAGE
;-----------------------------------------------------------------------;
; VALIDATE adjusts parameters as necessary ;
;-----------------------------------------------------------------------;
VAL_SSZ_TBL LABEL WORD ;table of valid sector sizes
VAL_SSZ_S DW 128 ;smallest valid sector size
DW 256
VAL_SSZ_L DW 512 ;largest valid sector size
VAL_SSZ_N EQU ($-VAL_SSZ_TBL)/2 ;number of table entries
ASSUME DS:CSEG
VALIDATE PROC ;validate parameters
MOV BPB_AUSZ,1 ;initial allocation unit is 1 sector
CALL VAL_BSIZE ;validate buffer size
CALL VAL_SSZ ;validate (adjust if necessary) BPB_SSZ
VALIDATE_A:
AND ERR_FLAG,255-ERR_PASS ;indicate nothing changed this pass
MOV AX,BPB_SSZ ;sector size
CWD ;clear DX for division
DIV WPARA_SIZE ;sector size/para size
MOV PARAS_PER_SECTOR,AX ;number of paragraphs/sector
MOV AX,BUFF_SIZE ;requested buffersize in KB
MUL C1024 ;DX:AX = buffer size in bytes
DIV BPB_SSZ ;/sector size = # sectors
MOV BPB_SECN,AX ;store number of sectors
CALL VAL_DIRN ;validate number of directory entries
TEST ERR_FLAG,ERR_PASS ;may have reset sector size
JNZ VALIDATE_A ;recompute directory & FAT sizes
CALL VAL_FAT ;compute FAT entries, validity test
TEST ERR_FLAG,ERR_PASS ;if cluster size altered this pass
JNZ VALIDATE_A ;recompute directory & FAT sizes
;Make certain buffer size is large enough to contain:
; boot sector(s)
; FAT sector(s)
; directory sector(s)
; at least 1 data cluster
MOV AL,BPB_FATN ;number of FAT copies
CBW ;clear AH
MUL BPB_FATSZ ;* sectors for 1 FAT = FAT sectors
ADD AX,BPB_RES ;+ reserved sectors
ADD AX,DIR_SECTORS ;+ directory sectors
MOV CL,BPB_AUSZ ;get sectors/cluster
XOR CH,CH ;CX = sectors in one cluster
ADD AX,CX ;+ one data cluster
CMP BPB_SECN,AX ;compare with sectors available
JAE VALIDATE_X ;jump if enough sectors
CMP DIR_SECTORS,1 ;down to 1 directory sector?
JBE VALIDATE_C ;can't let it go below 1
MOV AX,BPB_SSZ ;sector size
CWD ;clear DX for division
DIV DIRE_SIZE ;sectorsize/dir entry size = entries/sector
SUB BPB_DIRN,AX ;reduce directory entries by 1 sector
OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
JMP VALIDATE_A ;retry with new directory entries number
VALIDATE_C: ;not enough space for any VDISK
OR ERR_FLAG,ERR_SYSSZ
VALIDATE_X:
RET
SUBTTL VAL_BSIZE Validate buffer size
PAGE
;-----------------------------------------------------------------------;
; VAL_BSIZE adjusts the buffer size as necessary ;
;-----------------------------------------------------------------------;
VAL_BSIZE PROC
CALL GET_MSIZE ;determine memory available to VDISK
;returns available KB in AX
OR AX,AX ;is any memory available at all?
JNZ VAL_BSIZE_B ;yes, continue
OR ERR_FLAG,ERR_SYSSZ ;indicate system too small for VDISK
MOV BUFF_SIZE,1 ;set up minimal values to continue init
MOV AX,VAL_SSZ_S ;smallest possible sector size
MOV BPB_SSZ,AX
MOV BPB_DIRN,4 ;4 directory entries
RET
VAL_BSIZE_B: ;some memory is available
CMP AX,BUFF_SIZE ;is available memory >= requested?
JAE VAL_BSIZE_C ;if so, we're done
MOV BUFF_SIZE,AX ;give all available memory
OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted
VAL_BSIZE_C:
RET
GET_MSIZE PROC ;determine memory available to VDISK
;returns KB available in AX
CMP EM_SW,0 ;extended memory?
JE GET_MSIZE_2 ;use non-extended memory routine
MOV AH,EM_MEMSIZE ;function code to AH
INT EM_INT ;get extended memory size in AX
JC GET_MSIZE_Z ;if error, no extended memory installed
MUL C1024 ;DX,AX = bytes of extended memory
ADD DX,10H ;DX,AX = high addr of extended memory+1
SUB AX,AVAIL_LO ;- address of first available byte
SBB DL,AVAIL_HI ;is number of free bytes
DIV C1024 ;AX = number of whole free kilobytes
RET
GET_MSIZE_2: ;non-extended memory size determination
;Compute AX = total system size, - (VDISK end address + 64KB)
MOV AX,START_BUFFER_PARA ;paragraph end of VDISK code
XOR DX,DX ;clear for division
DIV PARA_PER_KB ;KB address of load point
ADD DX,0FFFFH ;round upward to KB boundary
ADC AX,MIN_MEMORY_LEFT ;pick up CY and the 64KB we should leave
PUSH AX ;save across interrupt
INT MEM_SIZE ;get total system size
POP DX ;amount of total that we can't use
SUB AX,DX ;available space to VDISK
JNC GET_MSIZE_X ;exit if positive
GET_MSIZE_Z:
XOR AX,AX ;indicate no memory available
GET_MSIZE_X: ;exit from memory size determination
RET
GET_MSIZE ENDP
VAL_BSIZE ENDP
SUBTTL VAL_SSZ Validate Sector Size
PAGE
;-----------------------------------------------------------------------;
; VAL_SSZ validates sector size, adjusting if necessary ;
;-----------------------------------------------------------------------;
VAL_SSZ PROC ;validate sector size
CMP CS:EM_SW,0 ;extended memory ?
JE VAL_SSZ_ST ;no,go check sector size
MOV BX,MAXSEC_TRF ;move number of sectors to transfer
CMP BX,1 ;> or equal to 1 ?
JB DFLT_TRF ;set default if it is
CMP BX,8 ;> than 8 ?
JA DFLT_TRF ;set default if it is
JMP VAL_SSZ_ST ;continue processing
DFLT_TRF: ;set default
MOV MAXSEC_TRF,DFLT_ESS ;
MOV BX,MAXSEC_TRF ;
OR CS:ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted
VAL_SSZ_ST: ;validate sector size
MOV MAX_CNT,BX ;initialize maximum number of sectors
;to transfer for extended memory case
MOV BX,BPB_SSZ ;requested sector size
MOV CX,VAL_SSZ_N ;number of table entries
MOV SI,OFFSET VAL_SSZ_TBL ;DS:SI point to table start
VAL_SSZ_A:
LODSW ;get table entry, step table pointer
CMP AX,BX ;is value in table?
JE VAL_SSZ_X ;exit if value found
LOOP VAL_SSZ_A ;loop until table end
MOV BX,DFLT_SSZ ;get default sector size
MOV BPB_SSZ,BX ;set sector size to default value
OR ERR_FLAG,ERR_SSZ ;indicate sector size adjusted
VAL_SSZ_X:
;Compute the maximum number of sectors that can be moved in 64KB (less one)
;Restricting moves to this amount avoids 64KB boundary problems.
CMP CS:EM_SW,0 ;extended memory ?
JNE SIZE_DONE ;yes, we are done
XOR DX,DX
MOV AX,0FFFFH ;64KB - 1
DIV BX ;/sector size
MOV MAX_CNT,AX ;max sectors in one move
SIZE_DONE:
RET
VAL_SSZ ENDP
SUBTTL VAL_DIRN Validate number of directory entries
PAGE
;-----------------------------------------------------------------------;
; VAL_DIRN validates and adjusts the number of directory entries. ;
; ;
; Minimum is MIN_DIRN, maximum is MAX_DIRN. If outside these ;
; limits, DFLT_DIRN is used. ;
; ;
; The number of directory entries is rounded upward to fill ;
; a sector ;
;-----------------------------------------------------------------------;
VAL_DIRN PROC
MOV AX,BPB_DIRN ;requested directory entries
CMP AX,MIN_DIRN ;if less than minimum
JB VAL_DIRN_A ;use default instead
CMP AX,MAX_DIRN ;if <= maximum
JBE VAL_DIRN_B ;accept value as provided
VAL_DIRN_A:
MOV AX,DFLT_DIRN ;use default directory entries
OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
VAL_DIRN_B: ;AX is number of directory entries
MUL DIRE_SIZE ;* 32 = bytes of directory requested
DIV BPB_SSZ ;/ sector size = # of directory sectors
OR DX,DX ;test remainder for zero
JZ VAL_DIRN_C ;jump if exact fit
INC AX ;increment directory sectors
OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
VAL_DIRN_C: ;make sure enough sectors available
MOV DX,BPB_SECN ;total sectors on media
SUB DX,BPB_RES ;less reserved sectors
SUB DX,2 ;less minimum FAT and 1 data sector
CMP AX,DX ;if directory sectors <= available
JLE VAL_DIRN_D ;use requested amount
MOV AX,1 ;use only one directory sector
OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted
VAL_DIRN_D:
MOV DIR_SECTORS,AX ;save number of directory sectors
MUL BPB_SSZ ;dir sectors * sector size = dir bytes
DIV DIRE_SIZE ;dir bytes / entry size = entries
MOV BPB_DIRN,AX ;store adjusted directory entries
RET
VAL_DIRN ENDP
SUBTTL VAL_FAT Validate File Allocation Table (FAT)
PAGE
;-----------------------------------------------------------------------;
;VAL_FAT computes: ;
;BPB_FATSZ, the number of sectors required per FAT copy ;
; ;
;Each FAT entry is 12 bits long, for a maximum of 4095 FAT entries. ;
;(A few FAT entries are reserved, so the highest number of FAT entries ;
;we permit is 0FE0H.) With large buffer sizes and small sector sizes, ;
;we have more allocation units to describe than a 12-bit entry will ;
;describe. If the number of FAT entries is too large, the sector size ;
;is increased (up to a maximum of 512 bytes), and then the allocation ;
;unit (cluster) size is doubled, until we have few enough allocation ;
;units to be properly described in 12 bits. ;
; ;
;This computation is slightly conservative in that the FAT entries ;
;necessary to describe the FAT sectors are included in the computation. ;
;-----------------------------------------------------------------------;
VAL_FAT PROC
MOV AX,BPB_SECN ;total number of sectors
SUB AX,BPB_RES ;don't count boot sector(s)
SUB AX,DIR_SECTORS ;don't count directory sectors
JG VAL_FAT_A ;jump if some remaining
MOV BPB_SSZ,DFLT_SSZ ;force default sector size
OR ERR_FLAG,ERR_SSZ+ERR_PASS ;indicate sector size adjusted
JMP SHORT VAL_FAT_X ;recompute all values
VAL_FAT_A:
XOR DX,DX ;clear DX for division
MOV CL,BPB_AUSZ ;CX = sectors/cluster
XOR CH,CH
DIV CX ;whole number of clusters in AX
ADD DX,0FFFFH ;set carry if remainder
ADC AX,0 ;increment AX if remainder
CMP AX,MAX_FATE ;number of FAT entries too large?
JBE VAL_FAT_C ;no, continue
MOV AX,BPB_SSZ ;pick up current sector size
CMP AX,VAL_SSZ_L ;already at largest permitted?
JE VAL_FAT_B ;yes, can't make it any larger
SHL BPB_SSZ,1 ;double sector size
OR ERR_FLAG,ERR_SSZB ;indicate sector size adjusted
JMP SHORT VAL_FAT_X ;recompute all sizes with new BPBSSZ
VAL_FAT_B: ;sector size is at maximum
SHL BPB_AUSZ,1 ;double allocation unit size
OR ERR_FLAG,ERR_PASS ;indicate another pass required
JMP SHORT VAL_FAT_X ;recompute values
VAL_FAT_C: ;FAT size = 1.5 * number of clusters
MOV CX,AX ;number of clusters
SHL AX,1 ;* 2
ADD AX,CX ;* 3
SHR AX,1 ;* 1.5
ADC AX,3 ;add 3 bytes for first 2 FAT entries
;(media descriptor and FFFFH), and CY
XOR DX,DX ;clear DX for division
DIV BPB_SSZ ;FAT size/sector size
ADD DX,0FFFFH ;set carry if remainder
ADC AX,0 ;round upward
MOV BPB_FATSZ,AX ;number of sectors for 1 FAT copy
VAL_FAT_X:
RET
VAL_FAT ENDP
VALIDATE ENDP
SUBTTL COPY_BPB Copy BPB to Boot Record
PAGE
;-----------------------------------------------------------------------;
; COPY_BPB copies the BIOS Parameter Block (BPB) ;
; to the VDISK Boot Record ;
;-----------------------------------------------------------------------;
ASSUME DS:CSEG
COPY_BPB PROC ;Copy BBP to Boot Record
PUSH DS
POP ES ;set ES = DS
MOV CX,BPB_LEN ;length of BPB
MOV SI,OFFSET BPB ;source offset
MOV DI,OFFSET BOOT_BPB ;target offset
REP MOVSB ;copy BPB to boot record
RET
COPY_BPB ENDP
SUBTTL VERIFY_EXTENDER
PAGE
;-----------------------------------------------------------------------;
; VERIFY_EXTENDER makes sure that if an Expansion Unit is ;
; installed, the memory size switches on the Extender Card ;
; are correctly set. ;
;-----------------------------------------------------------------------;
ASSUME DS:CSEG
EXT_P210 EQU 0210H ;write to latch expansion bus data
;read to verify expansion bus data
EXT_P213 EQU 0213H ;Expansion Unit status
VERIFY_EXTENDER PROC
NOP
MOV DX,EXT_P210 ;Expansion bus data port address
MOV AX,5555H ;set data pattern
OUT DX,AL ;write 55H to control port
PUSH DX
POP DX
JMP SHORT $+2 ;Let the I/O circuits catch up
IN AL,020h ;Clear the CMOS bus drivers!
IN AL,DX ;recover data
CMP AH,AL ;did we recover the same data?
JNE VERIFY_EXTENDER_X ;if not, no extender card
NOT AX ;set AX = 0AAAAH
OUT DX,AL ;write 0AAH to control port
PUSH DX ;load data line
POP DX ;load data line
JMP SHORT $+2 ;Let the I/O circuits catch up
IN AL,020h ;Clear the CMOS bus drivers!
IN AL,DX ;recover data
CMP AH,AL ;did we recover the same data?
JNE VERIFY_EXTENDER_X ;if not, no extender card
;Expansion Unit is present.
;Determine what the switch settings should be on the Extender Card
INT MEM_SIZE ;get system memory size in KB in AX
ADD AX,63D ;memory size + 63K
MOV CL,6 ;2^6 = 64
SHR AX,CL ;divide by 64
;AX is highest segment address
MOV AH,AL ;save number of segments
;Read Expander card switch settings
MOV DX,EXT_P213 ;expansion unit status
IN AL,DX ;read status
;bits 7-4 (hi nibble) are switches
MOV CL,4 ;shift count
SHR AL,CL ;shift switches to bits 3-0 of AL
CMP AH,AL ;do switches match memory size?
JE VERIFY_EXTENDER_X ;yes, exit normally
OR ERR_FLAG,ERR_EXTSW ;indicate switch settings are wrong
VERIFY_EXTENDER_X:
RET
VERIFY_EXTENDER ENDP
SUBTTL UPDATE_AVAIL
PAGE
;-----------------------------------------------------------------------;
; UPDATE_AVAIL updates the address of the first byte in extended ;
; memory not used by any VDISK buffer ;
;-----------------------------------------------------------------------;
UPDATE_AVAIL PROC ;update AVAIL_LO and AVAIL_HI of first VDISK
MOV AX,BUFF_SIZE ;number of KB of VDISK buffer
MUL C1024 ;DX,AX = number of bytes of VDISK buffer
PUSH DS
MOV DS,FIRST_VDISK ;set DS to first VDISK
ADD DS:AVAIL_LO,AX ;update first available byte location
ADC DS:AVAIL_HI,DL
POP DS
RET
UPDATE_AVAIL ENDP
SUBTTL FORMAT_VDISK
PAGE
;-----------------------------------------------------------------------;
; This Request Header is used by MOVE_VDISK to move the ;
; first few sectors of the virtual disk (boot, FAT, and ;
; Directory) into extended memory. ;
;-----------------------------------------------------------------------;
MOVE_RH DB MOVE_RH_L ;length of request header
DB 0 ;sub unit
DB 8 ;output operation
DW 0 ;status
DQ ? ;reserved for DOS
DB ? ;media descriptor byte
MOVE_RHO DW ? ;offset of data transfer address
MOVE_RHS DW ? ;segment of data transfer address
MOVE_RHCNT DW ? ;count of sectors to transfer
DW 0 ;starting sector number
MOVE_RH_L EQU $-MOVE_RH ;length of request header
;-----------------------------------------------------------------------;
; FORMAT_VDISK formats the boot sector, FAT, and directory of an ;
; extended memory VDISK in storage immediately following ;
; VDISK code, in preparation for moving to extended memory. ;
;-----------------------------------------------------------------------;
FORMAT_VDISK PROC ;format boot record, FATs and directory
MOV AX,CS ;compute 20-bit address
MUL WPARA_SIZE ;16 * segment
ADD AX,OFFSET MSGEND ;+ offset
ADC DL,0 ;pick up carry
ADD AX,STACK_SIZE ;plus stack size
ADC DL,0 ;pick up carry
DIV WPARA_SIZE ;split into segment(AX)&offset(DX)
MOV MOVE_RHS,AX ;save in Request Header for move
MOV MOVE_RHO,DX
MOV DI,DX ;offset to DI
MOV ES,AX ;segment to ES
;copy the boot record
MOV SI,OFFSET BOOT_RECORD ;point to source field
MOV AX,BPB_RES ;number of reserved sectors
MUL BPB_SSZ ;* sector size = length of boot records
MOV CX,AX ;length to CX for move
REP MOVSB ;move boot record(s)
;format the FAT(s)
MOV CL,BPB_FATN ;number of FATs
XOR CH,CH
FORMAT_VDISK_A: ;set up one FAT
PUSH CX ;save loop counter on stack
MOV AL,BPB_MCB ;media control byte
STOSB ;store media control byte, increment DI
MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH
STOSW
MOV AX,BPB_FATSZ ;number of sectors per FAT
MUL BPB_SSZ ;* sector size = length of FAT in bytes
SUB AX,3 ;less the 3 bytes we've stored
MOV CX,AX ;count to CX
XOR AX,AX
REP STOSB ;clear remainder of FAT
POP CX ;get loop counter off stack
LOOP FORMAT_VDISK_A ;loop for all copies of the FAT
;Format the directory
MOV SI,OFFSET VOL_LABEL ;point to volume label
MOV CX,VOL_LABEL_LEN ;length of volume directory entry
REP MOVSB ;move volume id to directory
MOV AX,DIR_ENTRY_SIZE ;length of 1 directory entry
MUL BPB_DIRN ;* number entries = bytes of directory
SUB AX,VOL_LABEL_LEN ;less length of volume label
MOV CX,AX ;CX = length of rest of directory
XOR AX,AX
REP STOSB ;clear directory to nulls
RET
FORMAT_VDISK ENDP
SUBTTL MOVE_VDISK
PAGE
;-----------------------------------------------------------------------;
; MOVE_VDISK moves the formatted boot sector, FAT, and directory ;
; into extended memory. ;
;-----------------------------------------------------------------------;
MOVE_VDISK PROC
MOV AL,BPB_FATN ;number of FAT copies
CBW ;clear AH
MUL BPB_FATSZ ;number of FAT sectors
ADD AX,BPB_RES ;+ reserved sectors
ADD AX,DIR_SECTORS ;+ directory sectors
MOV MOVE_RHCNT,AX ;store as I/O length
MOV BX,OFFSET MOVE_RH ;DS:BX point to request header
PUSH DS ;make sure DS gets preserved
CALL INOUT ;move to extended memory
POP DS
RET
MOVE_VDISK ENDP
SUBTTL UPDATE_BOOT
PAGE
;-----------------------------------------------------------------------;
; UPDATE_BOOT updates the BOOT_EM word in the first extended ;
; memory VDISK (address 10 001EH) to show the kilobyte address ;
; of the first extended memory byte not used by any VDISK buffer. ;
;-----------------------------------------------------------------------;
UPDATE_BOOT PROC
PUSH DS
MOV DS,FIRST_VDISK ;set DS to first VDISK
MOV AX,DS:AVAIL_LO ;24-bit end address of all VDISKs
MOV DL,DS:AVAIL_HI
XOR DH,DH
POP DS
DIV C1024 ;address / 1024
MOV BOOT_EM,AX ;store in temporary location
MOV AX,2 ;length of block move is 2 bytes
MOV TGT.DESC_LMT,AX
MOV SRC.DESC_LMT,AX
MOV AX,PARA_SIZE ;16
MOV CX,CS ;our segment address
MUL CX ;16 * segment address
ADD AX,OFFSET BOOT_EM ;+ offset of source data
ADC DL,0 ;pick up any carry
MOV SRC.DESC_BASEL,AX ;store source base address
MOV SRC.DESC_BASEH,DL
MOV TGT.DESC_BASEL,BOOT_EM_OFF ;offset of BOOT_EM
MOV TGT.DESC_BASEH,10H ;1 megabyte
MOV CX,1 ;move 1 word
PUSH CS
POP ES
MOV SI,OFFSET GDT ;ES:DI point to global descriptor table
MOV AH,EM_BLKMOVE ;function code
INT EM_INT ;move BOOT_EM to 10 001EH
RET
UPDATE_BOOT ENDP
SUBTTL STEAL_INT19
PAGE
;-----------------------------------------------------------------------;
; STEAL_INT19 changes the INT 19H vector to point to this VDISK ;
; so that subsequent extended memory VDISKS may locate the ;
; AVAIL_HI and AVAIL_LO fields to determine their buffer start ;
; addresses. ;
;-----------------------------------------------------------------------;
STEAL_INT19 PROC
PUSH DS
XOR AX,AX
MOV DS,AX ;set DS = 0
ASSUME DS:INT_VEC
CLI ;disable interrupts
LES DI,DS:BOOT_VEC ;get original vector's content
MOV CS:INTV19O,DI ;save original vector
MOV CS:INTV19S,ES
MOV DS:BOOT_VECO,OFFSET VDISK_INT19 ;offset of new INT routine
MOV DS:BOOT_VECS,CS ;segment of new INT routine
STI ;enable interrupts again
POP DS ;restore DS
RET
STEAL_INT19 ENDP
SUBTTL FILL_RH Fill in Request Header
PAGE
;-----------------------------------------------------------------------;
; FILL_RH fills in the Request Header returned to DOS ;
;-----------------------------------------------------------------------;
ASSUME DS:CSEG
FILL_RH PROC ;fill in INIT Request Header fields
MOV CX,START_BUFFER_PARA ;segment end of VDISK resident code
MOV AX,PARAS_PER_SECTOR ;paragraphs per sector
MUL BPB_SECN ;* number of sectors
ADD AX,CX ;+ starting segment
MOV DX,AX ;DX is segment of end VDISK buffer
CMP EM_SW,0 ;if extended memory not requested
JE FILL_RH_A ;skip DX adjustment
MOV DX,CX ;end of code segment addr
FILL_RH_A: ;DX is proper ending segment address
MOV AL,1 ;number of units
test CS:err_flag2,err_baddos
jnz dont_install
TEST ERR_FLAG,ERR_SYSSZ+ERR_EXTSW ;if bypassing install
JZ FILL_RH_B ;jump if installing driver
dont_install:
MOV DX,CS ;segment of end address
XOR AL,AL ;number of units is zero
FILL_RH_B:
PUSH DS ;preserve DS
LDS BX,RH_PTRA ;get Request Header addr in DS:BX
MOV RH.RH0_NUN,AL ;store number of units (0 or 1)
MOV RH.RH0_ENDO,0 ;end offset is always zero
MOV RH.RH0_ENDS,DX ;end of VDISK or end of buffer
MOV RH.RH0_BPBO,OFFSET BPB_PTR
MOV RH.RH0_BPBS,CS ;BPB array address
POP DS ;restore DS
RET
FILL_RH ENDP
SUBTTL WRITE_MESSAGES and associated routines
PAGE
;-----------------------------------------------------------------------;
; WRITE_MESSAGE writes a series of messages to the standard ;
; output device showing the VDISK parameter values actually used. ;
;-----------------------------------------------------------------------;
CHAR4 DB 'nnnn$' ;build 4 ASCII decimal digits
ASSUME DS:CSEG
WRITE_MESSAGES PROC ;display all messages
MSG IMSG ;'VDISK virtual disk $'
test CS:err_flag2,err_baddos
jz check_dos_version
msg errm8
ret
;If DOS Version 3.x is in use, the Request Header contains a drive code
;that is displayed to show which drive letter was assigned to this
;VDISK. This field is not present in the DOS Version 2 Request Header.
check_dos_version:
MOV AH,DOS_VERS ;get DOS version call
INT DOS ;invoke DOS
CMP AL,3 ;DOS Version 3 or greater?
JB WRITE_MESSAGES_A ;no, bypass drive letter
PUSH DS ;preserve DS
LDS BX,RH_PTRA ;get Request Header Address
MOV DL,RH.RH0_DRIV ;get drive code
ADD DL,'A' ;convert to drive letter
POP DS ;restore DS
MOV AH,DOS_PCHR ;function code to write character in DL
INT DOS ;display drive letter
MOV DL,':' ;display trailing colon
INT DOS
WRITE_MESSAGES_A:
MSG MSGCRLF ;end the first line
;If any of the user specified values has been adjusted, issue an
;appropriate message
TEST ERR_FLAG,ERR_BSIZE ;was buffersize adjusted?
JZ WRITE_MESSAGES_B ;if not, skip message
MSG ERRM1 ;buffer size adjusted
WRITE_MESSAGES_B:
TEST ERR_FLAG,ERR_SSZ ;was sector size adjusted?
JZ WRITE_MESSAGES_C ;if not, skip message
MSG ERRM2 ;sector size adjusted
WRITE_MESSAGES_C:
TEST ERR_FLAG,ERR_DIRN ;were directory entries adjusted?
JZ WRITE_MESSAGES_D0 ;if not, skip message
MSG ERRM3 ;directory entries adjusted
WRITE_MESSAGES_D0:
TEST ERR_FLAG,ERR_ESIZE ;was transfer size adjusted?
JZ WRITE_MESSAGES_D ;if not, skip message
MSG ERRM7 ;transfer size adjusted
WRITE_MESSAGES_D:
TEST ERR_FLAG,ERR_SWTCH ;was an invalid switch character found?
JZ WRITE_MESSAGES_E ;if not, skip message
MSG ERRM5 ;invalid switch character
WRITE_MESSAGES_E:
TEST ERR_FLAG,ERR_SYSSZ ;is system size too small to install?
JZ WRITE_MESSAGES_F ;if not, bypass error message
MSG ERRM4 ;too large for system storage
RET ;skip messages showing adjusted sizes
WRITE_MESSAGES_F:
TEST ERR_FLAG,ERR_EXTSW ;extender card switches wrong?
JZ WRITE_MESSAGES_G ;if not, bypass error message
MSG ERRM6 ;extender card switches wrong msg
RET ;skip remaining messages
WRITE_MESSAGES_G: ;display adjusted size messages
MSG MSG1 ;buffer size:
MOV DX,BUFF_SIZE ;buffer size in binary
CALL STOR_SIZE ;convert binary to ASCII decimal
MSG CHAR4 ;print 4 decimals
MSG MSG2 ;KB,CR,LF
MSG MSG3 ;sector size:
MOV DX,BPB_SSZ
CALL STOR_SIZE ;convert binary to ASCII decimal
MSG CHAR4 ;print 4 decimals
MSG MSGCRLF ;finish off line
MSG MSG4 ;directory entries:
MOV DX,BPB_DIRN ;number of directory entries
CALL STOR_SIZE
MSG CHAR4 ;print 4 decimals
MSG MSGCRLF ;finish off the line
CMP CS:EM_SW,0 ;extended memory ?
JE END_LINE ;
MSG MSG5 ;transfer size:
MOV DX,MAXSEC_TRF
CALL STOR_SIZE ;convert binary to ASCII decimal
MSG CHAR4 ;print 4 decimals
MSG MSGCRLF ;finish off line
END_LINE:
MSG MSGCRLF ;one more blank line to set it off
RET ;return to INIT_P1
;SHOW_MSG displays a string at DS:DX on the standard output device
;String is terminated by a $
SHOW_MSG PROC ;display string at DS:DX
PUSH AX ;preserve AX across call
MOV AH,DOS_PSTR ;DOS function code
INT DOS ;invoke DOS print string function
POP AX ;restore AX
RET
SHOW_MSG ENDP
;STOR_SIZE converts the content of DX to 4 decimal characters in CHAR4
;(DX must be <= 9999)
STOR_SIZE PROC ;convert DX to 4 decimals in CHAR4
;develop 4 packed decimal digits in AX
XOR AX,AX ;clear result register
MOV CX,16 ;shift count
STOR_SIZE_B:
SHL DX,1 ;shift high bit into carry
ADC AL,AL ;double AL, carry in
DAA ;adjust for packed decimal
XCHG AL,AH
ADC AL,AL ;double high byte, carry in
DAA
XCHG AL,AH
LOOP STOR_SIZE_B ;AX contains 4 packed decimal digits
PUSH CS
POP ES ;point ES:DI to output string
MOV DI,OFFSET CHAR4
MOV CX,1310H ;10H in CL is difference between blank and zero
;13H in CH is decremented and ANDed to force
;last character not to be zero suppressed
PUSH AX ;save AX on stack
MOV DL,AH ;2 decimals to DL
CALL STOR_SIZE_2 ;display DL as 2 decimal characters
POP DX ;bring low 2 decimals into DL
STOR_SIZE_2: ;display DL as 2 decimal characters
MOV DH,DL ;save 2 decimals in DH
SHR DL,1 ;shift high order decimal right to low position
SHR DL,1
SHR DL,1
SHR DL,1
CALL STOR_SIZE_1 ;display low nibble of DL
MOV DL,DH ;get low decimal from pair
STOR_SIZE_1: ;display low nibble of DL as 1 decimal char
AND DL,0FH ;clear high nibble
JZ STOR_SIZE_Z ;if digit is significant,
XOR CL,CL ;defeat zero suppression
STOR_SIZE_Z:
DEC CH ;decrement zero suppress counter
AND CL,CH ;always display least significant digit
OR DL,'0' ;convert packed decimal to ASCII
SUB DL,CL ;zero suppress (nop or change '0' to ' ')
MOV AL,DL ;char to DL
STOSB ;store char at ES:DI, increment DI
RET
STOR_SIZE ENDP
WRITE_MESSAGES ENDP
INIT_P1 ENDP ;end of INIT part one
;-----------------------------------------------------------------------;
; VDISK Message definitions ;
;-----------------------------------------------------------------------;
IMSG DB 'VDISK virtual disk ','$'
ERRM1 DB ' Buffer size adjusted',CR,LF,'$'
ERRM2 DB ' Sector size adjusted',CR,LF,'$'
ERRM3 DB ' Directory entries adjusted',CR,LF,'$'
ERRM4 DB ' VDISK not installed - insufficient memory'
DB CR,LF,CR,LF,BEL,'$'
ERRM5 DB ' Invalid switch character',CR,LF,'$'
ERRM6 DB ' VDISK not installed - Extender Card switches'
DB CR,LF
DB ' do not match system memory size'
DB CR,LF,CR,LF,BEL,'$'
ERRM7 DB ' Transfer size adjusted',CR,LF,'$'
ERRM8 DB ' VDISK not installed - Incorrect DOS version'
DB CR,LF,CR,LF,BEL,'$'
MSG1 DB ' Buffer size: $'
MSG2 DB ' KB'
MSGCRLF DB CR,LF,'$'
MSG3 DB ' Sector size: $'
MSG4 DB ' Directory entries: $'
MSG5 DB ' Transfer size: $'
MSGEND LABEL BYTE ; End of message text
CSEG ENDS
END
|