home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PROGRAMS / UTILS / FLOPPIES / VHARD101.ZIP / VHARD.ASM < prev    next >
Encoding:
Assembly Source File  |  1990-07-22  |  56.8 KB  |  2,120 lines

  1.  
  2.     title    Fake hard disk driver
  3.     subttl    Prologue
  4.     page    60,132
  5.  
  6. comment    {
  7.  
  8. ******************************************************************************
  9.  
  10. File VHARD.ASM
  11.  
  12. Author:
  13.     Aaron L. Brenner
  14.  
  15.     BIX mail address albrenner
  16.     GEnie address A.BRENNER
  17.  
  18.     This program is hereby released to the public domain.
  19.  
  20. Purpose:
  21.     A DOS device driver that will make a floppy drive double as a small
  22.     hard disk.
  23.  
  24.     This is accomplished by spreading the fake HD over a series of
  25.     diskettes. The disks must be prepared (with VHPREP) by formatting to
  26.     10 sectors/track for a capacity of 400KB per diskette. Also, VHPREP
  27.     will stamp each diskette with a unique ID built from the date and time
  28.     the first diskette was prepared and the sequence number of it. VHPREP
  29.     is also responsible for performing the low and high level format on
  30.     all disks. Formatting is performed by the driver, but under VHPREP
  31.     control.
  32.  
  33.     VHPREP communicates with the driver via a dummy character device
  34.     driver named VHARDCTL, which will provide access to driver functions.
  35.     The only driver command supported is IOCTL Write (all others are
  36.     NOPs), and only 7 bytes are actually used. These 7 bytes are treated
  37.     as a command block, structured as follows:
  38.  
  39.     Format command
  40.     byte     0
  41.     byte    track to format (head in bit 7, cylinder in low 7 bits)
  42.     byte    return status
  43.     dword    pointer to format parameter buffer (see details in int 13h)
  44.  
  45.     Read Track command
  46.     byte    1
  47.     byte    track to read (head in bit 7, cylinder in low 7 bits)
  48.     byte    return status
  49.     dword    pointer to buffer to read into
  50.  
  51.     Write Track command
  52.     byte    2
  53.     byte    track to write (head in bit 7, cylinder in low 7 bits)
  54.     byte    return status
  55.     dword    pointer to buffer to write from
  56.  
  57.     Verify Track command
  58.     byte    3
  59.     byte    track to verify (head in bit 7, cylinder in low 7 bits)
  60.     byte    return status
  61.  
  62.     Set FAT cache buffer
  63.     byte    4
  64.     byte    == 0, disable cache
  65.         == 1, enable cache
  66.         == 2, flush cache
  67.         == 3, disable cache autoflush
  68.         == 4, enable cache autoflush (DOS 3+, SHARE installed)
  69.         == 5, return information about the cache
  70.     byte    return status
  71.     dword    pointer to cache buffer
  72.     This command is used to enable or disable the FAT/directory cache.
  73.     Once the cache is enabled, logical sectors 1 through 24 will be cached
  74.     for both read and write. They will only be written when driver
  75.     function 14 (Device close) is called AND the reference count is 0.
  76.     To accomodate older versions of DOS, subcommand 2 will manually flush
  77.     the cache to disk. Subcommands 3 & 4 will disable or enable the
  78.     auto-flush.
  79.  
  80.     Retrieve Driver Data
  81.     byte    5
  82.     byte    unused
  83.     byte    set to 0 by VHARD
  84.     dword    pointer to buffer to receive the following data:
  85.         byte        drive number assigned by DOS
  86.         word        VHARD version
  87.         19 bytes    BPB for VHARD
  88.  
  89. Revision history:
  90. 0.90    06/27/90    ALB    Created. This version uses 13 360KB diskettes
  91.                 for appx. 5MB of faked hard disk space. Note:
  92.                 cache handling is NOT yet implemented.
  93.  
  94. ******************************************************************************
  95.  
  96. endcomment {
  97.  
  98.     subttl    Included files
  99.     page
  100.  
  101.     include    dd_defs.inc
  102.     include    vhard.inc        ; Some structures we use
  103.  
  104. DEBUG    equ    0
  105.  
  106.  
  107.     subttl    Macros and templates
  108.     page
  109.  
  110. BIOS_seg    segment at 40h
  111.  
  112.     org    62h
  113. active_page    db    ?
  114.  
  115.     org    84h
  116. crt_lines    db    ?
  117.  
  118. BIOS_seg    ends
  119.  
  120.  
  121. ;*****************************************************************************
  122. ;
  123. ; Version of VHARD
  124. ;
  125. ;*****************************************************************************
  126. VHARD_version    equ    100h
  127. VHARD_text_ver    equ    <'1.00'>
  128.  
  129.  
  130.     subttl    Device driver headers
  131.     page
  132.  
  133. vhard_seg    segment
  134.  
  135.     org    0
  136.  
  137. assume    cs:vhard_seg, ds:nothing, es:nothing, ss:nothing
  138.  
  139. ;
  140. ; Declare the header for the dummy char device used to communicate with
  141. ; the block driver
  142. ;
  143.         dw    offset next_hdr
  144. nextdvrseg    dw    -1
  145.         dw    DA_IS_CHAR or DA_IOCTL
  146.         dw    strategy
  147.         dw    du_int
  148.         db    'VHARDCTL'
  149.  
  150. ;
  151. ; Declare the header for the device driver
  152. ; Attributes:
  153. ;    block device
  154. ;    removable media
  155. ;
  156. next_hdr    dw    -1, -1
  157.         dw    DA_IS_BLK or DA_IS_REM
  158.         dw    strategy
  159.         dw    vh_int
  160.         db    1, 'VHARD  '
  161.  
  162.     subttl    Device driver data
  163.     page
  164.  
  165. ;
  166. ; Pointer passed by DOS to the request packet
  167. ;
  168. req_ptr        dw    0, 0
  169.  
  170. ;
  171. ; Version of DOS
  172. ;
  173. dos_ver        dw    0
  174.  
  175. ;
  176. ; Local copy of the diskette parameters
  177. ;
  178. diskparms    db    4 dup(0)    ; Gets filled in at init time
  179. sect_per_track    db    0        ; Sectors per track (set at init time)
  180. gap_len        db    0
  181.         db    0
  182. fmt_gap_len    db    0
  183.         db    3 dup(0)
  184.  
  185. ;
  186. ; Save area for old parm pointer
  187. ;
  188. old_pptr    dw    0, 0
  189.  
  190. ;
  191. ; ID and # for the current disk
  192. ;
  193. curr_disk_id    db    'NO NAME',0
  194.         db    0
  195. curr_disk_num    db    0ffh
  196.  
  197. ;
  198. ; Counter of how many sectors read/written
  199. ;
  200. io_count    dw    0
  201.  
  202. ;
  203. ; Count of how many sectors to go
  204. ;
  205. io_to_go    dw    0
  206.  
  207. ;
  208. ; Flag indicating whether or not we have EGA/VGA video available. This is used
  209. ; in the prompt_user routine - if have_evga != 0, then we read the number of
  210. ; character lines on screen from 40:84. If have_evga == 0, then we always use
  211. ; line 24.
  212. ;
  213. have_evga    db    0
  214. last_curs    dw    0
  215.  
  216. ;
  217. ; If we're running under DOS 3.10 or higher, we get the drive number assigned
  218. ; from the the request block and save it here.
  219. ;
  220. our_drive    db    0ffh
  221.  
  222. ;
  223. ; Data used to manage the FAT/dir cache.
  224. ;
  225. cache_ptr    dw    0,0        ; Pointer to the cache
  226. cache_flags    db    0        ; Bit flags
  227.  
  228. ;
  229. ; Messages that get displayed at various times
  230. ;
  231. need_disk    db    'Put disk ID '
  232. pr_disk_id    db    8 dup(' ')
  233.         db    ' #'
  234. pr_disk_num    db    '  '
  235.         db    ' in drive A:, then press a key',0
  236.  
  237. need_first_disk    db    'Put disk #0 of the set you want to use'
  238.         db    ' in drive A:, then press a key',0
  239.  
  240. ;
  241. ; Save area for the chunk of screen we diddle
  242. ;
  243. screen_save    dw    80 dup(0)
  244.  
  245. ;
  246. ; Buffer for the boot sector (containing ID, etc.)
  247. ;
  248. boot_sect_buf    db    512 dup(0)
  249.  
  250. ;*****************************************************************************
  251. ;
  252. ; These 2 tables are used to translate BIOS error codes into the status codes
  253. ; expected by DOS and VHPREP, respectively
  254. ;
  255. ;*****************************************************************************
  256. DOS_sts_table    db    80h,    DE_NOT_READY
  257.         db    40h,    DE_SEEK_ERROR
  258.         db    20h,    DE_GEN_FAIL
  259.         db    10h,    DE_CRC_ERROR
  260.         db    9,    DE_WRITE_FAULT
  261.         db    8,    DE_WRITE_FAULT
  262.         db    4,    DE_NOT_FOUND
  263.         db    3,    DE_WRITE_PROT
  264.         db    2,    DE_GEN_FAIL
  265.         db    1,    DE_GEN_FAIL
  266.  
  267. CMD_sts_table    db    80h,    STS_TIMEOUT
  268.         db    40h,    STS_BAD_SEEK
  269.         db    20h,    STS_BAD_NEC
  270.         db    10h,    STS_BAD_CRC
  271.         db    9,    STS_DMA_BOUND
  272.         db    8,    STS_BAD_DMA
  273.         db    4,    STS_NOT_FND
  274.         db    3,    STS_WRITE_PROT
  275.         db    2,    STS_ADDR_MARK
  276.         db    1,    STS_BAD_CMD
  277.  
  278.  
  279. ;*****************************************************************************
  280. ;
  281. ; BIOS parameter block (BPB) that DOS is going to use
  282. ;
  283. ;*****************************************************************************
  284. our_bpb    DOS_BPB    <512, 8, 1, 2, 256, (13 * 799), 0fch, 4, 47, 26, 0>
  285.  
  286. our_bpb_array    dw    offset our_bpb
  287.  
  288.  
  289. ;*****************************************************************************
  290. ;
  291. ; For this version, at least, this driver will support the OPEN and CLOSE
  292. ; calls. These will increment and decrement a reference counter that will be
  293. ; used to know when to the write the FAT cache (if caching is enabled).
  294. ;
  295. ;*****************************************************************************
  296. ref_count    dw    0
  297.  
  298.  
  299.     subttl    Device driver STRATEGY and INTERRUPT routines
  300.     page
  301.  
  302. ;*****************************************************************************
  303. ;
  304. ; Strategy entry point. Used for both drivers
  305. ;
  306. ;*****************************************************************************
  307. strategy    proc    far
  308.  
  309.     mov    cs:req_ptr[0],bx    ; Save the pointer
  310.     mov    cs:req_ptr[2],es    ;
  311.     ret                ; Return to DOS
  312.  
  313. strategy    endp
  314.  
  315.  
  316. ;*****************************************************************************
  317. ;
  318. ; Interrupt routine for the VH driver
  319. ;
  320. ;*****************************************************************************
  321. vh_int        proc    far
  322.  
  323. assume    ds:nothing, es:nothing
  324.  
  325.     call    save_regs        ; Save the registers
  326.     cld                ; Make sure of direction
  327.     push    cs            ; Set DS to our segment
  328.     pop    ds            ;
  329.  
  330. assume    ds:vhard_seg
  331.  
  332. if    DEBUG
  333.     call    dump_entry
  334. endif
  335.  
  336.     les    si,dword ptr req_ptr    ; Point to the request packet
  337.     mov    bl,es:[si].DDrh_command    ; Get the command
  338.     mov    es:[si].DDrh_status,0    ; Default to no error
  339.     cmp    bl,DC_REM_MEDIA        ; Check for the highest we support
  340.     jbe    vhint_l1        ; Use it - it's in range
  341.     call    err_fnc            ; Return that it's a bad command
  342.     jmp    short vhint_exit    ; And return
  343. vhint_l1:
  344.     sub    bh,bh            ; Make the command a word
  345.     shl    bx,1            ; Now a word offset
  346.     call    vh_dispatch[bx]        ; Vector to the routine
  347.     les    si,dword ptr req_ptr    ; Point back to the request packet
  348.     or    es:[si].DDrh_status,DS_DONE; Say we're done
  349. vhint_exit:
  350.  
  351. if    DEBUG
  352.     call    dump_exit
  353. endif
  354.  
  355.     call    rest_regs        ; Restore the registers
  356.     ret                ; Return to DOS
  357.  
  358. vh_int        endp
  359.  
  360.  
  361. if    DEBUG
  362. entry_str    db    'Entry:',0
  363. exit_str    db    'Exit: ',0
  364.  
  365. dump_entry    proc    near
  366.  
  367.     mov    si,offset entry_str
  368.     jmp    short dump_either
  369.  
  370. dump_entry    endp
  371.  
  372. dump_exit    proc    near
  373.  
  374.     mov    si,offset exit_str
  375. dump_either:
  376.     call    outp_str
  377.     les    si,req_ptr
  378.     mov    cl,es:[si]
  379.     sub    ch,ch
  380. dmpe_l1:
  381.     mov    al,' '
  382.     call    outp_char
  383.     lods    byte ptr es:[si]
  384.     call    outp_byte
  385.     loop    dmpe_l1
  386.     mov    al,13
  387.     call    outp_char
  388.     mov    al,10
  389.     call    outp_char
  390.     ret
  391.  
  392. dump_exit    endp
  393.  
  394. outp_str    proc    near
  395.  
  396.     lodsb
  397.     or    al,al
  398.     jz    otps_l1
  399.     call    outp_char
  400.     jmp    short outp_str
  401. otps_l1:
  402.     ret
  403.  
  404. outp_str    endp
  405.     
  406. outp_char    proc    near
  407.  
  408.     push    ax
  409.     push    dx
  410.     sub    ah,ah
  411.     sub    dx,dx
  412.     int    17h
  413.     pop    dx
  414.     pop    ax
  415.     ret
  416.  
  417. outp_char    endp
  418.  
  419. outp_byte    proc    near
  420.  
  421.     push    ax
  422.     shr    al,1
  423.     shr    al,1
  424.     shr    al,1
  425.     shr    al,1
  426.     call    outp_nyb
  427.     pop    ax
  428. outp_nyb:
  429.     and    al,0fh
  430.     add    al,90h
  431.     daa
  432.     adc    al,40h
  433.     daa
  434.     call    outp_char
  435.     ret
  436.  
  437. outp_byte    endp
  438. endif
  439.  
  440. ;*****************************************************************************
  441. ;
  442. ; Interrupt routine for the dummy VHARDCTL driver
  443. ;
  444. ;*****************************************************************************
  445. du_int        proc    far
  446.  
  447.     call    save_regs        ; Save all the registers
  448.     push    cs            ; Set DS to our segment
  449.     pop    ds
  450.  
  451. assume    ds:vhard_seg
  452.  
  453.     les    si,dword ptr req_ptr    ; Point to the request packet
  454.     mov    bl,es:[si].DDrh_command    ; Get the command
  455.     cmp    bl,DC_IOCTL_WRITE    ; Check for the highest we support
  456.     jbe    duint_l1        ; Use it - it's in range
  457.     call    err_fnc            ; Return that it's a bad command
  458.     jmp    short duint_exit    ; And return
  459. duint_l1:
  460.     sub    bh,bh            ; Make the command a word
  461.     shl    bx,1            ; Now a word offset
  462.     call    du_dispatch[bx]        ; Vector to the routine
  463.     les    bx,dword ptr req_ptr    ; Point back to the request packet
  464.     or    es:[bx].DDrh_status,DS_DONE; Say we're done
  465.     mov    es:[bx].DDir_count,size VH_CMD
  466. duint_exit:
  467.     call    rest_regs        ; Restore them now
  468.     ret                ; Return to DOS
  469.  
  470. du_int        endp
  471.  
  472.  
  473.     subttl    Dispatch tables for INTERRUPT routines
  474.     page
  475.  
  476. ;*****************************************************************************
  477. ;
  478. ; Dispatch table for the VH driver
  479. ;
  480. ;*****************************************************************************
  481. vh_dispatch    dw    drv_init    ; 0 - Init
  482.         dw    vh_med_check    ; 1 - Media check
  483.         dw    vh_build_bpb    ; 2 - Build BPB
  484.         dw    err_fnc        ; 3 - IOCTL read (not supported)
  485.         dw    vh_read        ; 4 - Read
  486.         dw    err_fnc        ; 5 - Peek input (not for blk)
  487.         dw    err_fnc        ; 6 - Input status (not for blk)
  488.         dw    err_fnc        ; 7 - Input flush (not for blk)
  489.         dw    vh_write    ; 8 - Write
  490.         dw    vh_write    ; 9 - Write w/verify
  491.         dw    err_fnc        ; A - Output status (not for blk)
  492.         dw    err_fnc        ; B - Output flush (not for blk)
  493.         dw    err_fnc        ; C - IOCTL write (not supported)
  494.         dw    vh_open        ; D - Device open
  495.         dw    vh_close    ; E - Device close
  496.         dw    vh_set_rem    ; F - "Removable media" check
  497.  
  498. du_dispatch    dw    du_init        ; 0 - Init
  499.         dw    err_fnc        ; 1 - Media check
  500.         dw    err_fnc        ; 2 - Build BPB
  501.         dw    err_fnc        ; 3 - IOCTL read (not supported)
  502.         dw    err_fnc        ; 4 - Read
  503.         dw    err_fnc        ; 5 - Peek input (not for blk)
  504.         dw    err_fnc        ; 6 - Input status (not for blk)
  505.         dw    err_fnc        ; 7 - Input flush (not for blk)
  506.         dw    err_fnc        ; 8 - Write
  507.         dw    err_fnc        ; 9 - Write w/verify
  508.         dw    err_fnc        ; A - Output status (not for blk)
  509.         dw    err_fnc        ; B - Output flush (not for blk)
  510.         dw    du_write    ; C - IOCTL Write
  511.  
  512.  
  513.     subttl    Error function
  514.     page
  515.  
  516. ;*****************************************************************************
  517. ;
  518. ; Error function - sets the error bit to 1, and the error code to 3 (Unknown
  519. ; command).
  520. ;
  521. ;*****************************************************************************
  522. err_fnc        proc    near
  523.  
  524.     mov    al,DE_BAD_COMMAND    ; Set error code
  525. ;
  526. ; Alternate entry point. Call with AL set to error code
  527. ;
  528. err_return:
  529.     mov    ah,DS_ERROR shr 8    ; Set ERROR bit
  530.     les    bx,dword ptr req_ptr    ; Point to the request packet
  531.     mov    es:[bx].DDrh_status,ax    ; Set return status
  532.     ret                ; Return to caller
  533.  
  534. err_fnc        endp
  535.  
  536.  
  537.     subttl    Driver function 1 - Media check
  538.     page
  539.  
  540. ;*****************************************************************************
  541. ;
  542. ; Driver function 1 - Media check
  543. ;
  544. ;*****************************************************************************
  545. vh_med_check    proc    near
  546.  
  547. assume    ds:vhard_seg, es:nothing
  548.  
  549.     call    set_disk_parms        ; Set up the diskette parameters
  550.     call    check_boot_sect        ; See if the disk ID has changed
  551.     jc    vhmc_error        ; Exit if error
  552.     jz    vhmc_nochange        ; Return properly if no change
  553.     les    si,dword ptr req_ptr    ; Point to the DOS request block
  554.     mov    es:[si].DDmr_return,0ffh; Note that it's changed
  555.     test    cache_flags,CACHE_INSTALLED    ; Is the cache there?
  556.     jz    vhmc_l1                ; Nope - never mind
  557.     test    cache_flags,CACHE_DIRTY    ; Is there anything in it?
  558.     jz    vhmc_l1            ; Nope - never mind
  559.     call    do_flush_cache        ; Flush the FAT cache
  560. vhmc_l1:
  561.     and    cache_flags,not (CACHE_VALID or CACHE_DIRTY)
  562.     mov    ref_count,0        ; Force the ref count to 0
  563.     call    get_disk_0        ; Make sure we get the first disk
  564.     cmp    byte ptr dos_ver[0],3    ; Is it DOS 3.x (or higher)?
  565.     jb    vhmc_exit        ; Nope - exit
  566.     mov    word ptr es:[si].DDmr_idptr[0],offset curr_disk_id
  567.     mov    word ptr es:[si].DDmr_idptr[2],cs
  568.     jmp    short vhmc_exit        ;
  569. vhmc_nochange:
  570.     les    si,dword ptr req_ptr    ; Point to the DOS request block
  571.     mov    es:[si].DDmr_return,1    ; Set the return value
  572.     test    cache_flags,CACHE_INSTALLED    ; Is the cache there?
  573.     jz    vhmc_exit        ; No - never mind
  574.     test    cache_flags,CACHE_VALID    ; Does it hold valid data?
  575.     jnz    vhmc_exit        ; Yep - just exit
  576.     call    get_disk_0        ; Load the cache
  577.     jmp    short vhmc_exit        ; Exit now
  578. vhmc_error:
  579.     mov    ax,DE_NOT_READY        ;
  580.     call    err_return        ; Indicate the error
  581. vhmc_exit:
  582.     call    rest_disk_parms        ; Restore the diskette parameters
  583.     ret                ; Return to dispatcher
  584.  
  585. vh_med_check    endp
  586.  
  587.  
  588.     subttl    Driver function 2 - Build BPB
  589.     page
  590.  
  591. ;*****************************************************************************
  592. ;
  593. ; Driver function 2 - Build BPB
  594. ;
  595. ; Also, the disk ID is gotten from the disk in the drive. If there is no disk
  596. ; in the drive, we prompt for it.
  597. ;
  598. ;*****************************************************************************
  599. vh_build_bpb    proc    near
  600.  
  601. assume    ds:vhard_seg, es:nothing
  602.  
  603.     save    si, es
  604.     mov    word ptr es:[si].DDbr_bpb_ptr[0],offset our_bpb
  605.     mov    word ptr es:[si].DDbr_bpb_ptr[2],cs
  606.     call    get_disk_0        ; Get the first disk
  607.     jnc    vhbb_exit        ; If they didn't press Esc, exit
  608.     mov    ax,DE_NOT_READY        ; Say that the drive wasn't ready
  609.     call    err_return        ;
  610. vhbb_exit:
  611.     restore
  612.     ret                ; Return to DOS
  613.  
  614. vh_build_bpb    endp
  615.  
  616.  
  617.     subttl    Driver function 4 - Read
  618.     page
  619.  
  620. ;*****************************************************************************
  621. ;
  622. ; Driver function 4 - Read
  623. ;
  624. ;*****************************************************************************
  625. vh_read        proc    near
  626.  
  627. assume    ds:vhard_seg, es:nothing
  628.  
  629.     mov    bp,offset sect_read    ; Address of I/O routine
  630.     jmp    short vhwt_l1        ; Do the I/O operation
  631.  
  632. vh_read        endp
  633.  
  634.  
  635.     subttl    Driver functions 8 & 9 - Write & Write w/Verify
  636.     page
  637.  
  638. ;*****************************************************************************
  639. ;
  640. ; Driver function 8 - Write (also fn 9, Write w/Verify)
  641. ;
  642. ;*****************************************************************************
  643. vh_write    proc    near
  644.  
  645. assume    ds:vhard_seg, es:nothing
  646.  
  647.     mov    bp,offset sect_write    ; Point to the right routine
  648. vhwt_l1:
  649.     call    set_disk_parms        ; Set up the diskette parameters
  650.     mov    ax,es:[si].DDir_start    ; Get starting sector
  651.     mov    cx,es:[si].DDir_count    ; Get the count
  652.     les    bx,es:[si].DDir_buffer    ; Point to the buffer
  653.     or    ax,ax            ; See if DOS wants a sector that falls
  654.     jz    vhwt_l99        ;  in the range 1 to 24
  655.     cmp    ax,24            ;
  656.     ja    vhwt_l99        ;
  657.     test    cache_flags,CACHE_INSTALLED    ; Is the cache there?
  658.     jz    vhwt_l99        ; No - never mind
  659.     test    cache_flags,CACHE_VALID    ; Do we have valid data in the cache?
  660.     jz    vhwt_l99        ; No - read from disk
  661.     dec    ax            ; Adjust start sector for cache
  662.     push    cx            ; Save the count
  663.     mov    cl,9            ; Make sector # offset into cache
  664.     shl    ax,cl            ;
  665.     pop    cx
  666.     mov    dx,cx            ; Turn sector count into byte count
  667.     mov    cl,9            ;
  668.     shl    dx,cl            ;
  669.     mov    cx,dx            ;
  670.     cmp    bp,offset sect_read    ; Are we reading?
  671.     jne    vhwt_l2            ; No - set up for writing
  672.     lds    si,dword ptr cache_ptr    ; Point to the cache
  673.     add    si,ax            ; Point to start of first sector
  674.     mov    di,bx            ; Point into the buffer
  675.     jmp    short vhwt_l3        ; Continue
  676. vhwt_l2:
  677.     or    cache_flags,CACHE_DIRTY    ; The cache is dirty now
  678.     push    es            ; Need DS:SI pointing to the buffer
  679.     pop    ds            ;
  680.     mov    si,bx            ;
  681.     les    di,dword ptr cs:cache_ptr; And ES:DI pointing to the cache
  682.     add    di,ax            ; Point to the sector in the cache
  683. vhwt_l3:
  684.     shr    cx,1            ; Move as many words as possible
  685.     rep    movsw            ;
  686.     rcl    cx,1            ; Move the odd byte
  687.     rep    movsb            ;
  688.     push    cs            ; Set DS back to our segment
  689.     pop    ds            ;
  690.     jmp    short vhwt_exit        ; Exit now
  691. vhwt_l99:
  692.     call    io_operation        ; Do the I/O operation
  693.     jnc    vhwt_exit        ; Exit if no error
  694.     mov    bx,offset DOS_sts_table    ; Point to translation table
  695.     call    xlat_status        ; Make it a DOS error code
  696.     cmp    al,DE_WRITE_FAULT    ; Generic "Write Fault"?
  697.     jne    vhwt_err        ; No - just exit
  698.     cmp    bp,offset sect_write    ; Was it a write op?
  699.     je    vhwt_err        ; Yep - never mind
  700.     mov    al,DE_READ_FAULT    ; Make that a "Read Fault"
  701. vhwt_err:
  702.     call    err_return        ; Return the error
  703. vhwt_exit:
  704.     call    rest_disk_parms        ; Restore the disk parameters
  705.     ret                ; Return to int routine
  706.  
  707. vh_write    endp
  708.  
  709.  
  710. ;*****************************************************************************
  711. ;
  712. ; Perform a disk I/O operation.
  713. ;
  714. ; Input:
  715. ;    AX        starting sector
  716. ;    CX        # of sectors
  717. ;    ES:BX        pointer to buffer
  718. ;    BP        address of routine to call to read or write
  719. ;
  720. ; Output:
  721. ;    CF = 1        an error occurred
  722. ;            AL = error code to return
  723. ;    CF = 0:
  724. ;    CX        number of sectors actually transferred
  725. ;
  726. ; Registers:
  727. ;    All but AX and CX preserved.
  728. ;
  729. ;*****************************************************************************
  730. io_operation    proc    near
  731.  
  732.     mov    io_to_go,cx        ; Save count as number to do
  733.     mov    di,ax            ; Save start sector for a bit
  734.     sub    ax,ax            ; Figure out if it could wrap
  735.     dec    ax            ;
  736.     sub    ax,bx            ;
  737.     mov    cl,9            ;
  738.     shr    ax,cl            ;
  739.     cmp    ax,io_to_go        ; Can it hold that many?
  740.     jae    io_op1            ; Yep - never mind
  741.     mov    io_to_go,ax        ; Set to just that many
  742. io_op1:
  743.     sub    ax,ax            ; Clear the current count
  744.     mov    io_count,ax        ;
  745.     mov    ax,di            ; Get the starting sector back
  746.     call    calc_disk        ; Figure out where that is
  747.     call    get_num_disk        ; Get the right disk
  748.     jc    io_op_error        ; Don't bother if they hit Esc or error
  749. ;
  750. ; At this point, we have figured out what disk to use, and it's in the drive.
  751. ; Now, all we have to do is start reading or writing.
  752. ;
  753.     mov    al,11            ; Figure out how many sectors to start
  754.     sub    al,cl            ;
  755.     cbw                ;
  756.     mov    si,ax            ; Save it for later
  757. io_op3:
  758.     cmp    si,io_to_go        ; See if there are that many to go
  759.     jbe    io_op4            ; Yep - just do the operation
  760.     mov    si,io_to_go        ; Just do that many
  761.     mov    ax,si            ;
  762. io_op4:
  763.     call    bp            ; Call the appropriate routine
  764.     jc    io_op_err2        ; Exit if error
  765. io_op5:
  766.     add    io_count,si        ; Add to number complete
  767.     sub    io_to_go,si        ; Knock that many off the count to do
  768.     jz    io_op_exit        ; If it went to 0, we're done
  769.     mov    ax,si            ;
  770.     push    cx            ; Save it (gets used for shift count)
  771.     mov    cl,9            ; Multiply by 512
  772.     shl    ax,cl            ;
  773.     pop    cx            ;
  774.     add    bx,ax            ; Move the buffer pointer
  775.     jnc    io_op6            ; If it wraps, we're done
  776.     clc                ; No error
  777.     jmp    short io_op_exit    ; Just exit
  778. io_op6:
  779.     mov    al,10            ; Do 10 the next time 'round
  780.     cbw                ;
  781.     mov    si,ax            ;
  782.     mov    cl,1            ; Start at sector 1
  783.     xor    dh,1            ; Next head (0 or 1)
  784.     jnz    io_op3            ; If just going to other head, loop
  785.     inc    ch            ; Next track
  786.     cmp    ch,40            ; Still on this disk?
  787.     jb    io_op3            ; Yep - do more I/O
  788.     inc    curr_disk_num        ; Next disk
  789.     mov    al,curr_disk_num    ; Get the disk number
  790.     call    get_num_disk        ; Get that disk in the drive
  791.     jc    io_op_error        ; Exit if error or Esc pressed
  792.     mov    cx,2            ; Start back at track 0, sector 2
  793.     mov    ax,9            ; Do up to 9 more sectors on next disk
  794.     mov    si,ax            ;
  795.     jmp    short io_op3        ; Loop back
  796. io_op_error:
  797.     mov    ah,80h            ; Say that it wasn't ready
  798. io_op_err2:
  799.     stc                ;
  800. io_op_exit:
  801.     mov    cx,io_count        ; Get number transferred
  802.     ret                ; Return to caller
  803.  
  804. io_operation    endp
  805.  
  806.  
  807.     subttl    Driver function 0DH - Device open
  808.     page
  809.  
  810. ;*****************************************************************************
  811. ;
  812. ; Driver function 0DH - Device open
  813. ;
  814. ;*****************************************************************************
  815. vh_open    proc    near
  816.  
  817.     inc    ref_count        ; Just increment the reference count
  818.     ret                ; Return to caller
  819.  
  820. vh_open    endp
  821.  
  822.  
  823.     subttl    Driver function 0EH - Device close
  824.     page
  825.  
  826. ;*****************************************************************************
  827. ;
  828. ; Driver function 0EH - Device close
  829. ;
  830. ;*****************************************************************************
  831. vh_close    proc    near
  832.  
  833.     dec    ref_count        ; Decrement the reference count
  834.     jnz    vhcl_exit        ; If there are still files open, exit
  835.     test    cache_flags,CACHE_INSTALLED    ; Is the cache there?
  836.     jz    vhcl_exit            ; Nope - just exit
  837.     test    cache_flags,CACHE_AUTO    ; Do we want to flush automagically?
  838.     jz    vhcl_exit        ; Nope - just exit
  839.     test    cache_flags,CACHE_DIRTY    ; Is the cache dirty?
  840.     jz    vhcl_exit        ; Nope - never mind
  841.     call    set_disk_parms        ; Set up the diskette parameters
  842.     call    do_flush_cache        ; Flush that cache
  843.     call    rest_disk_parms        ; Get the old diskette parms back
  844. vhcl_exit:
  845.     ret                ; Return to caller
  846.  
  847. vh_close    endp
  848.  
  849.  
  850.     subttl    Driver function 0FH - "Removable media" check
  851.     page
  852.  
  853. ;*****************************************************************************
  854. ;
  855. ; Driver function 0FH - "Removable media" check
  856. ;
  857. ;*****************************************************************************
  858. vh_set_rem    proc    near
  859.  
  860.     and    es:[si].DDrh_status,not DS_BUSY    ; Say we're removable
  861.     ret                ; Return to caller
  862.  
  863. vh_set_rem    endp
  864.  
  865.  
  866.     subttl    Cache functions
  867.     page
  868.  
  869. ;*****************************************************************************
  870. ;
  871. ; Flush the FAT/root dir cache
  872. ;
  873. ; Registers:
  874. ;    All preserved.
  875. ;
  876. ;*****************************************************************************
  877. do_flush_cache    proc    near
  878.  
  879. assume    ds:vhard_seg, es:nothing
  880.  
  881.     save    ax, bx, cx, bp, es
  882.     les    bx,dword ptr cache_ptr    ; Point to the cache
  883.     mov    bp,offset sect_write    ; Routine to do the I/O
  884. dflc_l1:
  885.     mov    ax,1            ; Start with logical sector 1
  886.     mov    cx,24            ; Write 24 Sectors
  887.     call    io_operation        ;
  888.     jc    dflc_l1            ; No matter what, it must be done
  889.     and    cache_flags,not CACHE_DIRTY    ; Turn off the "dirty" bit
  890.     restore
  891.     ret                ; Return to caller
  892.  
  893. do_flush_cache    endp
  894.  
  895.  
  896.     subttl    VHARDCTL IOCTL write function
  897.     page
  898.  
  899. ;*****************************************************************************
  900. ;
  901. ; This is the only driver function supported by the dummy control driver
  902. ; VHARDCTL. It dispatches to one of the commands.
  903. ; On entry, ES:SI points to the driver request packet.
  904. ;
  905. ;*****************************************************************************
  906. du_write    proc    near
  907.  
  908. assume    ds:vhard_seg, es:nothing
  909.  
  910.     mov    es:[si].DDrh_status,0    ; Start with no error
  911.     les    bx,es:[si].DDir_buffer    ; Point to the buffer
  912.     mov    al,es:[bx].VC_cmd_code    ; Get the command to perform
  913.     cmp    al,LAST_CMD        ; Valid command?
  914.     jbe    duwr_ok            ; Yep - dispatch it
  915.     call    err_fnc            ; Report an invalid driver command
  916.     jmp    short duwr_exit        ; Exit now
  917. duwr_ok:
  918.     cbw                ; Make it a word
  919.     shl    ax,1            ; Make it a dispatch table offset
  920.     mov    di,ax            ; Into a usable register
  921.     call    cmd_dispatch[di]    ; Call the routine
  922. duwr_exit:
  923.     ret                ; Return to caller
  924.  
  925. du_write    endp
  926.  
  927.     subttl    VHARDCTL handling routines
  928.     page
  929.  
  930. ;*****************************************************************************
  931. ;
  932. ; The following routines handle the commands to VHARDCTL. Each of them expects
  933. ; ES:BX to point to the command block (NOT the driver request block!), and
  934. ; DS to be the same as CS.
  935. ;
  936. ;*****************************************************************************
  937.  
  938.  
  939. ;*****************************************************************************
  940. ;
  941. ; Called when VHARDCTL gets a "Format Track" command
  942. ;
  943. ;*****************************************************************************
  944. cmd_format_trk    proc    near
  945.  
  946. assume    ds:vhard_seg, es:nothing
  947.  
  948.     push    bp            ; Save retry counter
  949.     call    set_disk_parms        ; Set up the diskette parameters
  950.     mov    al,es:[bx].VC_track    ; Get the track/head to format
  951.     mov    ch,7fh            ; Mask to keep track
  952.     and    ch,al            ; Get the track
  953.     rol    al,1            ; Get the head
  954.     and    al,1            ;
  955.     mov    dh,al            ; Get the head in the right place
  956.     mov    dl,$OUR_DRIVE        ;
  957.     mov    bp,3            ; 3 tries
  958. cfmt_l1:
  959.     mov    ax,50ah            ; Format command
  960.     push    es            ; Save these
  961.     push    bx            ;
  962.     les    bx,es:[bx].VC_buffer    ; Point to the buffer
  963.     int    13h            ; Call BIOS to do it
  964.     pop    bx            ; Get pointer back
  965.     pop    es            ;
  966.     jnc    cfmt_l3            ; If it went, continue
  967.     dec    bp            ; Count off a try
  968.     jz    cfmt_l2            ; If it failed, return error
  969.     mov    ah,0            ; Do a disk reset
  970.     int    13h            ;
  971.     jnc    cfmt_l1            ; If it FAILED to reset, drop
  972. cfmt_l2:
  973.     push    bx            ; Save cmd block ptr
  974.     mov    bx,offset CMD_sts_table    ; Point to translation table
  975.     call    xlat_status        ; Translate to our error code
  976.     pop    bx            ; Point back to the cmd block
  977.     mov    es:[bx].VC_status,al    ; Set the return status
  978.     jmp    short cfmt_exit        ; Exit now
  979. cfmt_l3:
  980.     mov    es:[bx].VC_status,STS_OK; Report success
  981. cfmt_exit:
  982.     call    rest_disk_parms        ; Restore the diskette parameters
  983.     pop    bp            ; Get back what we used
  984.     ret                ; Return to caller
  985.  
  986. cmd_format_trk    endp
  987.  
  988.  
  989. ;*****************************************************************************
  990. ;
  991. ; Called when VHARDCTL gets a "Read Track" command
  992. ;
  993. ;*****************************************************************************
  994. cmd_read_trk    proc    near
  995.  
  996.     call    set_disk_parms        ; Set up the diskette parameters
  997.     mov    al,es:[bx].VC_track    ; Get the track to read
  998.     mov    ch,7fh            ; Get mask for the track
  999.     and    ch,al            ; Get the track
  1000.     mov    cl,1            ; Start with sector 1
  1001.     rol    al,1            ; Get the head
  1002.     and    al,1            ; Mask it out
  1003.     mov    dh,al            ; Into the right register
  1004.     mov    dl,$OUR_DRIVE        ; Drive to read from
  1005.     mov    al,10            ; Number to read
  1006.     push    bx            ; Save this ptr so we can set status
  1007.     push    es            ;
  1008.     les    bx,es:[bx].VC_buffer    ; Point to the buffer
  1009.     call    sect_read        ; Try to read 'em
  1010.     pop    es            ; Restore the pointer
  1011.     pop    bx            ;
  1012.     jnc    crdt_ok            ; If ok, return success
  1013.     push    bx
  1014.     mov    bx,offset CMD_sts_table    ; Point to the translation table
  1015.     call    xlat_status        ; Get the status code we want
  1016.     pop    bx            ; Get the pointer back
  1017.     mov    es:[bx].VC_status,al    ; Set the return status
  1018.     jmp    short crdt_exit        ; Exit now
  1019. crdt_ok:
  1020.     mov    es:[bx].VC_status,STS_OK; Return that it succeeded
  1021. crdt_exit:
  1022.     call    rest_disk_parms        ; Restore the diskette parameters
  1023.     ret                ; Return to caller
  1024.  
  1025. cmd_read_trk    endp
  1026.  
  1027.  
  1028. ;*****************************************************************************
  1029. ;
  1030. ; Called when VHARDTCL gets a "Write Track" command
  1031. ;
  1032. ;*****************************************************************************
  1033. cmd_write_trk    proc    near
  1034.  
  1035.     call    set_disk_parms        ; Set up the diskette parameters
  1036.     mov    al,es:[bx].VC_track    ; Get the track to write
  1037.     mov    ch,7fh            ; Get mask for the track
  1038.     and    ch,al            ; Get the track
  1039.     mov    cl,1            ; Start with sector 1
  1040.     rol    al,1            ; Get the head
  1041.     and    al,1            ; Mask it out
  1042.     mov    dh,al            ; Into the right register
  1043.     mov    dl,$OUR_DRIVE        ; Drive to write to
  1044.     mov    al,10            ; Number to write
  1045.     push    bx            ; Save this ptr so we can set status
  1046.     push    es            ;
  1047.     les    bx,es:[bx].VC_buffer    ; Point to the buffer
  1048.     call    sect_write        ; Try to write 'em
  1049.     pop    es            ; Restore the pointer
  1050.     pop    bx            ;
  1051.     jnc    cwrt_ok            ; If ok, return success
  1052.     push    bx
  1053.     mov    bx,offset CMD_sts_table    ; Point to the translation table
  1054.     call    xlat_status        ; Get the status code we want
  1055.     pop    bx            ; Get the pointer back
  1056.     mov    es:[bx].VC_status,al    ; Set the return status
  1057.     jmp    short cwrt_exit        ; Exit now
  1058. cwrt_ok:
  1059.     mov    es:[bx].VC_status,STS_OK; Return that it succeeded
  1060. cwrt_exit:
  1061.     call    rest_disk_parms        ; Restore the diskette parameters
  1062.     ret                ; Return to caller
  1063.  
  1064. cmd_write_trk    endp
  1065.  
  1066.  
  1067. ;*****************************************************************************
  1068. ;
  1069. ; Called when VHARDCTL gets a "Verify Track" command
  1070. ;
  1071. ;*****************************************************************************
  1072. cmd_verify_trk    proc    near
  1073.  
  1074.     call    set_disk_parms        ; Set up the diskette parameters
  1075.     mov    al,es:[bx].VC_track    ; Get the track to verify
  1076.     mov    ch,7fh            ; Get mask for the track
  1077.     and    ch,al            ; Get the track
  1078.     mov    cl,1            ; Start with sector 1
  1079.     rol    al,1            ; Get the head
  1080.     and    al,1            ; Mask it out
  1081.     mov    dh,al            ; Into the right register
  1082.     mov    dl,$OUR_DRIVE        ; Drive to read from
  1083.     mov    al,10            ; Number to verify
  1084.     call    sect_verify        ; Try to verify 'em
  1085.     jnc    crdt_ok            ; If ok, return success
  1086.     push    bx
  1087.     mov    bx,offset CMD_sts_table    ; Point to the translation table
  1088.     call    xlat_status        ; Get the status code we want
  1089.     pop    bx            ; Get the pointer back
  1090.     mov    es:[bx].VC_status,al    ; Set the return status
  1091.     jmp    short crdt_exit        ; Exit now
  1092. cvft_ok:
  1093.     mov    es:[bx].VC_status,STS_OK; Return that it succeeded
  1094. cvft_exit:
  1095.     call    rest_disk_parms        ; Restore the diskette parameters
  1096.     ret                ; Return to caller
  1097.  
  1098. cmd_verify_trk    endp
  1099.  
  1100.  
  1101. ;*****************************************************************************
  1102. ;
  1103. ; Called when VHARDCTL gets a "Set Cache" command
  1104. ;
  1105. ; There are 5 sub-commands supported. The subcommand is gotten from the byte
  1106. ; used by the other routines to specify the track.
  1107. ;
  1108. ; The 6 sub-commands are:
  1109. ;    0    disable the cache
  1110. ;        On return, the status byte will be set to either STS_OK,
  1111. ;        STS_NOT_ENAB (meaning that the cache is not installed), or
  1112. ;        STS_CACHE_DIRTY (meaning that the cache needs to be flushed).
  1113. ;        If the cache is successfully disabled, the VC_track byte will
  1114. ;        be set to the current flags for the cache and the VC_buffer
  1115. ;        will be set to the address of the cache.
  1116. ;    1    enable the cache
  1117. ;        On return, the status byte will be set to either STS_OK or
  1118. ;        STS_ALREADY (meaning that the cache is already installed).
  1119. ;        If the cache is already installed, the VC_track byte will be
  1120. ;        set to the current flags for the cache and the VC_buffer will
  1121. ;        be set to the current cache address.
  1122. ;    2    flush the cache
  1123. ;        On return, the status byte will be set to indicate the result.
  1124. ;        If the cache isn't dirty, this is a no-op.
  1125. ;
  1126. ;    3    disable cache autoflush
  1127. ;
  1128. ;    4    enable cache autoflush. VHCACHE will only make this call if
  1129. ;        the DOS version is 3.00 or higher AND SHARE.EXE is loaded.
  1130. ;
  1131. ;    5    get cache status. Returns the following at VC_buffer:
  1132. ;            byte    cache flags
  1133. ;            dword    address of cache
  1134. ;
  1135. ;*****************************************************************************
  1136. cmd_set_cache    proc    near
  1137.  
  1138.     mov    al,es:[bx].VC_track    ; Get the sub-command
  1139.     cmp    al,5            ; See if it's valid
  1140.     jbe    cmsc_l1            ; Yep - handle it
  1141.     mov    es:[bx].VC_status,STS_UNK_CMD    ; Return a bad command
  1142.     ret                ; Return to caller
  1143. cmsc_l1:
  1144.     cbw                ; Make it a word
  1145.     shl    ax,1            ; Make it a table offset
  1146.     mov    di,ax            ; Into a pointer reg
  1147.     jmp    cache_dispatch[di]    ; Switch to the appropriate routine
  1148.  
  1149. cmd_set_cache    endp
  1150.  
  1151.  
  1152. ;*****************************************************************************
  1153. ;
  1154. ; Handle a "Cache Disable" command
  1155. ;
  1156. ; If the cache isn't enabled, return STS_NOT_ENAB.
  1157. ; If the CACHE_DIRTY bit is set (meaning that the cache needs to be flushed),
  1158. ; set ES:[BX].VC_track to the current cache flags, set ES:[BX].VC_buffer to
  1159. ; point to the cache, and return STS_CACHE_DIRTY.
  1160. ;
  1161. ; Otherwise, disable it, set ES:[BX].VC_track to the current
  1162. ; cache flags, set ES:[BX].VC_buffer to point to the cache, and return STS_OK.
  1163. ;
  1164. ;*****************************************************************************
  1165. disable_cache    proc    near
  1166.  
  1167.     test    cache_flags,CACHE_INSTALLED    ; Is the cache installed?
  1168.     jnz    dsch_l1                ; Yep - see if it's dirty
  1169.     mov    es:[bx].VC_status,STS_NOT_ENAB    ; Return it wasn't installed
  1170.     jmp    short dsch_exit            ; Exit now
  1171. dsch_l1:
  1172.     test    cache_flags,CACHE_DIRTY        ; Is the cache dirty?
  1173.     jz    dsch_l2                ; Nope - disable it
  1174.     mov    al,cache_flags            ; Get the current flags
  1175.     mov    es:[bx].VC_track,al        ; Return 'em
  1176.     mov    al,STS_CACHE_DIRTY        ; Report it's dirty
  1177.     jmp    short dsch_l3            ; Continue
  1178. dsch_l2:
  1179.     mov    al,STS_OK            ; Return it's ok
  1180.     mov    cache_flags,0            ; Get rid of all cache flags
  1181. dsch_l3:
  1182.     mov    es:[bx].VC_status,al        ; Save return status
  1183.     mov    ax,cache_ptr[0]            ; Return address of cache
  1184.     mov    word ptr es:[bx].VC_buffer[0],ax;
  1185.     mov    ax,cache_ptr[2]            ;
  1186.     mov    word ptr es:[bx].VC_buffer[2],ax;
  1187. dsch_exit:
  1188.     ret                    ; Return to caller
  1189.  
  1190. disable_cache    endp
  1191.  
  1192.  
  1193. ;*****************************************************************************
  1194. ;
  1195. ; Handle a "Cache Enable" command.
  1196. ;
  1197. ; If the cache is enabled already, set ES:[BX].VC_track to the current cache
  1198. ; flags, set ES:[BX].VC_buffer to point to the cache, and return STS_ALREADY.
  1199. ;
  1200. ; Otherwise, set our internal cache pointer to ES:[BX].VC_buffer, set the
  1201. ; cache flags to CACHE_INSTALLED, and return STS_OK.
  1202. ;
  1203. ;*****************************************************************************
  1204. enable_cache    proc    near
  1205.  
  1206.     test    cache_flags,CACHE_INSTALLED    ; Is it installed?
  1207.     jz    ench_l1                ; No - install now
  1208.     mov    es:[bx].VC_status,STS_ALREADY    ; Say it's already installed
  1209.     mov    al,cache_flags            ; Get the current flags
  1210.     mov    es:[bx].VC_track,al        ; Return 'em
  1211.     mov    ax,cache_ptr[0]            ; Return cache address
  1212.     mov    word ptr es:[bx].VC_buffer[0],ax;
  1213.     mov    ax,cache_ptr[2]            ;
  1214.     mov    word ptr es:[bx].VC_buffer[2],ax;
  1215.     jmp    short ench_exit            ; Exit now
  1216. ench_l1:
  1217. ;
  1218. ; The cache isn't installed yet. Install it now.
  1219. ; Address is in ES:[BX].VC_buffer.
  1220. ;
  1221.     mov    ax,word ptr es:[bx].VC_buffer[0]; Point to new cache
  1222.     mov    cache_ptr[0],ax            ;
  1223.     mov    ax,word ptr es:[bx].VC_buffer[2];
  1224.     mov    cache_ptr[2],ax            ;
  1225.     mov    cache_flags,CACHE_INSTALLED    ;
  1226.     mov    es:[bx].VC_status,STS_OK    ; Return ok status
  1227. ench_exit:
  1228.     ret
  1229.  
  1230. enable_cache    endp
  1231.  
  1232.  
  1233. ;*****************************************************************************
  1234. ;
  1235. ; Handle a "Flush Cache" command.
  1236. ;
  1237. ; If the cache is not installed, return STS_NOT_ENAB.
  1238. ;
  1239. ; If the cache is not dirty, return STS_OK.
  1240. ;
  1241. ; Otherwise, try to flush the cache, returning the status from the disk I/O
  1242. ; operation.
  1243. ;
  1244. ;*****************************************************************************
  1245. flush_cache    proc    near
  1246.  
  1247.     test    cache_flags,CACHE_INSTALLED    ; Is the cache installed?
  1248.     jnz    flch_l1                ; Yep - see if it's dirty
  1249.     mov    al,STS_NOT_ENAB            ; Return that it ain't there
  1250.     jmp    short flch_done
  1251. flch_l1:
  1252.     test    cache_flags,CACHE_DIRTY        ; Is it dirty?
  1253.     jz    flch_ok                ; No - just return ok
  1254.     call    set_disk_parms            ; Set the diskette parameters
  1255.     call    do_flush_cache            ; Do the flush
  1256.     pushf                    ; Save result flag
  1257.     call    rest_disk_parms            ; Restore the parameters
  1258.     popf                    ; Get error flag back
  1259.     jnc    flch_ok                ; If it went, return success
  1260.     push    bx                ; Save command blk ptr
  1261.     mov    bx,offset CMD_sts_table        ; Point to translate table
  1262.     call    xlat_status            ; Translate the status
  1263.     pop    bx                ;
  1264.     jmp    short flch_done            ; Exit now
  1265. flch_ok:
  1266.     mov    al,STS_OK            ; Return success
  1267. flch_done:
  1268.     mov    es:[bx].VC_status,al        ; Save the status
  1269.     ret
  1270.  
  1271. flush_cache    endp
  1272.  
  1273.  
  1274. ;*****************************************************************************
  1275. ;
  1276. ; Handle the "Disable Auto-Flush" command.
  1277. ;
  1278. ; If the cache is not installed, return STS_NOT_ENAB.
  1279. ;
  1280. ;*****************************************************************************
  1281. disab_auto    proc    near
  1282.  
  1283.     test    cache_flags,CACHE_INSTALLED    ; Is that baby installed?
  1284.     jnz    dsau_l1                ; Yep - disable autoflush
  1285.     mov    es:[bx].VC_status,STS_NOT_ENAB    ; Return it ain't there
  1286.     jmp    short dsau_exit            ; Exit now
  1287. dsau_l1:
  1288.     mov    es:[bx].VC_status,STS_OK    ; Return success
  1289.     and    cache_flags,not CACHE_AUTO    ; Disable it
  1290. dsau_exit:
  1291.     ret                    ; Return to caller
  1292.  
  1293. disab_auto    endp
  1294.  
  1295.  
  1296. ;*****************************************************************************
  1297. ;
  1298. ; Handle the "Enable Autoflush" command
  1299. ;
  1300. ; If the cache is not installed, return STS_NOT_ENAB.
  1301. ;
  1302. ;*****************************************************************************
  1303. enab_auto    proc    near
  1304.  
  1305.     test    cache_flags,CACHE_INSTALLED    ; Is that baby installed?
  1306.     jnz    enau_l1                ; Yep - disable autoflush
  1307.     mov    es:[bx].VC_status,STS_NOT_ENAB    ; Return it ain't there
  1308.     jmp    short enau_exit            ; Exit now
  1309. enau_l1:
  1310.     mov    es:[bx].VC_status,STS_OK    ; Return success
  1311.     or    cache_flags,CACHE_AUTO        ; Enable it
  1312. enau_exit:
  1313.     ret                    ; Return to caller
  1314.  
  1315. enab_auto    endp
  1316.  
  1317.  
  1318. ;*****************************************************************************
  1319. ;
  1320. ; Handle a "Get Cache Information" command.
  1321. ;
  1322. ; Returns the cache flags and the address of the cache.
  1323. ;
  1324. ;*****************************************************************************
  1325. get_cache_info    proc    near
  1326.  
  1327.     mov    es:[bx].VC_status,STS_OK; Always returns success
  1328.     les    di,es:[bx].VC_buffer    ; Point to the buffer
  1329.     mov    al,cache_flags        ; Get the current cache flags
  1330.     stosb                ; Return 'em
  1331.     mov    ax,cache_ptr[0]        ; Get the cache address offset
  1332.     stosw                ; Stuff it
  1333.     mov    ax,cache_ptr[2]        ; Same with the cache address segment
  1334.     stosw                ;
  1335.     ret                ; Return to caller
  1336.  
  1337. get_cache_info    endp
  1338.  
  1339.  
  1340. ;*****************************************************************************
  1341. ;
  1342. ; Called when VHARDCTL gets a "Get Driver Data" command. This copies data from
  1343. ; our data area into the buffer.
  1344. ; Data returned in the buffer:
  1345. ;    byte    drive number assigned by DOS
  1346. ;    word    VHARD version
  1347. ;    struc    BPB used
  1348. ;
  1349. ;*****************************************************************************
  1350. cmd_get_vhdata    proc    near
  1351.  
  1352.     les    di,es:[bx].VC_buffer    ; Point to their buffer
  1353.     mov    al,our_drive        ; Get the drive number
  1354.     stosb                ; Store it
  1355.     mov    ax,VHARD_version    ; Store the driver version
  1356.     stosw                ;
  1357.     mov    si,offset our_bpb    ; Copy our BPB
  1358.     mov    cx,size DOS_BPB        ;
  1359.     rep    movsb            ;
  1360.     ret                ; Return to caller
  1361.  
  1362. cmd_get_vhdata    endp
  1363.  
  1364.  
  1365. ;*****************************************************************************
  1366. ;
  1367. ; This dispatch table is used to handle the special commands called via
  1368. ; VHARDCTL.
  1369. ;
  1370. ;*****************************************************************************
  1371. cmd_dispatch    dw    cmd_format_trk    ; Format a track
  1372.         dw    cmd_read_trk    ; Read a track
  1373.         dw    cmd_write_trk    ; Write a track
  1374.         dw    cmd_verify_trk    ; Verify a track
  1375.         dw    cmd_set_cache    ; Deal with the cache
  1376.         dw    cmd_get_vhdata    ; Get data
  1377.  
  1378. ;*****************************************************************************
  1379. ;
  1380. ; Yet another dispatch table (this one's for the cache commands)
  1381. ;
  1382. ;*****************************************************************************
  1383. cache_dispatch    dw    disable_cache    ; Disable the cache
  1384.         dw    enable_cache    ; Enable the cache
  1385.         dw    flush_cache    ; Flush the cache
  1386.         dw    disab_auto    ; Disable auto-flush
  1387.         dw    enab_auto    ; Enable auto-flush
  1388.         dw    get_cache_info    ; Get cache information
  1389.  
  1390.  
  1391.     subttl    Utility routines
  1392.     page
  1393.  
  1394. ;*****************************************************************************
  1395. ;
  1396. ; U T I L I T Y   R O U T I N E S
  1397. ;
  1398. ;*****************************************************************************
  1399.  
  1400.  
  1401. ;*****************************************************************************
  1402. ;
  1403. ; Save all registers
  1404. ;
  1405. ;*****************************************************************************
  1406. save_regs    proc    near
  1407.  
  1408. assume    ss:nothing
  1409.  
  1410.     save    ax, bx, cx, dx, si, di, ds, es
  1411.     mov    ax,bp            ; Get current BP value
  1412.     mov    bp,sp            ; Point into the stack
  1413.     xchg    ax,[bp + 16]        ; Swap it with the return address
  1414.     push    ax            ; Put that on the stack
  1415.     mov    ax,[bp + 14]        ; Set AX back to original value
  1416.     mov    bp,[bp + 16]        ; Same with BP
  1417.     ret                ; Return to caller
  1418.  
  1419. save_regs    endp
  1420.  
  1421.  
  1422. ;*****************************************************************************
  1423. ;
  1424. ; Restore all registers
  1425. ;
  1426. ;*****************************************************************************
  1427. rest_regs    proc    near
  1428.  
  1429.     pop    ax            ; Get return address
  1430.     mov    bp,sp            ; Get a pointer into the stack
  1431.     xchg    ax,[bp + 16]        ; Swap retaddr with the saved BP value
  1432.     mov    bp,ax            ; This restores BP
  1433.     restore
  1434.     ret                ; Return to caller
  1435.  
  1436. rest_regs    endp
  1437.  
  1438.  
  1439. ;*****************************************************************************
  1440. ;
  1441. ; Read sectors from disk.
  1442. ;
  1443. ; Input:
  1444. ;    DH    Head number
  1445. ;    CH    Track number
  1446. ;    CL    Starting sector number
  1447. ;    AL    Number of sectors to read
  1448. ;    ES:BX    Address of buffer
  1449. ;
  1450. ; Output:
  1451. ;    AH    Status returned by BIOS
  1452. ;    CF    Error indicator
  1453. ;
  1454. ; Registers:
  1455. ;    BP preserved, all others trashed.
  1456. ;
  1457. ;*****************************************************************************
  1458. sect_read    proc    near
  1459.  
  1460.     save    si, bp
  1461.     mov    bp,3            ; Retry 3 times
  1462.     mov    si,ax            ; Save # of sectors
  1463. sctr_l1:
  1464.     mov    ah,2            ; BIOS function to read
  1465.     mov    dl,$OUR_DRIVE        ; Drive to read from
  1466.     int    13h            ; Call the BIOS to do the work
  1467.     jnc    sctr_exit        ; If it went, exit now
  1468.     dec    bp            ; That was 1 try
  1469.     jz    sctr_exit        ; If that was the last, exit
  1470.     mov    ah,0            ; Do a reset
  1471.     int    13h            ;
  1472.     jc    sctr_exit        ; If that fails, we got problems...
  1473.     mov    ax,si            ; Get # to read again
  1474.     jmp    short sctr_l1        ; Try again
  1475. sctr_exit:
  1476.     restore
  1477.     ret                ; Return to caller
  1478.  
  1479. sect_read    endp
  1480.  
  1481.  
  1482. ;*****************************************************************************
  1483. ;
  1484. ; Verify sectors.
  1485. ;
  1486. ; Input:
  1487. ;    DH    Head number
  1488. ;    CH    Track number
  1489. ;    CL    Starting sector number
  1490. ;    AL    Number of sectors to verify
  1491. ;
  1492. ; Output:
  1493. ;    AH    Status returned by BIOS
  1494. ;    CF    Error indicator
  1495. ;
  1496. ; Registers:
  1497. ;    BP preserved, all others trashed.
  1498. ;
  1499. ;*****************************************************************************
  1500. sect_verify    proc    near
  1501.  
  1502.     save    si, bp
  1503.     mov    bp,3            ; Retry 3 times
  1504.     mov    si,ax            ; Save # of sectors
  1505. sctv_l1:
  1506.     mov    ah,4            ; BIOS function to verify
  1507.     mov    dl,$OUR_DRIVE        ; Drive to read from
  1508.     int    13h            ; Call the BIOS to do the work
  1509.     jnc    sctv_exit        ; If it went, exit now
  1510.     dec    bp            ; That was 1 try
  1511.     jz    sctv_exit        ; If that was the last, exit
  1512.     mov    ah,0            ; Do a reset
  1513.     int    13h            ;
  1514.     jc    sctv_exit        ; If that fails, we got problems...
  1515.     mov    ax,si            ; Get # to read again
  1516.     jmp    short sctv_l1        ; Try again
  1517. sctv_exit:
  1518.     restore
  1519.     ret                ; Return to caller
  1520.  
  1521. sect_verify    endp
  1522.  
  1523.  
  1524. ;*****************************************************************************
  1525. ;
  1526. ; Write sectors to disk.
  1527. ;
  1528. ; Input:
  1529. ;    DH    Head number
  1530. ;    CH    Track number
  1531. ;    CL    Starting sector number
  1532. ;    AL    Number of sectors to write
  1533. ;    ES:BX    Address of buffer
  1534. ;
  1535. ; Output:
  1536. ;    AH    Status returned by BIOS
  1537. ;    CF    Error indicator
  1538. ;
  1539. ; Registers:
  1540. ;    BP preserved, all others trashed.
  1541. ;
  1542. ;*****************************************************************************
  1543. sect_write    proc    near
  1544.  
  1545.     save    si,bp
  1546.     mov    bp,3            ; Retry 3 times
  1547.     mov    si,ax            ; Save # to write
  1548. sctw_l1:
  1549.     mov    ah,3            ; BIOS function to write
  1550.     mov    dl,$OUR_DRIVE        ; Drive to write to
  1551.     int    13h            ; Call the BIOS to do the work
  1552.     jnc    sctw_exit        ; If it went, exit now
  1553.     dec    bp            ; That was 1 try
  1554.     jz    sctw_exit        ; If that was the last, exit
  1555.     mov    ah,0            ; Do a reset
  1556.     int    13h            ;
  1557.     jc    sctw_exit        ; If that fails, we got problems...
  1558.     mov    ax,si            ; Get # to write
  1559.     jmp    short sctw_l1        ; Try again
  1560. sctw_exit:
  1561.     restore
  1562.     ret                ; Return to caller
  1563.  
  1564. sect_write    endp
  1565.  
  1566.  
  1567. ;*****************************************************************************
  1568. ;
  1569. ; Set the diskette parameters for 10 sectors per track
  1570. ;
  1571. ; Input:
  1572. ;    None.
  1573. ;
  1574. ; Output:
  1575. ;    An internal copy of the parameters is made, the address at the vector
  1576. ;    for int 1eh is saved, and the vector is set to point to our copy.
  1577. ;
  1578. ; Registers:
  1579. ;    All preserved.
  1580. ;
  1581. ;*****************************************************************************
  1582. set_disk_parms    proc    near
  1583.  
  1584. assume    ds:vhard_seg, es:nothing
  1585.  
  1586.     save    cx, si, di, es
  1587.  
  1588.     push    ds            ; Save our segment, too
  1589.     sub    si,si            ; Set it to segment for int vectors
  1590.     mov    ds,si            ;
  1591.     lds    si,ds:[1eh * 4]        ; Point to the current disk parms
  1592.     mov    cs:old_pptr[0],si    ; Save it for restore later
  1593.     mov    cs:old_pptr[2],ds    ;
  1594.     push    cs            ; Set ES to our segment
  1595.     pop    es
  1596.  
  1597. assume    ds:nothing, es:vhard_seg
  1598.  
  1599.     mov    di,offset diskparms    ; Point to our copy of the parameters
  1600.     mov    cx,11            ; Only need 11 bytes
  1601.     cld                ; Make sure of direction
  1602.     rep    movsb            ; Copy them
  1603.     pop    ds            ; Get our segment back
  1604.  
  1605. assume    ds:vhard_seg
  1606.  
  1607.     mov    sect_per_track,10    ; Set that aright
  1608.     mov    gap_len,14h
  1609.     mov    fmt_gap_len,20h
  1610.     sub    cx,cx            ; Point back to the vectors
  1611.     mov    es,cx            ;
  1612.     mov    word ptr es:[1eh * 4],offset diskparms
  1613.     mov    es:[(1eh * 4) + 2],cs    ; Set pointer to new parms
  1614.  
  1615.     restore
  1616.     ret                ; Return to caller
  1617.  
  1618. set_disk_parms    endp
  1619.  
  1620.  
  1621. ;*****************************************************************************
  1622. ;
  1623. ; Restore the diskette parameters
  1624. ;
  1625. ; Input:
  1626. ;    None.
  1627. ;
  1628. ; Output:
  1629. ;    The vector for int 1eh is restored.
  1630. ;
  1631. ; Registers:
  1632. ;    All preserved.
  1633. ;
  1634. ;*****************************************************************************
  1635. rest_disk_parms    proc    near
  1636.  
  1637. assume    ds:vhard_seg, es:nothing
  1638.  
  1639.     save    ax, es
  1640.     sub    ax,ax            ; Get segment for int vectors
  1641.     mov    es,ax            ;
  1642.     mov    ax,old_pptr[0]        ; Restore the old parm pointer
  1643.     mov    es:[1eh * 4],ax        ;
  1644.     mov    ax,old_pptr[2]        ;
  1645.     mov    es:[(1eh * 4) + 2],ax    ;
  1646.     restore
  1647.     ret                ; Return to caller
  1648.  
  1649. rest_disk_parms    endp
  1650.  
  1651.  
  1652. ;*****************************************************************************
  1653. ;
  1654. ; Translate a BIOS error code to a status code according to the table at BX.
  1655. ;
  1656. ; Input:
  1657. ;    AH    BIOS return code
  1658. ;    DS:BX    Pointer to translation table
  1659. ;
  1660. ; Output:
  1661. ;    AL    Corresponding status code
  1662. ;
  1663. ; Registers:
  1664. ;    AX trashed; all others preserved
  1665. ;
  1666. ;*****************************************************************************
  1667. xlat_status    proc    near
  1668.  
  1669. assume    ds:vhard_seg
  1670.  
  1671.     save    bx, cx
  1672.     mov    cx,10            ; Check for 10 BIOS codes
  1673. xlts_l1:
  1674.     cmp    ah,[bx]            ; Is it this one?
  1675.     je    xlts_l2            ; Yep - return the status code
  1676.     inc    bx            ; Point to next one
  1677.     inc    bx
  1678.     loop    xlts_l1            ;
  1679.     mov    al,STS_BAD_ERROR    ; Unknown error code
  1680.     jmp    short xlts_exit
  1681. xlts_l2:
  1682.     mov    al,1[bx]        ; Get the corresponding status code
  1683. xlts_exit:
  1684.     restore
  1685.     ret                ; Return to caller
  1686.  
  1687. xlat_status    endp
  1688.  
  1689.  
  1690. ;*****************************************************************************
  1691. ;
  1692. ; Calculate the disk, track, head, and sector for a given logical sector.
  1693. ;
  1694. ; Input:
  1695. ;    AX    DOS' logical sector
  1696. ;
  1697. ; Output:
  1698. ;    AX    Disk number
  1699. ;    DH    Head number
  1700. ;    CH    Track number
  1701. ;    CL    Sector number
  1702. ;
  1703. ; Registers:
  1704. ;    All register not used for parameters are preserved.
  1705. ;
  1706. ;*****************************************************************************
  1707. calc_disk    proc    near
  1708.  
  1709.     save    bx, si
  1710.     mov    bx,799            ; Divide by # of sectors/disk
  1711.     sub    dx,dx            ;  to get AX = disk #,
  1712.     div    bx            ;  DX = logical sector on that disk
  1713.     mov    si,ax            ; Save the disk #
  1714.     mov    ax,dx            ; Get the logical sector on the disk
  1715.     inc    ax            ; Allow for boot sector
  1716.     mov    bx,10            ; Divide by # of sectors/track
  1717.     sub    dx,dx            ;  to get AX = track,
  1718.     div    bx            ;  DX = logical sector on the track
  1719.     mov    cl,dl            ; Get the sector
  1720.     inc    cl            ; Since sectors start at 1...
  1721.     shr    ax,1            ; Figure out the cylinder
  1722.     rcl    dh,1            ; Set DH to the head
  1723.     mov    ch,al            ; Return the track
  1724.     mov    ax,si            ; Get the disk # back
  1725.     restore
  1726.     ret                ; Return to caller
  1727.  
  1728. calc_disk    endp
  1729.  
  1730.  
  1731. ;*****************************************************************************
  1732. ;
  1733. ; Read in the boot sector, checking to see if the ID matches our current ID.
  1734. ;
  1735. ; Input:
  1736. ;    curr_disk_id        the ID we're expecting.
  1737. ;
  1738. ; Output:
  1739. ;    CF = 1            if the diskette can't be read
  1740. ;    ZF = 1            if the disk IDs match
  1741. ;
  1742. ; Registers:
  1743. ;    All preserved.
  1744. ;
  1745. ;*****************************************************************************
  1746. check_boot_sect    proc    near
  1747.  
  1748. assume    ds:vhard_seg, es:vhard_seg
  1749.  
  1750.     save    ax, bx, cx, dx, si, di, es
  1751.  
  1752.     push    cs            ; Make sure ES is right
  1753.     pop    es
  1754.  
  1755. assume    es:vhard_seg
  1756.  
  1757.     call    read_boot_sect        ; Try to read the boot sector
  1758.     jc    ckbs_exit        ; Return w/error flag set if problem
  1759.     mov    si,offset boot_sect_buf.BS_disk_id
  1760.     mov    di,offset curr_disk_id
  1761.     mov    cx,8            ; Check 8 bytes
  1762.     rep    cmpsb            ;
  1763.     clc                ; Clear error flag
  1764. ckbs_exit:
  1765.     restore
  1766.     ret                ; Return to caller
  1767.  
  1768. check_boot_sect    endp
  1769.  
  1770.  
  1771. ;*****************************************************************************
  1772. ;
  1773. ; Read the boot sector of the diskette in drive A: into the buffer at
  1774. ; boot_sect_buf.
  1775. ;
  1776. ; Input:
  1777. ;    None.
  1778. ;
  1779. ; Output:
  1780. ;    CF = 0        Success.
  1781. ;       = 1        Unable to read the diskette.
  1782. ;
  1783. ; Registers:
  1784. ;    All preserved.
  1785. ;
  1786. ;*****************************************************************************
  1787. read_boot_sect    proc    near
  1788.  
  1789. assume    ds:vhard_seg, es:nothing
  1790.  
  1791.     save    ax, bx, cx, dx, es
  1792.     mov    dh,0            ; Need to read Track 0, Head 0, Sect 1
  1793.     mov    cx,1            ;
  1794.     mov    al,1            ; Just 1 sector
  1795.     push    cs            ; Into our buffer
  1796.     pop    es            ;
  1797.  
  1798. assume    es:vhard_seg
  1799.  
  1800.     mov    bx,offset boot_sect_buf    ;
  1801.     call    sect_read        ; Read it
  1802.     restore                ;
  1803.  
  1804. assume    es:nothing
  1805.  
  1806.     ret                ; Return to caller
  1807.  
  1808. read_boot_sect    endp
  1809.  
  1810.  
  1811. ;*****************************************************************************
  1812. ;
  1813. ; Save the last line of the screen, display a prompt there, and wait for a
  1814. ; keystroke.
  1815. ;
  1816. ; Input:
  1817. ;    DS:SI            pointer to prompt to display
  1818. ;
  1819. ; Output:
  1820. ;    CF = 1            Esc was pressed
  1821. ;
  1822. ; Registers:
  1823. ;    All preserved.
  1824. ;
  1825. ;*****************************************************************************
  1826. prompt_user    proc    near
  1827.  
  1828. assume    ds:vhard_seg, es:nothing
  1829.  
  1830.     save    ax, bx, cx, dx, si, es
  1831.     mov    dh,24            ; Assume no EGA/VGA
  1832.     mov    ax,40h            ; Get at the number of lines on screen
  1833.     mov    es,ax            ;
  1834.  
  1835. assume    es:BIOS_seg
  1836.  
  1837.     cmp    have_evga,0        ; Do we have an EGA/VGA on board?
  1838.     je    prpu_l1            ; No - use line 24
  1839.     mov    dh,es:[crt_lines]    ; Get last line on screen
  1840. prpu_l1:
  1841.     mov    bh,es:[active_page]    ; Get the current video page
  1842.     push    cs            ; Set ES to our segment
  1843.     pop    es            ;
  1844.  
  1845. assume    es:vhard_seg
  1846.  
  1847.     push    dx            ; Save line to use
  1848.     mov    ah,3            ; Get the current cursor position
  1849.     int    10h            ;
  1850.     mov    last_curs,dx        ; Save it
  1851.     pop    dx            ;
  1852.     mov    dl,0            ; Start at the beginning of the line
  1853.     mov    bl,4fh            ; Use hi-white on red
  1854.     mov    cx,1            ; Only doing 1 char at a time
  1855.     mov    di,offset screen_save    ; Point to save area
  1856. prpu_l2:
  1857.     lodsb                ; Get a character to display
  1858.     or    al,al            ; Done yet?
  1859.     jz    prpu_l3            ; Yep - exit
  1860.     push    ax            ; Save it for a sec
  1861.     mov    ah,2            ; Set the cursor position
  1862.     int    10h            ;
  1863.     mov    ah,8            ; Read the char/attr there
  1864.     int    10h            ;
  1865.     stosw                ; Save it
  1866.     pop    ax            ; Get the new one back
  1867.     mov    ah,9            ; Write that character
  1868.     int    10h            ;
  1869.     inc    dl            ; Next column
  1870.     jmp    short prpu_l2        ; Loop back for more
  1871. prpu_l3:
  1872.     mov    ah,1            ; See if there are any keys
  1873.     int    16h            ;
  1874.     jz    prpu_l31        ; No - wait for one
  1875.     mov    ah,0            ; Read the key
  1876.     int    16h            ;
  1877.     jmp    short prpu_l3        ; Loop until buffer is empty
  1878. prpu_l31:
  1879.     sub    ah,ah            ; Read a key
  1880.     int    16h            ;
  1881.     push    ax            ; Save the key read
  1882.     mov    si,offset screen_save    ; Point back to the save area
  1883.     mov    dl,0            ; Start back at the beginning of line
  1884. prpu_l4:
  1885.     cmp    si,di            ; Reached the end yet?
  1886.     je    prpu_l5            ; Nope - keep restoring
  1887.     lodsw                ; Get char/attr
  1888.     mov    bl,ah            ; Get the attribute for the write
  1889.     push    ax            ; Save the character to write
  1890.     mov    ah,2            ; Set the cursor
  1891.     int    10h            ;
  1892.     pop    ax            ; Get the character back
  1893.     mov    ah,9            ; Write it out now
  1894.     int    10h
  1895.     inc    dl            ; Next column
  1896.     jmp    short prpu_l4
  1897. prpu_l5:
  1898.     mov    ah,2            ; Set cursor back to last position
  1899.     mov    dx,last_curs        ;
  1900.     int    10h            ;
  1901.     pop    ax            ; Get keystroke back
  1902.     cmp    ax,11bh            ; Escape key?
  1903.     je    prpu_exit        ; Yep - return w/CF=1
  1904.     stc                ; Return CF=0
  1905. prpu_exit:
  1906.     cmc                ; Make CF the way we want
  1907.     restore
  1908.     ret                ; Return to caller
  1909.  
  1910. prompt_user    endp
  1911.  
  1912.  
  1913. ;*****************************************************************************
  1914. ;
  1915. ; Prompt for a specific disk, waiting for a key.
  1916. ;
  1917. ; Input:
  1918. ;    curr_disk_id            ID of the disk set
  1919. ;    curr_disk_num            disk number to prompt for
  1920. ;
  1921. ; Output:
  1922. ;    CF = 1            The user pressed Esc. This indicates that they
  1923. ;                want an error returned to DOS.
  1924. ;
  1925. ; Registers:
  1926. ;    All preserved.
  1927. ;
  1928. ;*****************************************************************************
  1929. prompt_for_disk    proc    near
  1930.  
  1931. assume    ds:vhard_seg, es:nothing
  1932.  
  1933.     save    ax, cx, si, di, es
  1934.     push    cs            ; Make sure ES is right
  1935.     pop    es            ;
  1936.  
  1937. assume    es:vhard_seg
  1938.  
  1939.     mov    si,offset curr_disk_id    ; Point to the ID to ask for
  1940.     mov    di,offset pr_disk_id    ; Point to where to put it
  1941.     mov    cx,4            ; Copy 8 bytes (4 words)
  1942.     rep    movsw            ;
  1943.     mov    al,curr_disk_num    ; Get the number to ask for
  1944.     aam                ; Split it up
  1945.     or    ax,3030h        ; Make it 2 ASCII digits
  1946.     xchg    al,ah            ; Put 'em in the right order
  1947.     mov    word ptr pr_disk_num,ax    ; Store 'em in the string
  1948.     mov    si,offset need_disk    ; Point to the prompt string
  1949.     call    prompt_user        ; Prompt them for it
  1950.     restore
  1951.     ret                ; Return to caller
  1952.  
  1953. prompt_for_disk    endp
  1954.  
  1955.  
  1956. ;*****************************************************************************
  1957. ;
  1958. ; Prompt for the first disk of a set, and make sure it's loaded.
  1959. ;
  1960. ; Input:
  1961. ;    None.
  1962. ;
  1963. ; Output:
  1964. ;    CF = 0    Disk is successfully loaded
  1965. ;
  1966. ; Reserved:
  1967. ;    All preserved.
  1968. ;
  1969. ;*****************************************************************************
  1970. get_disk_0    proc    near
  1971.  
  1972.     save    ax, bx, cx, si, di, bp, es
  1973.     call    read_boot_sect        ; Load the boot sector
  1974.     jnc    gtd0_l2            ; If we got it, continue
  1975. gtd0_l1:
  1976.     mov    si,offset need_first_disk; Prompt for a disk
  1977.     call    prompt_user        ;
  1978.     jc    gtd0_exit        ; Exit if Esc pressed
  1979.     call    read_boot_sect        ; Read the boot sector
  1980.     jc    gtd0_l1            ; Loop back if unable
  1981. gtd0_l2:
  1982.     cmp    boot_sect_buf.BS_disk_num,0    ; Is it disk 0?
  1983.     jne    gtd0_l1                ; No - try again
  1984.     mov    si,offset boot_sect_buf.BS_disk_id    ; Copy disk ID
  1985.     mov    di,offset curr_disk_id            ;
  1986.     mov    cx,4                    ;
  1987.     push    cs                    ;
  1988.     pop    es                    ;
  1989.     rep    movsw                    ;
  1990.     mov    curr_disk_num,0        ; Set the disk to 0
  1991.     test    cache_flags,CACHE_INSTALLED    ; Is the cache there?
  1992.     jz    gtd0_l3                ; No - just get the ID
  1993.     test    cache_flags,CACHE_VALID        ; Is it already valid?
  1994.     jnz    gtd0_l3                ; Yep - just get the ID
  1995.     mov    bp,offset sect_read    ; Routine to use for the I/O
  1996.     mov    ax,1            ; Start at sector 1
  1997.     mov    cx,24            ; Read 24 sectors
  1998.     les    bx,cache_ptr        ; Point to the cache
  1999.     call    io_operation        ; Read 'em
  2000.     jc    gtd0_exit        ; If error, return it
  2001.     or    cache_flags,CACHE_VALID    ; It's got valid data now
  2002. gtd0_l3:
  2003.     clc                ; Indicate success
  2004. gtd0_exit:
  2005.     restore
  2006.     ret                ; Return to caller
  2007.  
  2008. get_disk_0    endp
  2009.  
  2010.  
  2011.  
  2012. ;*****************************************************************************
  2013. ;
  2014. ; Prompt for a disk, and make sure it's loaded
  2015. ;
  2016. ; Input:
  2017. ;    AL    Number of disk to prompt for
  2018. ;
  2019. ; Output:
  2020. ;    CF = 0    Disk is successfully loaded
  2021. ;
  2022. ; Registers:
  2023. ;    All preserved.
  2024. ;
  2025. ;*****************************************************************************
  2026. get_num_disk    proc    near
  2027.  
  2028.     cmp    curr_disk_num,0ffh    ; Do we have one yet?
  2029.     mov    curr_disk_num,al    ;
  2030.     jne    gtnd_l1            ; Yes - never mind
  2031.     call    get_disk_0        ; Get disk 0 to get a disk ID
  2032.     jc    gtnd_exit        ; Exit if unable
  2033.     or    al,al            ; Do we want disk 0?
  2034.     jz    gtnd_l2            ; Yep - don't bother reading again
  2035. gtnd_l1:
  2036.     call    check_boot_sect        ; Check the boot sector
  2037.     jc    gtnd_exit        ; Exit if error
  2038.     jnz    gtnd_l11        ; If IDs don't match, prompt for it
  2039.     cmp    al,boot_sect_buf.BS_disk_num    ; Right number?
  2040.     je    gtnd_l2            ; Yep - exit w/CF clear
  2041. gtnd_l11:
  2042.     call    prompt_for_disk        ; Prompt for the disk
  2043.     jc    gtnd_exit        ; Exit if Esc was pressed
  2044.     jmp    short gtnd_l1        ; Loop back now
  2045. gtnd_l2:
  2046.     clc                ; Clear error flag
  2047. gtnd_exit:
  2048.     ret                ; Return to caller
  2049.  
  2050. get_num_disk    endp
  2051.  
  2052.  
  2053. $drv_end    label    byte
  2054.  
  2055.  
  2056.     subttl    INIT routines for both drivers
  2057.     page
  2058.  
  2059. ;*****************************************************************************
  2060. ;
  2061. ; INIT routine for VHARD itself
  2062. ;
  2063. ;*****************************************************************************
  2064. drv_init        proc    near
  2065.  
  2066. assume    ds:vhard_seg, es:nothing
  2067.  
  2068.     mov    es:[si].DDir_media_id,1    ; Just 1 unit
  2069.     mov    word ptr es:[si].DDir_buffer,offset $drv_end
  2070.     mov    word ptr es:[si].DDir_buffer[2],cs
  2071.     mov    word ptr es:[si].DDir_count,offset our_bpb_array
  2072.     mov    word ptr es:[si].DDir_start,cs
  2073.     mov    ah,30h            ; Get the DOS version
  2074.     int    21h            ;
  2075.     xchg    al,ah            ;
  2076.     mov    dos_ver,ax        ; Save it
  2077.     cmp    ax,30ah            ; At least 3.10?
  2078.     jb    drvi_l0            ; Nope - can't get the drive
  2079.     mov    al,byte ptr es:22[si]    ; Get the drive assigned
  2080.     mov    our_drive,al        ; Save it
  2081. drvi_l0:
  2082.     mov    ax,1a00h        ; See if there's a VGA on board
  2083.     mov    bx,0a5a5h        ;
  2084.     int    10h            ;
  2085.     cmp    bx,0a5a5h        ; Did it change?
  2086.     jne    drvi_l1            ; Yep - say that we have EGA/VGA
  2087.     mov    ah,12h            ; See if we have an EGA
  2088.     mov    bx,0ff10h        ;
  2089.     int    10h            ;
  2090.     cmp    bh,0ffh            ; Did it change?
  2091.     je    drvi_exit        ; No - no EGA or VGA
  2092. drvi_l1:
  2093.     or    have_evga,1        ; Set the flag
  2094. drvi_exit:
  2095.     mov    dx,offset banner    ; Display the program banner
  2096.     mov    ah,9            ;
  2097.     int    21h            ;
  2098.     ret                ; Return to dispatcher
  2099.  
  2100. drv_init        endp
  2101.  
  2102.  
  2103. banner    db    13, 10, 'VHARD virtual disk driver v'
  2104. %    db    VHARD_text_ver
  2105.     db    ' - Public Domain Software', 13, 10, '$'
  2106.  
  2107.  
  2108. du_init        proc    near
  2109.  
  2110.     mov    word ptr es:[si][0].DDir_buffer,offset $drv_end
  2111.     mov    word ptr es:[si][2].DDir_buffer,cs
  2112.     ret
  2113.  
  2114. du_init        endp
  2115.  
  2116.  
  2117. vhard_seg    ends
  2118.  
  2119.     end
  2120.