home *** CD-ROM | disk | FTP | other *** search
/ The Unsorted BBS Collection / thegreatunsorted.tar / thegreatunsorted / programming / asm_programming / AS03.ZIP / RAMDISK.ASM < prev    next >
Assembly Source File  |  1985-03-18  |  72KB  |  2,074 lines

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