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