home *** CD-ROM | disk | FTP | other *** search
-
- title Fake hard disk driver
- subttl Prologue
- page 60,132
-
- comment {
-
- ******************************************************************************
-
- File VHARD.ASM
-
- Author:
- Aaron L. Brenner
-
- BIX mail address albrenner
- GEnie address A.BRENNER
-
- This program is hereby released to the public domain.
-
- Purpose:
- A DOS device driver that will make a floppy drive double as a small
- hard disk.
-
- This is accomplished by spreading the fake HD over a series of
- diskettes. The disks must be prepared (with VHPREP) by formatting to
- 10 sectors/track for a capacity of 400KB per diskette. Also, VHPREP
- will stamp each diskette with a unique ID built from the date and time
- the first diskette was prepared and the sequence number of it. VHPREP
- is also responsible for performing the low and high level format on
- all disks. Formatting is performed by the driver, but under VHPREP
- control.
-
- VHPREP communicates with the driver via a dummy character device
- driver named VHARDCTL, which will provide access to driver functions.
- The only driver command supported is IOCTL Write (all others are
- NOPs), and only 7 bytes are actually used. These 7 bytes are treated
- as a command block, structured as follows:
-
- Format command
- byte 0
- byte track to format (head in bit 7, cylinder in low 7 bits)
- byte return status
- dword pointer to format parameter buffer (see details in int 13h)
-
- Read Track command
- byte 1
- byte track to read (head in bit 7, cylinder in low 7 bits)
- byte return status
- dword pointer to buffer to read into
-
- Write Track command
- byte 2
- byte track to write (head in bit 7, cylinder in low 7 bits)
- byte return status
- dword pointer to buffer to write from
-
- Verify Track command
- byte 3
- byte track to verify (head in bit 7, cylinder in low 7 bits)
- byte return status
-
- Set FAT cache buffer
- byte 4
- byte == 0, disable cache
- == 1, enable cache
- == 2, flush cache
- == 3, disable cache autoflush
- == 4, enable cache autoflush (DOS 3+, SHARE installed)
- == 5, return information about the cache
- byte return status
- dword pointer to cache buffer
- This command is used to enable or disable the FAT/directory cache.
- Once the cache is enabled, logical sectors 1 through 24 will be cached
- for both read and write. They will only be written when driver
- function 14 (Device close) is called AND the reference count is 0.
- To accomodate older versions of DOS, subcommand 2 will manually flush
- the cache to disk. Subcommands 3 & 4 will disable or enable the
- auto-flush.
-
- Retrieve Driver Data
- byte 5
- byte unused
- byte set to 0 by VHARD
- dword pointer to buffer to receive the following data:
- byte drive number assigned by DOS
- word VHARD version
- 19 bytes BPB for VHARD
-
- Revision history:
- 0.90 06/27/90 ALB Created. This version uses 13 360KB diskettes
- for appx. 5MB of faked hard disk space. Note:
- cache handling is NOT yet implemented.
-
- ******************************************************************************
-
- endcomment {
-
- subttl Included files
- page
-
- include dd_defs.inc
- include vhard.inc ; Some structures we use
-
- DEBUG equ 0
-
-
- subttl Macros and templates
- page
-
- BIOS_seg segment at 40h
-
- org 62h
- active_page db ?
-
- org 84h
- crt_lines db ?
-
- BIOS_seg ends
-
-
- ;*****************************************************************************
- ;
- ; Version of VHARD
- ;
- ;*****************************************************************************
- VHARD_version equ 100h
- VHARD_text_ver equ <'1.00'>
-
-
- subttl Device driver headers
- page
-
- vhard_seg segment
-
- org 0
-
- assume cs:vhard_seg, ds:nothing, es:nothing, ss:nothing
-
- ;
- ; Declare the header for the dummy char device used to communicate with
- ; the block driver
- ;
- dw offset next_hdr
- nextdvrseg dw -1
- dw DA_IS_CHAR or DA_IOCTL
- dw strategy
- dw du_int
- db 'VHARDCTL'
-
- ;
- ; Declare the header for the device driver
- ; Attributes:
- ; block device
- ; removable media
- ;
- next_hdr dw -1, -1
- dw DA_IS_BLK or DA_IS_REM
- dw strategy
- dw vh_int
- db 1, 'VHARD '
-
- subttl Device driver data
- page
-
- ;
- ; Pointer passed by DOS to the request packet
- ;
- req_ptr dw 0, 0
-
- ;
- ; Version of DOS
- ;
- dos_ver dw 0
-
- ;
- ; Local copy of the diskette parameters
- ;
- diskparms db 4 dup(0) ; Gets filled in at init time
- sect_per_track db 0 ; Sectors per track (set at init time)
- gap_len db 0
- db 0
- fmt_gap_len db 0
- db 3 dup(0)
-
- ;
- ; Save area for old parm pointer
- ;
- old_pptr dw 0, 0
-
- ;
- ; ID and # for the current disk
- ;
- curr_disk_id db 'NO NAME',0
- db 0
- curr_disk_num db 0ffh
-
- ;
- ; Counter of how many sectors read/written
- ;
- io_count dw 0
-
- ;
- ; Count of how many sectors to go
- ;
- io_to_go dw 0
-
- ;
- ; Flag indicating whether or not we have EGA/VGA video available. This is used
- ; in the prompt_user routine - if have_evga != 0, then we read the number of
- ; character lines on screen from 40:84. If have_evga == 0, then we always use
- ; line 24.
- ;
- have_evga db 0
- last_curs dw 0
-
- ;
- ; If we're running under DOS 3.10 or higher, we get the drive number assigned
- ; from the the request block and save it here.
- ;
- our_drive db 0ffh
-
- ;
- ; Data used to manage the FAT/dir cache.
- ;
- cache_ptr dw 0,0 ; Pointer to the cache
- cache_flags db 0 ; Bit flags
-
- ;
- ; Messages that get displayed at various times
- ;
- need_disk db 'Put disk ID '
- pr_disk_id db 8 dup(' ')
- db ' #'
- pr_disk_num db ' '
- db ' in drive A:, then press a key',0
-
- need_first_disk db 'Put disk #0 of the set you want to use'
- db ' in drive A:, then press a key',0
-
- ;
- ; Save area for the chunk of screen we diddle
- ;
- screen_save dw 80 dup(0)
-
- ;
- ; Buffer for the boot sector (containing ID, etc.)
- ;
- boot_sect_buf db 512 dup(0)
-
- ;*****************************************************************************
- ;
- ; These 2 tables are used to translate BIOS error codes into the status codes
- ; expected by DOS and VHPREP, respectively
- ;
- ;*****************************************************************************
- DOS_sts_table db 80h, DE_NOT_READY
- db 40h, DE_SEEK_ERROR
- db 20h, DE_GEN_FAIL
- db 10h, DE_CRC_ERROR
- db 9, DE_WRITE_FAULT
- db 8, DE_WRITE_FAULT
- db 4, DE_NOT_FOUND
- db 3, DE_WRITE_PROT
- db 2, DE_GEN_FAIL
- db 1, DE_GEN_FAIL
-
- CMD_sts_table db 80h, STS_TIMEOUT
- db 40h, STS_BAD_SEEK
- db 20h, STS_BAD_NEC
- db 10h, STS_BAD_CRC
- db 9, STS_DMA_BOUND
- db 8, STS_BAD_DMA
- db 4, STS_NOT_FND
- db 3, STS_WRITE_PROT
- db 2, STS_ADDR_MARK
- db 1, STS_BAD_CMD
-
-
- ;*****************************************************************************
- ;
- ; BIOS parameter block (BPB) that DOS is going to use
- ;
- ;*****************************************************************************
- our_bpb DOS_BPB <512, 8, 1, 2, 256, (13 * 799), 0fch, 4, 47, 26, 0>
-
- our_bpb_array dw offset our_bpb
-
-
- ;*****************************************************************************
- ;
- ; For this version, at least, this driver will support the OPEN and CLOSE
- ; calls. These will increment and decrement a reference counter that will be
- ; used to know when to the write the FAT cache (if caching is enabled).
- ;
- ;*****************************************************************************
- ref_count dw 0
-
-
- subttl Device driver STRATEGY and INTERRUPT routines
- page
-
- ;*****************************************************************************
- ;
- ; Strategy entry point. Used for both drivers
- ;
- ;*****************************************************************************
- strategy proc far
-
- mov cs:req_ptr[0],bx ; Save the pointer
- mov cs:req_ptr[2],es ;
- ret ; Return to DOS
-
- strategy endp
-
-
- ;*****************************************************************************
- ;
- ; Interrupt routine for the VH driver
- ;
- ;*****************************************************************************
- vh_int proc far
-
- assume ds:nothing, es:nothing
-
- call save_regs ; Save the registers
- cld ; Make sure of direction
- push cs ; Set DS to our segment
- pop ds ;
-
- assume ds:vhard_seg
-
- if DEBUG
- call dump_entry
- endif
-
- les si,dword ptr req_ptr ; Point to the request packet
- mov bl,es:[si].DDrh_command ; Get the command
- mov es:[si].DDrh_status,0 ; Default to no error
- cmp bl,DC_REM_MEDIA ; Check for the highest we support
- jbe vhint_l1 ; Use it - it's in range
- call err_fnc ; Return that it's a bad command
- jmp short vhint_exit ; And return
- vhint_l1:
- sub bh,bh ; Make the command a word
- shl bx,1 ; Now a word offset
- call vh_dispatch[bx] ; Vector to the routine
- les si,dword ptr req_ptr ; Point back to the request packet
- or es:[si].DDrh_status,DS_DONE; Say we're done
- vhint_exit:
-
- if DEBUG
- call dump_exit
- endif
-
- call rest_regs ; Restore the registers
- ret ; Return to DOS
-
- vh_int endp
-
-
- if DEBUG
- entry_str db 'Entry:',0
- exit_str db 'Exit: ',0
-
- dump_entry proc near
-
- mov si,offset entry_str
- jmp short dump_either
-
- dump_entry endp
-
- dump_exit proc near
-
- mov si,offset exit_str
- dump_either:
- call outp_str
- les si,req_ptr
- mov cl,es:[si]
- sub ch,ch
- dmpe_l1:
- mov al,' '
- call outp_char
- lods byte ptr es:[si]
- call outp_byte
- loop dmpe_l1
- mov al,13
- call outp_char
- mov al,10
- call outp_char
- ret
-
- dump_exit endp
-
- outp_str proc near
-
- lodsb
- or al,al
- jz otps_l1
- call outp_char
- jmp short outp_str
- otps_l1:
- ret
-
- outp_str endp
-
- outp_char proc near
-
- push ax
- push dx
- sub ah,ah
- sub dx,dx
- int 17h
- pop dx
- pop ax
- ret
-
- outp_char endp
-
- outp_byte proc near
-
- push ax
- shr al,1
- shr al,1
- shr al,1
- shr al,1
- call outp_nyb
- pop ax
- outp_nyb:
- and al,0fh
- add al,90h
- daa
- adc al,40h
- daa
- call outp_char
- ret
-
- outp_byte endp
- endif
-
- ;*****************************************************************************
- ;
- ; Interrupt routine for the dummy VHARDCTL driver
- ;
- ;*****************************************************************************
- du_int proc far
-
- call save_regs ; Save all the registers
- push cs ; Set DS to our segment
- pop ds
-
- assume ds:vhard_seg
-
- les si,dword ptr req_ptr ; Point to the request packet
- mov bl,es:[si].DDrh_command ; Get the command
- cmp bl,DC_IOCTL_WRITE ; Check for the highest we support
- jbe duint_l1 ; Use it - it's in range
- call err_fnc ; Return that it's a bad command
- jmp short duint_exit ; And return
- duint_l1:
- sub bh,bh ; Make the command a word
- shl bx,1 ; Now a word offset
- call du_dispatch[bx] ; Vector to the routine
- les bx,dword ptr req_ptr ; Point back to the request packet
- or es:[bx].DDrh_status,DS_DONE; Say we're done
- mov es:[bx].DDir_count,size VH_CMD
- duint_exit:
- call rest_regs ; Restore them now
- ret ; Return to DOS
-
- du_int endp
-
-
- subttl Dispatch tables for INTERRUPT routines
- page
-
- ;*****************************************************************************
- ;
- ; Dispatch table for the VH driver
- ;
- ;*****************************************************************************
- vh_dispatch dw drv_init ; 0 - Init
- dw vh_med_check ; 1 - Media check
- dw vh_build_bpb ; 2 - Build BPB
- dw err_fnc ; 3 - IOCTL read (not supported)
- dw vh_read ; 4 - Read
- dw err_fnc ; 5 - Peek input (not for blk)
- dw err_fnc ; 6 - Input status (not for blk)
- dw err_fnc ; 7 - Input flush (not for blk)
- dw vh_write ; 8 - Write
- dw vh_write ; 9 - Write w/verify
- dw err_fnc ; A - Output status (not for blk)
- dw err_fnc ; B - Output flush (not for blk)
- dw err_fnc ; C - IOCTL write (not supported)
- dw vh_open ; D - Device open
- dw vh_close ; E - Device close
- dw vh_set_rem ; F - "Removable media" check
-
- du_dispatch dw du_init ; 0 - Init
- dw err_fnc ; 1 - Media check
- dw err_fnc ; 2 - Build BPB
- dw err_fnc ; 3 - IOCTL read (not supported)
- dw err_fnc ; 4 - Read
- dw err_fnc ; 5 - Peek input (not for blk)
- dw err_fnc ; 6 - Input status (not for blk)
- dw err_fnc ; 7 - Input flush (not for blk)
- dw err_fnc ; 8 - Write
- dw err_fnc ; 9 - Write w/verify
- dw err_fnc ; A - Output status (not for blk)
- dw err_fnc ; B - Output flush (not for blk)
- dw du_write ; C - IOCTL Write
-
-
- subttl Error function
- page
-
- ;*****************************************************************************
- ;
- ; Error function - sets the error bit to 1, and the error code to 3 (Unknown
- ; command).
- ;
- ;*****************************************************************************
- err_fnc proc near
-
- mov al,DE_BAD_COMMAND ; Set error code
- ;
- ; Alternate entry point. Call with AL set to error code
- ;
- err_return:
- mov ah,DS_ERROR shr 8 ; Set ERROR bit
- les bx,dword ptr req_ptr ; Point to the request packet
- mov es:[bx].DDrh_status,ax ; Set return status
- ret ; Return to caller
-
- err_fnc endp
-
-
- subttl Driver function 1 - Media check
- page
-
- ;*****************************************************************************
- ;
- ; Driver function 1 - Media check
- ;
- ;*****************************************************************************
- vh_med_check proc near
-
- assume ds:vhard_seg, es:nothing
-
- call set_disk_parms ; Set up the diskette parameters
- call check_boot_sect ; See if the disk ID has changed
- jc vhmc_error ; Exit if error
- jz vhmc_nochange ; Return properly if no change
- les si,dword ptr req_ptr ; Point to the DOS request block
- mov es:[si].DDmr_return,0ffh; Note that it's changed
- test cache_flags,CACHE_INSTALLED ; Is the cache there?
- jz vhmc_l1 ; Nope - never mind
- test cache_flags,CACHE_DIRTY ; Is there anything in it?
- jz vhmc_l1 ; Nope - never mind
- call do_flush_cache ; Flush the FAT cache
- vhmc_l1:
- and cache_flags,not (CACHE_VALID or CACHE_DIRTY)
- mov ref_count,0 ; Force the ref count to 0
- call get_disk_0 ; Make sure we get the first disk
- cmp byte ptr dos_ver[0],3 ; Is it DOS 3.x (or higher)?
- jb vhmc_exit ; Nope - exit
- mov word ptr es:[si].DDmr_idptr[0],offset curr_disk_id
- mov word ptr es:[si].DDmr_idptr[2],cs
- jmp short vhmc_exit ;
- vhmc_nochange:
- les si,dword ptr req_ptr ; Point to the DOS request block
- mov es:[si].DDmr_return,1 ; Set the return value
- test cache_flags,CACHE_INSTALLED ; Is the cache there?
- jz vhmc_exit ; No - never mind
- test cache_flags,CACHE_VALID ; Does it hold valid data?
- jnz vhmc_exit ; Yep - just exit
- call get_disk_0 ; Load the cache
- jmp short vhmc_exit ; Exit now
- vhmc_error:
- mov ax,DE_NOT_READY ;
- call err_return ; Indicate the error
- vhmc_exit:
- call rest_disk_parms ; Restore the diskette parameters
- ret ; Return to dispatcher
-
- vh_med_check endp
-
-
- subttl Driver function 2 - Build BPB
- page
-
- ;*****************************************************************************
- ;
- ; Driver function 2 - Build BPB
- ;
- ; Also, the disk ID is gotten from the disk in the drive. If there is no disk
- ; in the drive, we prompt for it.
- ;
- ;*****************************************************************************
- vh_build_bpb proc near
-
- assume ds:vhard_seg, es:nothing
-
- save si, es
- mov word ptr es:[si].DDbr_bpb_ptr[0],offset our_bpb
- mov word ptr es:[si].DDbr_bpb_ptr[2],cs
- call get_disk_0 ; Get the first disk
- jnc vhbb_exit ; If they didn't press Esc, exit
- mov ax,DE_NOT_READY ; Say that the drive wasn't ready
- call err_return ;
- vhbb_exit:
- restore
- ret ; Return to DOS
-
- vh_build_bpb endp
-
-
- subttl Driver function 4 - Read
- page
-
- ;*****************************************************************************
- ;
- ; Driver function 4 - Read
- ;
- ;*****************************************************************************
- vh_read proc near
-
- assume ds:vhard_seg, es:nothing
-
- mov bp,offset sect_read ; Address of I/O routine
- jmp short vhwt_l1 ; Do the I/O operation
-
- vh_read endp
-
-
- subttl Driver functions 8 & 9 - Write & Write w/Verify
- page
-
- ;*****************************************************************************
- ;
- ; Driver function 8 - Write (also fn 9, Write w/Verify)
- ;
- ;*****************************************************************************
- vh_write proc near
-
- assume ds:vhard_seg, es:nothing
-
- mov bp,offset sect_write ; Point to the right routine
- vhwt_l1:
- call set_disk_parms ; Set up the diskette parameters
- mov ax,es:[si].DDir_start ; Get starting sector
- mov cx,es:[si].DDir_count ; Get the count
- les bx,es:[si].DDir_buffer ; Point to the buffer
- or ax,ax ; See if DOS wants a sector that falls
- jz vhwt_l99 ; in the range 1 to 24
- cmp ax,24 ;
- ja vhwt_l99 ;
- test cache_flags,CACHE_INSTALLED ; Is the cache there?
- jz vhwt_l99 ; No - never mind
- test cache_flags,CACHE_VALID ; Do we have valid data in the cache?
- jz vhwt_l99 ; No - read from disk
- dec ax ; Adjust start sector for cache
- push cx ; Save the count
- mov cl,9 ; Make sector # offset into cache
- shl ax,cl ;
- pop cx
- mov dx,cx ; Turn sector count into byte count
- mov cl,9 ;
- shl dx,cl ;
- mov cx,dx ;
- cmp bp,offset sect_read ; Are we reading?
- jne vhwt_l2 ; No - set up for writing
- lds si,dword ptr cache_ptr ; Point to the cache
- add si,ax ; Point to start of first sector
- mov di,bx ; Point into the buffer
- jmp short vhwt_l3 ; Continue
- vhwt_l2:
- or cache_flags,CACHE_DIRTY ; The cache is dirty now
- push es ; Need DS:SI pointing to the buffer
- pop ds ;
- mov si,bx ;
- les di,dword ptr cs:cache_ptr; And ES:DI pointing to the cache
- add di,ax ; Point to the sector in the cache
- vhwt_l3:
- shr cx,1 ; Move as many words as possible
- rep movsw ;
- rcl cx,1 ; Move the odd byte
- rep movsb ;
- push cs ; Set DS back to our segment
- pop ds ;
- jmp short vhwt_exit ; Exit now
- vhwt_l99:
- call io_operation ; Do the I/O operation
- jnc vhwt_exit ; Exit if no error
- mov bx,offset DOS_sts_table ; Point to translation table
- call xlat_status ; Make it a DOS error code
- cmp al,DE_WRITE_FAULT ; Generic "Write Fault"?
- jne vhwt_err ; No - just exit
- cmp bp,offset sect_write ; Was it a write op?
- je vhwt_err ; Yep - never mind
- mov al,DE_READ_FAULT ; Make that a "Read Fault"
- vhwt_err:
- call err_return ; Return the error
- vhwt_exit:
- call rest_disk_parms ; Restore the disk parameters
- ret ; Return to int routine
-
- vh_write endp
-
-
- ;*****************************************************************************
- ;
- ; Perform a disk I/O operation.
- ;
- ; Input:
- ; AX starting sector
- ; CX # of sectors
- ; ES:BX pointer to buffer
- ; BP address of routine to call to read or write
- ;
- ; Output:
- ; CF = 1 an error occurred
- ; AL = error code to return
- ; CF = 0:
- ; CX number of sectors actually transferred
- ;
- ; Registers:
- ; All but AX and CX preserved.
- ;
- ;*****************************************************************************
- io_operation proc near
-
- mov io_to_go,cx ; Save count as number to do
- mov di,ax ; Save start sector for a bit
- sub ax,ax ; Figure out if it could wrap
- dec ax ;
- sub ax,bx ;
- mov cl,9 ;
- shr ax,cl ;
- cmp ax,io_to_go ; Can it hold that many?
- jae io_op1 ; Yep - never mind
- mov io_to_go,ax ; Set to just that many
- io_op1:
- sub ax,ax ; Clear the current count
- mov io_count,ax ;
- mov ax,di ; Get the starting sector back
- call calc_disk ; Figure out where that is
- call get_num_disk ; Get the right disk
- jc io_op_error ; Don't bother if they hit Esc or error
- ;
- ; At this point, we have figured out what disk to use, and it's in the drive.
- ; Now, all we have to do is start reading or writing.
- ;
- mov al,11 ; Figure out how many sectors to start
- sub al,cl ;
- cbw ;
- mov si,ax ; Save it for later
- io_op3:
- cmp si,io_to_go ; See if there are that many to go
- jbe io_op4 ; Yep - just do the operation
- mov si,io_to_go ; Just do that many
- mov ax,si ;
- io_op4:
- call bp ; Call the appropriate routine
- jc io_op_err2 ; Exit if error
- io_op5:
- add io_count,si ; Add to number complete
- sub io_to_go,si ; Knock that many off the count to do
- jz io_op_exit ; If it went to 0, we're done
- mov ax,si ;
- push cx ; Save it (gets used for shift count)
- mov cl,9 ; Multiply by 512
- shl ax,cl ;
- pop cx ;
- add bx,ax ; Move the buffer pointer
- jnc io_op6 ; If it wraps, we're done
- clc ; No error
- jmp short io_op_exit ; Just exit
- io_op6:
- mov al,10 ; Do 10 the next time 'round
- cbw ;
- mov si,ax ;
- mov cl,1 ; Start at sector 1
- xor dh,1 ; Next head (0 or 1)
- jnz io_op3 ; If just going to other head, loop
- inc ch ; Next track
- cmp ch,40 ; Still on this disk?
- jb io_op3 ; Yep - do more I/O
- inc curr_disk_num ; Next disk
- mov al,curr_disk_num ; Get the disk number
- call get_num_disk ; Get that disk in the drive
- jc io_op_error ; Exit if error or Esc pressed
- mov cx,2 ; Start back at track 0, sector 2
- mov ax,9 ; Do up to 9 more sectors on next disk
- mov si,ax ;
- jmp short io_op3 ; Loop back
- io_op_error:
- mov ah,80h ; Say that it wasn't ready
- io_op_err2:
- stc ;
- io_op_exit:
- mov cx,io_count ; Get number transferred
- ret ; Return to caller
-
- io_operation endp
-
-
- subttl Driver function 0DH - Device open
- page
-
- ;*****************************************************************************
- ;
- ; Driver function 0DH - Device open
- ;
- ;*****************************************************************************
- vh_open proc near
-
- inc ref_count ; Just increment the reference count
- ret ; Return to caller
-
- vh_open endp
-
-
- subttl Driver function 0EH - Device close
- page
-
- ;*****************************************************************************
- ;
- ; Driver function 0EH - Device close
- ;
- ;*****************************************************************************
- vh_close proc near
-
- dec ref_count ; Decrement the reference count
- jnz vhcl_exit ; If there are still files open, exit
- test cache_flags,CACHE_INSTALLED ; Is the cache there?
- jz vhcl_exit ; Nope - just exit
- test cache_flags,CACHE_AUTO ; Do we want to flush automagically?
- jz vhcl_exit ; Nope - just exit
- test cache_flags,CACHE_DIRTY ; Is the cache dirty?
- jz vhcl_exit ; Nope - never mind
- call set_disk_parms ; Set up the diskette parameters
- call do_flush_cache ; Flush that cache
- call rest_disk_parms ; Get the old diskette parms back
- vhcl_exit:
- ret ; Return to caller
-
- vh_close endp
-
-
- subttl Driver function 0FH - "Removable media" check
- page
-
- ;*****************************************************************************
- ;
- ; Driver function 0FH - "Removable media" check
- ;
- ;*****************************************************************************
- vh_set_rem proc near
-
- and es:[si].DDrh_status,not DS_BUSY ; Say we're removable
- ret ; Return to caller
-
- vh_set_rem endp
-
-
- subttl Cache functions
- page
-
- ;*****************************************************************************
- ;
- ; Flush the FAT/root dir cache
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- do_flush_cache proc near
-
- assume ds:vhard_seg, es:nothing
-
- save ax, bx, cx, bp, es
- les bx,dword ptr cache_ptr ; Point to the cache
- mov bp,offset sect_write ; Routine to do the I/O
- dflc_l1:
- mov ax,1 ; Start with logical sector 1
- mov cx,24 ; Write 24 Sectors
- call io_operation ;
- jc dflc_l1 ; No matter what, it must be done
- and cache_flags,not CACHE_DIRTY ; Turn off the "dirty" bit
- restore
- ret ; Return to caller
-
- do_flush_cache endp
-
-
- subttl VHARDCTL IOCTL write function
- page
-
- ;*****************************************************************************
- ;
- ; This is the only driver function supported by the dummy control driver
- ; VHARDCTL. It dispatches to one of the commands.
- ; On entry, ES:SI points to the driver request packet.
- ;
- ;*****************************************************************************
- du_write proc near
-
- assume ds:vhard_seg, es:nothing
-
- mov es:[si].DDrh_status,0 ; Start with no error
- les bx,es:[si].DDir_buffer ; Point to the buffer
- mov al,es:[bx].VC_cmd_code ; Get the command to perform
- cmp al,LAST_CMD ; Valid command?
- jbe duwr_ok ; Yep - dispatch it
- call err_fnc ; Report an invalid driver command
- jmp short duwr_exit ; Exit now
- duwr_ok:
- cbw ; Make it a word
- shl ax,1 ; Make it a dispatch table offset
- mov di,ax ; Into a usable register
- call cmd_dispatch[di] ; Call the routine
- duwr_exit:
- ret ; Return to caller
-
- du_write endp
-
- subttl VHARDCTL handling routines
- page
-
- ;*****************************************************************************
- ;
- ; The following routines handle the commands to VHARDCTL. Each of them expects
- ; ES:BX to point to the command block (NOT the driver request block!), and
- ; DS to be the same as CS.
- ;
- ;*****************************************************************************
-
-
- ;*****************************************************************************
- ;
- ; Called when VHARDCTL gets a "Format Track" command
- ;
- ;*****************************************************************************
- cmd_format_trk proc near
-
- assume ds:vhard_seg, es:nothing
-
- push bp ; Save retry counter
- call set_disk_parms ; Set up the diskette parameters
- mov al,es:[bx].VC_track ; Get the track/head to format
- mov ch,7fh ; Mask to keep track
- and ch,al ; Get the track
- rol al,1 ; Get the head
- and al,1 ;
- mov dh,al ; Get the head in the right place
- mov dl,$OUR_DRIVE ;
- mov bp,3 ; 3 tries
- cfmt_l1:
- mov ax,50ah ; Format command
- push es ; Save these
- push bx ;
- les bx,es:[bx].VC_buffer ; Point to the buffer
- int 13h ; Call BIOS to do it
- pop bx ; Get pointer back
- pop es ;
- jnc cfmt_l3 ; If it went, continue
- dec bp ; Count off a try
- jz cfmt_l2 ; If it failed, return error
- mov ah,0 ; Do a disk reset
- int 13h ;
- jnc cfmt_l1 ; If it FAILED to reset, drop
- cfmt_l2:
- push bx ; Save cmd block ptr
- mov bx,offset CMD_sts_table ; Point to translation table
- call xlat_status ; Translate to our error code
- pop bx ; Point back to the cmd block
- mov es:[bx].VC_status,al ; Set the return status
- jmp short cfmt_exit ; Exit now
- cfmt_l3:
- mov es:[bx].VC_status,STS_OK; Report success
- cfmt_exit:
- call rest_disk_parms ; Restore the diskette parameters
- pop bp ; Get back what we used
- ret ; Return to caller
-
- cmd_format_trk endp
-
-
- ;*****************************************************************************
- ;
- ; Called when VHARDCTL gets a "Read Track" command
- ;
- ;*****************************************************************************
- cmd_read_trk proc near
-
- call set_disk_parms ; Set up the diskette parameters
- mov al,es:[bx].VC_track ; Get the track to read
- mov ch,7fh ; Get mask for the track
- and ch,al ; Get the track
- mov cl,1 ; Start with sector 1
- rol al,1 ; Get the head
- and al,1 ; Mask it out
- mov dh,al ; Into the right register
- mov dl,$OUR_DRIVE ; Drive to read from
- mov al,10 ; Number to read
- push bx ; Save this ptr so we can set status
- push es ;
- les bx,es:[bx].VC_buffer ; Point to the buffer
- call sect_read ; Try to read 'em
- pop es ; Restore the pointer
- pop bx ;
- jnc crdt_ok ; If ok, return success
- push bx
- mov bx,offset CMD_sts_table ; Point to the translation table
- call xlat_status ; Get the status code we want
- pop bx ; Get the pointer back
- mov es:[bx].VC_status,al ; Set the return status
- jmp short crdt_exit ; Exit now
- crdt_ok:
- mov es:[bx].VC_status,STS_OK; Return that it succeeded
- crdt_exit:
- call rest_disk_parms ; Restore the diskette parameters
- ret ; Return to caller
-
- cmd_read_trk endp
-
-
- ;*****************************************************************************
- ;
- ; Called when VHARDTCL gets a "Write Track" command
- ;
- ;*****************************************************************************
- cmd_write_trk proc near
-
- call set_disk_parms ; Set up the diskette parameters
- mov al,es:[bx].VC_track ; Get the track to write
- mov ch,7fh ; Get mask for the track
- and ch,al ; Get the track
- mov cl,1 ; Start with sector 1
- rol al,1 ; Get the head
- and al,1 ; Mask it out
- mov dh,al ; Into the right register
- mov dl,$OUR_DRIVE ; Drive to write to
- mov al,10 ; Number to write
- push bx ; Save this ptr so we can set status
- push es ;
- les bx,es:[bx].VC_buffer ; Point to the buffer
- call sect_write ; Try to write 'em
- pop es ; Restore the pointer
- pop bx ;
- jnc cwrt_ok ; If ok, return success
- push bx
- mov bx,offset CMD_sts_table ; Point to the translation table
- call xlat_status ; Get the status code we want
- pop bx ; Get the pointer back
- mov es:[bx].VC_status,al ; Set the return status
- jmp short cwrt_exit ; Exit now
- cwrt_ok:
- mov es:[bx].VC_status,STS_OK; Return that it succeeded
- cwrt_exit:
- call rest_disk_parms ; Restore the diskette parameters
- ret ; Return to caller
-
- cmd_write_trk endp
-
-
- ;*****************************************************************************
- ;
- ; Called when VHARDCTL gets a "Verify Track" command
- ;
- ;*****************************************************************************
- cmd_verify_trk proc near
-
- call set_disk_parms ; Set up the diskette parameters
- mov al,es:[bx].VC_track ; Get the track to verify
- mov ch,7fh ; Get mask for the track
- and ch,al ; Get the track
- mov cl,1 ; Start with sector 1
- rol al,1 ; Get the head
- and al,1 ; Mask it out
- mov dh,al ; Into the right register
- mov dl,$OUR_DRIVE ; Drive to read from
- mov al,10 ; Number to verify
- call sect_verify ; Try to verify 'em
- jnc crdt_ok ; If ok, return success
- push bx
- mov bx,offset CMD_sts_table ; Point to the translation table
- call xlat_status ; Get the status code we want
- pop bx ; Get the pointer back
- mov es:[bx].VC_status,al ; Set the return status
- jmp short crdt_exit ; Exit now
- cvft_ok:
- mov es:[bx].VC_status,STS_OK; Return that it succeeded
- cvft_exit:
- call rest_disk_parms ; Restore the diskette parameters
- ret ; Return to caller
-
- cmd_verify_trk endp
-
-
- ;*****************************************************************************
- ;
- ; Called when VHARDCTL gets a "Set Cache" command
- ;
- ; There are 5 sub-commands supported. The subcommand is gotten from the byte
- ; used by the other routines to specify the track.
- ;
- ; The 6 sub-commands are:
- ; 0 disable the cache
- ; On return, the status byte will be set to either STS_OK,
- ; STS_NOT_ENAB (meaning that the cache is not installed), or
- ; STS_CACHE_DIRTY (meaning that the cache needs to be flushed).
- ; If the cache is successfully disabled, the VC_track byte will
- ; be set to the current flags for the cache and the VC_buffer
- ; will be set to the address of the cache.
- ; 1 enable the cache
- ; On return, the status byte will be set to either STS_OK or
- ; STS_ALREADY (meaning that the cache is already installed).
- ; If the cache is already installed, the VC_track byte will be
- ; set to the current flags for the cache and the VC_buffer will
- ; be set to the current cache address.
- ; 2 flush the cache
- ; On return, the status byte will be set to indicate the result.
- ; If the cache isn't dirty, this is a no-op.
- ;
- ; 3 disable cache autoflush
- ;
- ; 4 enable cache autoflush. VHCACHE will only make this call if
- ; the DOS version is 3.00 or higher AND SHARE.EXE is loaded.
- ;
- ; 5 get cache status. Returns the following at VC_buffer:
- ; byte cache flags
- ; dword address of cache
- ;
- ;*****************************************************************************
- cmd_set_cache proc near
-
- mov al,es:[bx].VC_track ; Get the sub-command
- cmp al,5 ; See if it's valid
- jbe cmsc_l1 ; Yep - handle it
- mov es:[bx].VC_status,STS_UNK_CMD ; Return a bad command
- ret ; Return to caller
- cmsc_l1:
- cbw ; Make it a word
- shl ax,1 ; Make it a table offset
- mov di,ax ; Into a pointer reg
- jmp cache_dispatch[di] ; Switch to the appropriate routine
-
- cmd_set_cache endp
-
-
- ;*****************************************************************************
- ;
- ; Handle a "Cache Disable" command
- ;
- ; If the cache isn't enabled, return STS_NOT_ENAB.
- ; If the CACHE_DIRTY bit is set (meaning that the cache needs to be flushed),
- ; set ES:[BX].VC_track to the current cache flags, set ES:[BX].VC_buffer to
- ; point to the cache, and return STS_CACHE_DIRTY.
- ;
- ; Otherwise, disable it, set ES:[BX].VC_track to the current
- ; cache flags, set ES:[BX].VC_buffer to point to the cache, and return STS_OK.
- ;
- ;*****************************************************************************
- disable_cache proc near
-
- test cache_flags,CACHE_INSTALLED ; Is the cache installed?
- jnz dsch_l1 ; Yep - see if it's dirty
- mov es:[bx].VC_status,STS_NOT_ENAB ; Return it wasn't installed
- jmp short dsch_exit ; Exit now
- dsch_l1:
- test cache_flags,CACHE_DIRTY ; Is the cache dirty?
- jz dsch_l2 ; Nope - disable it
- mov al,cache_flags ; Get the current flags
- mov es:[bx].VC_track,al ; Return 'em
- mov al,STS_CACHE_DIRTY ; Report it's dirty
- jmp short dsch_l3 ; Continue
- dsch_l2:
- mov al,STS_OK ; Return it's ok
- mov cache_flags,0 ; Get rid of all cache flags
- dsch_l3:
- mov es:[bx].VC_status,al ; Save return status
- mov ax,cache_ptr[0] ; Return address of cache
- mov word ptr es:[bx].VC_buffer[0],ax;
- mov ax,cache_ptr[2] ;
- mov word ptr es:[bx].VC_buffer[2],ax;
- dsch_exit:
- ret ; Return to caller
-
- disable_cache endp
-
-
- ;*****************************************************************************
- ;
- ; Handle a "Cache Enable" command.
- ;
- ; If the cache is enabled already, set ES:[BX].VC_track to the current cache
- ; flags, set ES:[BX].VC_buffer to point to the cache, and return STS_ALREADY.
- ;
- ; Otherwise, set our internal cache pointer to ES:[BX].VC_buffer, set the
- ; cache flags to CACHE_INSTALLED, and return STS_OK.
- ;
- ;*****************************************************************************
- enable_cache proc near
-
- test cache_flags,CACHE_INSTALLED ; Is it installed?
- jz ench_l1 ; No - install now
- mov es:[bx].VC_status,STS_ALREADY ; Say it's already installed
- mov al,cache_flags ; Get the current flags
- mov es:[bx].VC_track,al ; Return 'em
- mov ax,cache_ptr[0] ; Return cache address
- mov word ptr es:[bx].VC_buffer[0],ax;
- mov ax,cache_ptr[2] ;
- mov word ptr es:[bx].VC_buffer[2],ax;
- jmp short ench_exit ; Exit now
- ench_l1:
- ;
- ; The cache isn't installed yet. Install it now.
- ; Address is in ES:[BX].VC_buffer.
- ;
- mov ax,word ptr es:[bx].VC_buffer[0]; Point to new cache
- mov cache_ptr[0],ax ;
- mov ax,word ptr es:[bx].VC_buffer[2];
- mov cache_ptr[2],ax ;
- mov cache_flags,CACHE_INSTALLED ;
- mov es:[bx].VC_status,STS_OK ; Return ok status
- ench_exit:
- ret
-
- enable_cache endp
-
-
- ;*****************************************************************************
- ;
- ; Handle a "Flush Cache" command.
- ;
- ; If the cache is not installed, return STS_NOT_ENAB.
- ;
- ; If the cache is not dirty, return STS_OK.
- ;
- ; Otherwise, try to flush the cache, returning the status from the disk I/O
- ; operation.
- ;
- ;*****************************************************************************
- flush_cache proc near
-
- test cache_flags,CACHE_INSTALLED ; Is the cache installed?
- jnz flch_l1 ; Yep - see if it's dirty
- mov al,STS_NOT_ENAB ; Return that it ain't there
- jmp short flch_done
- flch_l1:
- test cache_flags,CACHE_DIRTY ; Is it dirty?
- jz flch_ok ; No - just return ok
- call set_disk_parms ; Set the diskette parameters
- call do_flush_cache ; Do the flush
- pushf ; Save result flag
- call rest_disk_parms ; Restore the parameters
- popf ; Get error flag back
- jnc flch_ok ; If it went, return success
- push bx ; Save command blk ptr
- mov bx,offset CMD_sts_table ; Point to translate table
- call xlat_status ; Translate the status
- pop bx ;
- jmp short flch_done ; Exit now
- flch_ok:
- mov al,STS_OK ; Return success
- flch_done:
- mov es:[bx].VC_status,al ; Save the status
- ret
-
- flush_cache endp
-
-
- ;*****************************************************************************
- ;
- ; Handle the "Disable Auto-Flush" command.
- ;
- ; If the cache is not installed, return STS_NOT_ENAB.
- ;
- ;*****************************************************************************
- disab_auto proc near
-
- test cache_flags,CACHE_INSTALLED ; Is that baby installed?
- jnz dsau_l1 ; Yep - disable autoflush
- mov es:[bx].VC_status,STS_NOT_ENAB ; Return it ain't there
- jmp short dsau_exit ; Exit now
- dsau_l1:
- mov es:[bx].VC_status,STS_OK ; Return success
- and cache_flags,not CACHE_AUTO ; Disable it
- dsau_exit:
- ret ; Return to caller
-
- disab_auto endp
-
-
- ;*****************************************************************************
- ;
- ; Handle the "Enable Autoflush" command
- ;
- ; If the cache is not installed, return STS_NOT_ENAB.
- ;
- ;*****************************************************************************
- enab_auto proc near
-
- test cache_flags,CACHE_INSTALLED ; Is that baby installed?
- jnz enau_l1 ; Yep - disable autoflush
- mov es:[bx].VC_status,STS_NOT_ENAB ; Return it ain't there
- jmp short enau_exit ; Exit now
- enau_l1:
- mov es:[bx].VC_status,STS_OK ; Return success
- or cache_flags,CACHE_AUTO ; Enable it
- enau_exit:
- ret ; Return to caller
-
- enab_auto endp
-
-
- ;*****************************************************************************
- ;
- ; Handle a "Get Cache Information" command.
- ;
- ; Returns the cache flags and the address of the cache.
- ;
- ;*****************************************************************************
- get_cache_info proc near
-
- mov es:[bx].VC_status,STS_OK; Always returns success
- les di,es:[bx].VC_buffer ; Point to the buffer
- mov al,cache_flags ; Get the current cache flags
- stosb ; Return 'em
- mov ax,cache_ptr[0] ; Get the cache address offset
- stosw ; Stuff it
- mov ax,cache_ptr[2] ; Same with the cache address segment
- stosw ;
- ret ; Return to caller
-
- get_cache_info endp
-
-
- ;*****************************************************************************
- ;
- ; Called when VHARDCTL gets a "Get Driver Data" command. This copies data from
- ; our data area into the buffer.
- ; Data returned in the buffer:
- ; byte drive number assigned by DOS
- ; word VHARD version
- ; struc BPB used
- ;
- ;*****************************************************************************
- cmd_get_vhdata proc near
-
- les di,es:[bx].VC_buffer ; Point to their buffer
- mov al,our_drive ; Get the drive number
- stosb ; Store it
- mov ax,VHARD_version ; Store the driver version
- stosw ;
- mov si,offset our_bpb ; Copy our BPB
- mov cx,size DOS_BPB ;
- rep movsb ;
- ret ; Return to caller
-
- cmd_get_vhdata endp
-
-
- ;*****************************************************************************
- ;
- ; This dispatch table is used to handle the special commands called via
- ; VHARDCTL.
- ;
- ;*****************************************************************************
- cmd_dispatch dw cmd_format_trk ; Format a track
- dw cmd_read_trk ; Read a track
- dw cmd_write_trk ; Write a track
- dw cmd_verify_trk ; Verify a track
- dw cmd_set_cache ; Deal with the cache
- dw cmd_get_vhdata ; Get data
-
- ;*****************************************************************************
- ;
- ; Yet another dispatch table (this one's for the cache commands)
- ;
- ;*****************************************************************************
- cache_dispatch dw disable_cache ; Disable the cache
- dw enable_cache ; Enable the cache
- dw flush_cache ; Flush the cache
- dw disab_auto ; Disable auto-flush
- dw enab_auto ; Enable auto-flush
- dw get_cache_info ; Get cache information
-
-
- subttl Utility routines
- page
-
- ;*****************************************************************************
- ;
- ; U T I L I T Y R O U T I N E S
- ;
- ;*****************************************************************************
-
-
- ;*****************************************************************************
- ;
- ; Save all registers
- ;
- ;*****************************************************************************
- save_regs proc near
-
- assume ss:nothing
-
- save ax, bx, cx, dx, si, di, ds, es
- mov ax,bp ; Get current BP value
- mov bp,sp ; Point into the stack
- xchg ax,[bp + 16] ; Swap it with the return address
- push ax ; Put that on the stack
- mov ax,[bp + 14] ; Set AX back to original value
- mov bp,[bp + 16] ; Same with BP
- ret ; Return to caller
-
- save_regs endp
-
-
- ;*****************************************************************************
- ;
- ; Restore all registers
- ;
- ;*****************************************************************************
- rest_regs proc near
-
- pop ax ; Get return address
- mov bp,sp ; Get a pointer into the stack
- xchg ax,[bp + 16] ; Swap retaddr with the saved BP value
- mov bp,ax ; This restores BP
- restore
- ret ; Return to caller
-
- rest_regs endp
-
-
- ;*****************************************************************************
- ;
- ; Read sectors from disk.
- ;
- ; Input:
- ; DH Head number
- ; CH Track number
- ; CL Starting sector number
- ; AL Number of sectors to read
- ; ES:BX Address of buffer
- ;
- ; Output:
- ; AH Status returned by BIOS
- ; CF Error indicator
- ;
- ; Registers:
- ; BP preserved, all others trashed.
- ;
- ;*****************************************************************************
- sect_read proc near
-
- save si, bp
- mov bp,3 ; Retry 3 times
- mov si,ax ; Save # of sectors
- sctr_l1:
- mov ah,2 ; BIOS function to read
- mov dl,$OUR_DRIVE ; Drive to read from
- int 13h ; Call the BIOS to do the work
- jnc sctr_exit ; If it went, exit now
- dec bp ; That was 1 try
- jz sctr_exit ; If that was the last, exit
- mov ah,0 ; Do a reset
- int 13h ;
- jc sctr_exit ; If that fails, we got problems...
- mov ax,si ; Get # to read again
- jmp short sctr_l1 ; Try again
- sctr_exit:
- restore
- ret ; Return to caller
-
- sect_read endp
-
-
- ;*****************************************************************************
- ;
- ; Verify sectors.
- ;
- ; Input:
- ; DH Head number
- ; CH Track number
- ; CL Starting sector number
- ; AL Number of sectors to verify
- ;
- ; Output:
- ; AH Status returned by BIOS
- ; CF Error indicator
- ;
- ; Registers:
- ; BP preserved, all others trashed.
- ;
- ;*****************************************************************************
- sect_verify proc near
-
- save si, bp
- mov bp,3 ; Retry 3 times
- mov si,ax ; Save # of sectors
- sctv_l1:
- mov ah,4 ; BIOS function to verify
- mov dl,$OUR_DRIVE ; Drive to read from
- int 13h ; Call the BIOS to do the work
- jnc sctv_exit ; If it went, exit now
- dec bp ; That was 1 try
- jz sctv_exit ; If that was the last, exit
- mov ah,0 ; Do a reset
- int 13h ;
- jc sctv_exit ; If that fails, we got problems...
- mov ax,si ; Get # to read again
- jmp short sctv_l1 ; Try again
- sctv_exit:
- restore
- ret ; Return to caller
-
- sect_verify endp
-
-
- ;*****************************************************************************
- ;
- ; Write sectors to disk.
- ;
- ; Input:
- ; DH Head number
- ; CH Track number
- ; CL Starting sector number
- ; AL Number of sectors to write
- ; ES:BX Address of buffer
- ;
- ; Output:
- ; AH Status returned by BIOS
- ; CF Error indicator
- ;
- ; Registers:
- ; BP preserved, all others trashed.
- ;
- ;*****************************************************************************
- sect_write proc near
-
- save si,bp
- mov bp,3 ; Retry 3 times
- mov si,ax ; Save # to write
- sctw_l1:
- mov ah,3 ; BIOS function to write
- mov dl,$OUR_DRIVE ; Drive to write to
- int 13h ; Call the BIOS to do the work
- jnc sctw_exit ; If it went, exit now
- dec bp ; That was 1 try
- jz sctw_exit ; If that was the last, exit
- mov ah,0 ; Do a reset
- int 13h ;
- jc sctw_exit ; If that fails, we got problems...
- mov ax,si ; Get # to write
- jmp short sctw_l1 ; Try again
- sctw_exit:
- restore
- ret ; Return to caller
-
- sect_write endp
-
-
- ;*****************************************************************************
- ;
- ; Set the diskette parameters for 10 sectors per track
- ;
- ; Input:
- ; None.
- ;
- ; Output:
- ; An internal copy of the parameters is made, the address at the vector
- ; for int 1eh is saved, and the vector is set to point to our copy.
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- set_disk_parms proc near
-
- assume ds:vhard_seg, es:nothing
-
- save cx, si, di, es
-
- push ds ; Save our segment, too
- sub si,si ; Set it to segment for int vectors
- mov ds,si ;
- lds si,ds:[1eh * 4] ; Point to the current disk parms
- mov cs:old_pptr[0],si ; Save it for restore later
- mov cs:old_pptr[2],ds ;
- push cs ; Set ES to our segment
- pop es
-
- assume ds:nothing, es:vhard_seg
-
- mov di,offset diskparms ; Point to our copy of the parameters
- mov cx,11 ; Only need 11 bytes
- cld ; Make sure of direction
- rep movsb ; Copy them
- pop ds ; Get our segment back
-
- assume ds:vhard_seg
-
- mov sect_per_track,10 ; Set that aright
- mov gap_len,14h
- mov fmt_gap_len,20h
- sub cx,cx ; Point back to the vectors
- mov es,cx ;
- mov word ptr es:[1eh * 4],offset diskparms
- mov es:[(1eh * 4) + 2],cs ; Set pointer to new parms
-
- restore
- ret ; Return to caller
-
- set_disk_parms endp
-
-
- ;*****************************************************************************
- ;
- ; Restore the diskette parameters
- ;
- ; Input:
- ; None.
- ;
- ; Output:
- ; The vector for int 1eh is restored.
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- rest_disk_parms proc near
-
- assume ds:vhard_seg, es:nothing
-
- save ax, es
- sub ax,ax ; Get segment for int vectors
- mov es,ax ;
- mov ax,old_pptr[0] ; Restore the old parm pointer
- mov es:[1eh * 4],ax ;
- mov ax,old_pptr[2] ;
- mov es:[(1eh * 4) + 2],ax ;
- restore
- ret ; Return to caller
-
- rest_disk_parms endp
-
-
- ;*****************************************************************************
- ;
- ; Translate a BIOS error code to a status code according to the table at BX.
- ;
- ; Input:
- ; AH BIOS return code
- ; DS:BX Pointer to translation table
- ;
- ; Output:
- ; AL Corresponding status code
- ;
- ; Registers:
- ; AX trashed; all others preserved
- ;
- ;*****************************************************************************
- xlat_status proc near
-
- assume ds:vhard_seg
-
- save bx, cx
- mov cx,10 ; Check for 10 BIOS codes
- xlts_l1:
- cmp ah,[bx] ; Is it this one?
- je xlts_l2 ; Yep - return the status code
- inc bx ; Point to next one
- inc bx
- loop xlts_l1 ;
- mov al,STS_BAD_ERROR ; Unknown error code
- jmp short xlts_exit
- xlts_l2:
- mov al,1[bx] ; Get the corresponding status code
- xlts_exit:
- restore
- ret ; Return to caller
-
- xlat_status endp
-
-
- ;*****************************************************************************
- ;
- ; Calculate the disk, track, head, and sector for a given logical sector.
- ;
- ; Input:
- ; AX DOS' logical sector
- ;
- ; Output:
- ; AX Disk number
- ; DH Head number
- ; CH Track number
- ; CL Sector number
- ;
- ; Registers:
- ; All register not used for parameters are preserved.
- ;
- ;*****************************************************************************
- calc_disk proc near
-
- save bx, si
- mov bx,799 ; Divide by # of sectors/disk
- sub dx,dx ; to get AX = disk #,
- div bx ; DX = logical sector on that disk
- mov si,ax ; Save the disk #
- mov ax,dx ; Get the logical sector on the disk
- inc ax ; Allow for boot sector
- mov bx,10 ; Divide by # of sectors/track
- sub dx,dx ; to get AX = track,
- div bx ; DX = logical sector on the track
- mov cl,dl ; Get the sector
- inc cl ; Since sectors start at 1...
- shr ax,1 ; Figure out the cylinder
- rcl dh,1 ; Set DH to the head
- mov ch,al ; Return the track
- mov ax,si ; Get the disk # back
- restore
- ret ; Return to caller
-
- calc_disk endp
-
-
- ;*****************************************************************************
- ;
- ; Read in the boot sector, checking to see if the ID matches our current ID.
- ;
- ; Input:
- ; curr_disk_id the ID we're expecting.
- ;
- ; Output:
- ; CF = 1 if the diskette can't be read
- ; ZF = 1 if the disk IDs match
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- check_boot_sect proc near
-
- assume ds:vhard_seg, es:vhard_seg
-
- save ax, bx, cx, dx, si, di, es
-
- push cs ; Make sure ES is right
- pop es
-
- assume es:vhard_seg
-
- call read_boot_sect ; Try to read the boot sector
- jc ckbs_exit ; Return w/error flag set if problem
- mov si,offset boot_sect_buf.BS_disk_id
- mov di,offset curr_disk_id
- mov cx,8 ; Check 8 bytes
- rep cmpsb ;
- clc ; Clear error flag
- ckbs_exit:
- restore
- ret ; Return to caller
-
- check_boot_sect endp
-
-
- ;*****************************************************************************
- ;
- ; Read the boot sector of the diskette in drive A: into the buffer at
- ; boot_sect_buf.
- ;
- ; Input:
- ; None.
- ;
- ; Output:
- ; CF = 0 Success.
- ; = 1 Unable to read the diskette.
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- read_boot_sect proc near
-
- assume ds:vhard_seg, es:nothing
-
- save ax, bx, cx, dx, es
- mov dh,0 ; Need to read Track 0, Head 0, Sect 1
- mov cx,1 ;
- mov al,1 ; Just 1 sector
- push cs ; Into our buffer
- pop es ;
-
- assume es:vhard_seg
-
- mov bx,offset boot_sect_buf ;
- call sect_read ; Read it
- restore ;
-
- assume es:nothing
-
- ret ; Return to caller
-
- read_boot_sect endp
-
-
- ;*****************************************************************************
- ;
- ; Save the last line of the screen, display a prompt there, and wait for a
- ; keystroke.
- ;
- ; Input:
- ; DS:SI pointer to prompt to display
- ;
- ; Output:
- ; CF = 1 Esc was pressed
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- prompt_user proc near
-
- assume ds:vhard_seg, es:nothing
-
- save ax, bx, cx, dx, si, es
- mov dh,24 ; Assume no EGA/VGA
- mov ax,40h ; Get at the number of lines on screen
- mov es,ax ;
-
- assume es:BIOS_seg
-
- cmp have_evga,0 ; Do we have an EGA/VGA on board?
- je prpu_l1 ; No - use line 24
- mov dh,es:[crt_lines] ; Get last line on screen
- prpu_l1:
- mov bh,es:[active_page] ; Get the current video page
- push cs ; Set ES to our segment
- pop es ;
-
- assume es:vhard_seg
-
- push dx ; Save line to use
- mov ah,3 ; Get the current cursor position
- int 10h ;
- mov last_curs,dx ; Save it
- pop dx ;
- mov dl,0 ; Start at the beginning of the line
- mov bl,4fh ; Use hi-white on red
- mov cx,1 ; Only doing 1 char at a time
- mov di,offset screen_save ; Point to save area
- prpu_l2:
- lodsb ; Get a character to display
- or al,al ; Done yet?
- jz prpu_l3 ; Yep - exit
- push ax ; Save it for a sec
- mov ah,2 ; Set the cursor position
- int 10h ;
- mov ah,8 ; Read the char/attr there
- int 10h ;
- stosw ; Save it
- pop ax ; Get the new one back
- mov ah,9 ; Write that character
- int 10h ;
- inc dl ; Next column
- jmp short prpu_l2 ; Loop back for more
- prpu_l3:
- mov ah,1 ; See if there are any keys
- int 16h ;
- jz prpu_l31 ; No - wait for one
- mov ah,0 ; Read the key
- int 16h ;
- jmp short prpu_l3 ; Loop until buffer is empty
- prpu_l31:
- sub ah,ah ; Read a key
- int 16h ;
- push ax ; Save the key read
- mov si,offset screen_save ; Point back to the save area
- mov dl,0 ; Start back at the beginning of line
- prpu_l4:
- cmp si,di ; Reached the end yet?
- je prpu_l5 ; Nope - keep restoring
- lodsw ; Get char/attr
- mov bl,ah ; Get the attribute for the write
- push ax ; Save the character to write
- mov ah,2 ; Set the cursor
- int 10h ;
- pop ax ; Get the character back
- mov ah,9 ; Write it out now
- int 10h
- inc dl ; Next column
- jmp short prpu_l4
- prpu_l5:
- mov ah,2 ; Set cursor back to last position
- mov dx,last_curs ;
- int 10h ;
- pop ax ; Get keystroke back
- cmp ax,11bh ; Escape key?
- je prpu_exit ; Yep - return w/CF=1
- stc ; Return CF=0
- prpu_exit:
- cmc ; Make CF the way we want
- restore
- ret ; Return to caller
-
- prompt_user endp
-
-
- ;*****************************************************************************
- ;
- ; Prompt for a specific disk, waiting for a key.
- ;
- ; Input:
- ; curr_disk_id ID of the disk set
- ; curr_disk_num disk number to prompt for
- ;
- ; Output:
- ; CF = 1 The user pressed Esc. This indicates that they
- ; want an error returned to DOS.
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- prompt_for_disk proc near
-
- assume ds:vhard_seg, es:nothing
-
- save ax, cx, si, di, es
- push cs ; Make sure ES is right
- pop es ;
-
- assume es:vhard_seg
-
- mov si,offset curr_disk_id ; Point to the ID to ask for
- mov di,offset pr_disk_id ; Point to where to put it
- mov cx,4 ; Copy 8 bytes (4 words)
- rep movsw ;
- mov al,curr_disk_num ; Get the number to ask for
- aam ; Split it up
- or ax,3030h ; Make it 2 ASCII digits
- xchg al,ah ; Put 'em in the right order
- mov word ptr pr_disk_num,ax ; Store 'em in the string
- mov si,offset need_disk ; Point to the prompt string
- call prompt_user ; Prompt them for it
- restore
- ret ; Return to caller
-
- prompt_for_disk endp
-
-
- ;*****************************************************************************
- ;
- ; Prompt for the first disk of a set, and make sure it's loaded.
- ;
- ; Input:
- ; None.
- ;
- ; Output:
- ; CF = 0 Disk is successfully loaded
- ;
- ; Reserved:
- ; All preserved.
- ;
- ;*****************************************************************************
- get_disk_0 proc near
-
- save ax, bx, cx, si, di, bp, es
- call read_boot_sect ; Load the boot sector
- jnc gtd0_l2 ; If we got it, continue
- gtd0_l1:
- mov si,offset need_first_disk; Prompt for a disk
- call prompt_user ;
- jc gtd0_exit ; Exit if Esc pressed
- call read_boot_sect ; Read the boot sector
- jc gtd0_l1 ; Loop back if unable
- gtd0_l2:
- cmp boot_sect_buf.BS_disk_num,0 ; Is it disk 0?
- jne gtd0_l1 ; No - try again
- mov si,offset boot_sect_buf.BS_disk_id ; Copy disk ID
- mov di,offset curr_disk_id ;
- mov cx,4 ;
- push cs ;
- pop es ;
- rep movsw ;
- mov curr_disk_num,0 ; Set the disk to 0
- test cache_flags,CACHE_INSTALLED ; Is the cache there?
- jz gtd0_l3 ; No - just get the ID
- test cache_flags,CACHE_VALID ; Is it already valid?
- jnz gtd0_l3 ; Yep - just get the ID
- mov bp,offset sect_read ; Routine to use for the I/O
- mov ax,1 ; Start at sector 1
- mov cx,24 ; Read 24 sectors
- les bx,cache_ptr ; Point to the cache
- call io_operation ; Read 'em
- jc gtd0_exit ; If error, return it
- or cache_flags,CACHE_VALID ; It's got valid data now
- gtd0_l3:
- clc ; Indicate success
- gtd0_exit:
- restore
- ret ; Return to caller
-
- get_disk_0 endp
-
-
-
- ;*****************************************************************************
- ;
- ; Prompt for a disk, and make sure it's loaded
- ;
- ; Input:
- ; AL Number of disk to prompt for
- ;
- ; Output:
- ; CF = 0 Disk is successfully loaded
- ;
- ; Registers:
- ; All preserved.
- ;
- ;*****************************************************************************
- get_num_disk proc near
-
- cmp curr_disk_num,0ffh ; Do we have one yet?
- mov curr_disk_num,al ;
- jne gtnd_l1 ; Yes - never mind
- call get_disk_0 ; Get disk 0 to get a disk ID
- jc gtnd_exit ; Exit if unable
- or al,al ; Do we want disk 0?
- jz gtnd_l2 ; Yep - don't bother reading again
- gtnd_l1:
- call check_boot_sect ; Check the boot sector
- jc gtnd_exit ; Exit if error
- jnz gtnd_l11 ; If IDs don't match, prompt for it
- cmp al,boot_sect_buf.BS_disk_num ; Right number?
- je gtnd_l2 ; Yep - exit w/CF clear
- gtnd_l11:
- call prompt_for_disk ; Prompt for the disk
- jc gtnd_exit ; Exit if Esc was pressed
- jmp short gtnd_l1 ; Loop back now
- gtnd_l2:
- clc ; Clear error flag
- gtnd_exit:
- ret ; Return to caller
-
- get_num_disk endp
-
-
- $drv_end label byte
-
-
- subttl INIT routines for both drivers
- page
-
- ;*****************************************************************************
- ;
- ; INIT routine for VHARD itself
- ;
- ;*****************************************************************************
- drv_init proc near
-
- assume ds:vhard_seg, es:nothing
-
- mov es:[si].DDir_media_id,1 ; Just 1 unit
- mov word ptr es:[si].DDir_buffer,offset $drv_end
- mov word ptr es:[si].DDir_buffer[2],cs
- mov word ptr es:[si].DDir_count,offset our_bpb_array
- mov word ptr es:[si].DDir_start,cs
- mov ah,30h ; Get the DOS version
- int 21h ;
- xchg al,ah ;
- mov dos_ver,ax ; Save it
- cmp ax,30ah ; At least 3.10?
- jb drvi_l0 ; Nope - can't get the drive
- mov al,byte ptr es:22[si] ; Get the drive assigned
- mov our_drive,al ; Save it
- drvi_l0:
- mov ax,1a00h ; See if there's a VGA on board
- mov bx,0a5a5h ;
- int 10h ;
- cmp bx,0a5a5h ; Did it change?
- jne drvi_l1 ; Yep - say that we have EGA/VGA
- mov ah,12h ; See if we have an EGA
- mov bx,0ff10h ;
- int 10h ;
- cmp bh,0ffh ; Did it change?
- je drvi_exit ; No - no EGA or VGA
- drvi_l1:
- or have_evga,1 ; Set the flag
- drvi_exit:
- mov dx,offset banner ; Display the program banner
- mov ah,9 ;
- int 21h ;
- ret ; Return to dispatcher
-
- drv_init endp
-
-
- banner db 13, 10, 'VHARD virtual disk driver v'
- % db VHARD_text_ver
- db ' - Public Domain Software', 13, 10, '$'
-
-
- du_init proc near
-
- mov word ptr es:[si][0].DDir_buffer,offset $drv_end
- mov word ptr es:[si][2].DDir_buffer,cs
- ret
-
- du_init endp
-
-
- vhard_seg ends
-
- end
-