
	title	Manage the VHARD FAT cache
	subttl	Prologue
	page	60,132

comment {

******************************************************************************

File VHCACHE.ASM

Author:
	Aaron L. Brenner

	BIX mail address albrenner
	GEnie address A.BRENNER

	This program is hereby released to the public domain.

Purpose:
	Provide a command-driven interface to the VHARD FAT/root dir cache.

Command syntax:
	VHCACHE [? | ON | OFF | MANUAL | AUTO | FLUSH]

Parameters:
	? or INFO	Display information about the cache.
	ON		Enable the cache.
	OFF		Disable and remove the cache.
	MANUAL		Disable auto-flush.
	AUTO		Enable auto-flush.
	FLUSH		Write the cache to disk.

	If no parameters, a brief usage message is displayed.

ERRORLEVEL returned:
	0		The requested operation was performed successfully.
	1		An error was reported by the driver.
	2		Command syntax error.
	3		VHARD.SYS not installed.
	4		Invalid DOS version
	5		Insufficient free memory

Notes:
	All commands (including "?") require that VHARD.SYS be installed.

Revision history:
1.00	07/20/90	ALB	Created.

******************************************************************************

endcomment {

	subttl	Included files
	page

	include	dd_defs.inc
	include	vhard.inc

	subttl	Program stack and data
	page

vhcache_stack	segment	stack

	dw	128 dup (0)

vhcache_stack	ends


vhcache_data	segment

assume	ds:vhcache_data

ax_value	dw	4c00h
dx_value	dw	0

vhctl_handle	dw	0
vhctl_name	db	'VHARDCTL',0

vhard_drive	db	0ffh		; Drive assigned (FF = unavailable)
vhard_ver	dw	0		; BCD version of driver
vhard_BPB	DOS_BPB <>		; BPB for the driver (we ignore it)

cache_flags	db	0		; Flags for the cache if installed
cache_addr	dw	0, 0		; Address of current cache

cmd_block	VH_CMD <>

cmd_keyword	db	8 dup (0)	; The keyword on the command line

vhversion	db	'VHARD version is $'
vhdrive_is	db	'VHARD is using drive letter $'
cached_at	db	'Currently installed cache is at $'
needs_flushing	db	'Cache needs to be FLUSHed', 13, 10, '$'
auto_enabled	db	'AUTOflush is enabled', 13, 10, '$'

;
; Error messages
;
no_share	db	'SHARE.EXE must be run before AUTOflush can be'
		db	' enabled.', 13, 10, '$'
bad_dos_ver	db	'Invalid DOS version for AUTOflush.', 13, 10, '$'
no_memory	db	'Not enough memory', 13, 10, '$'
no_cache	db	'The cache is not yet installed', 13, 10, '$'
unk_err		db	'unknown error$'
em1		db	'drive not read$'
em2		db	'seek error$'
em3		db	'general failure$'
em4		db	'CRC error$'
em5		db	'DMA boundary error$'
em6		db	'DMA overrun$'
em7		db	'sector not found$'
em8		db	'diskette is write-protected$'
em9		db	'address mark not found$'
em10		db	'unknown BIOS command$'
em11		db	'unknown VHARD command$'
em12		db	'cache already installed$'
em13		db	'cache not installed$'
em14		db	'cache must be flushed$'

err_prefix	db	'Error: $'
crlf_str	db	13, 10, '$'

error_messages	dw	unk_err
		dw	em1
		dw	em2
		dw	em3
		dw	em4
		dw	em5
		dw	em6
		dw	em7
		dw	em8
		dw	em9
		dw	em10
		dw	em11
		dw	em12
		dw	em13
		dw	em14


;
; Keywords we recognize
;
kw1		db	1,'?'
kw11		db	4,'INFO'
kw2		db	2,'ON'
kw3		db	3,'OFF'
kw4		db	6,'MANUAL'
kw5		db	4,'AUTO'
kw6		db	5,'FLUSH'

keywords	dw	kw1, kw_info
		dw	kw11, kw_info
		dw	kw2, kw_on
		dw	kw3, kw_off
		dw	kw4, kw_manual
		dw	kw5, kw_auto
		dw	kw6, kw_flush
		dw	0

usage_msg	db	'VHCACHE v1.00 - Public Domain Software', 13, 10
		db	13, 10, 'Usage: VHPREP [? | ON | OFF | MANUAL | AUTO'
		db	' | FLUSH]', 13, 10
		db	13, 10, '? or INFO     Display information on the cache'
		db	13, 10, 'ON            Enable the cache'
		db	13, 10, 'OFF           Disable and remove the cache'
		db	13, 10, 'MANUAL        Disable auto-flush'
		db	13, 10, 'AUTO          Enable auto-flush'
		db	13, 10, 'FLUSH         Write the cache to disk'
		db	13, 10, '$'

bad_kw		db	'Unknown keyword: "$'
bad_kw2		db	'".', 13, 10, '$'
no_driver	db	'VHARD.SYS must be installed', 13, 10, '$'

vhcache_data	ends


	subttl	Start of program code
	page	

vhcache_code	segment

assume	cs:vhcache_code, ds:nothing, es:nothing, ss:vhcache_stack


start	proc

	call	initialize		; Do program initialization

assume	ds:vhcache_data

	call	do_command		; Do the command they want
	mov	ax,ax_value		;
	mov	dx,dx_value		;
	int	21h			;

start	endp


;*****************************************************************************
;
; Perform once-only program initialization.
;
;*****************************************************************************
initialize	proc	near

	mov	ax,vhcache_data		; Get our data segment
	mov	es,ax			;

assume	es:vhcache_data

	mov	si,81h			; Point to the command tail
	cld				; Make sure of direction
init_l1:
	lodsb				; Get a character
	cmp	al,' '			; Is it a blank?
	je	init_l1			; Yep - ignore it
	cmp	al,9			; Tab?
	je	init_l1			; Yes - ignore it
	cmp	al,0dh			; End of the line?
	jne	init_l2			; No - copy the keyword
	push	es			; Set DS aright
	pop	ds			;

assume	ds:vhcache_data

	mov	dx,offset usage_msg	; Display the usage message
	mov	ah,9			;
	int	21h			;
	mov	ax,4c02h		; Pretend it's an error
	int	21h			; Exit to DOS
init_l2:

assume	ds:nothing

	dec	si			; Point back to where we stopped
	mov	di,offset cmd_keyword[1]; Point to where we're copying to
	mov	cx,6			; Max we want to copy
init_l3:
	lodsb				; Get the next byte
	cmp	al,' '			; Done with the command?
	je	init_l5			; Yes - stop copying
	cmp	al,9			; Done with it?
	je	init_l5			; Yes - stop copying
	cmp	al,0dh			; End of the line?
	je	init_l5			; Yes - stop copying
	jcxz	init_l3			; If the name buffer's full, ignore
	cmp	al,'a'			; Is it lower case?
	jb	init_l4			; No - use it
	cmp	al,'z'			; Is it?
	ja	init_l4			; Nope - use it
	xor	al,20h			; Make it upper case
init_l4:
	stosb				; Put it in the name buffer
	inc	cmd_keyword[0]		; Count this character
	dec	cx			; Both ways
	jmp	short init_l3		; Loop back for more
init_l5:
	sub	al,al			; Terminate it, too
	stosb				;
	push	es			; Swap segments so we can release our
	push	ds			;  environment
	pop	es			;
	pop	ds			;

assume	ds:vhcache_data, es:nothing

	mov	es,es:[2ch]		; Get our environment segment
	mov	ah,49h			; Fn to release memory
	int	21h			;
	push	ds			; Lastly, set ES to our data seg
	pop	es			;

assume	es:vhcache_data

	mov	dx,offset vhctl_name	; Try to get to the VHARDCTL device
	mov	ax,3c02h		;
	int	21h			;
	jnc	init_l6			; If we could, make sure it's a device
init_err:
	mov	dx,offset no_driver	; Complain that the driver's missing
	mov	ah,9			;
	int	21h			;
	mov	ax,4c03h		; Exit with appropriate code
	int	21h			;
init_l6:
	mov	bx,ax			; Get the handle for it
	mov	vhctl_handle,ax		; Save it for later use, too
	mov	ax,4400h		; Get info on the handle
	int	21h			;
	test	dl,80h			; Is this a device?
	jz	init_err		; No - just a file, so complain
	mov	cmd_block.VC_cmd_code,CMD_GETDATA; Get driver info
	mov	word ptr cmd_block.VC_buffer[0],offset vhard_drive
	mov	word ptr cmd_block.VC_buffer[2],ds
	mov	dx,offset cmd_block	; Write it out to the driver
	mov	cx,size VH_CMD		;
	mov	ax,4403h		;
	int	21h			;
	ret				; Return to Main

initialize	endp


;*****************************************************************************
;
; Perform the command specified by cmd_keyword.
;
;*****************************************************************************
do_command	proc	near

	sub	bx,bx			; Start at the base of the table
	mov	cx,bx			;
docm_l1:
	mov	di,keywords[bx]		; Pick up a keyword pointer
	or	di,di			; Hit the end of the table?
	jnz	docm_l2			; No - see if they match
	mov	dx,offset bad_kw	; Report an unknown keyword
	mov	ah,9			;
	int	21h			;
	mov	dx,offset cmd_keyword[1]; Display the errant keyword
	mov	cl,cmd_keyword[0]	;
	sub	ch,ch			;
	mov	bx,1			;
	mov	ah,40h			;
	int	21h			;
	mov	dx,offset bad_kw2	; Finish out the message
	mov	ah,9			;
	int	21h			;
	mov	ax,4c02h		; Exit with error code
	int	21h			;
docm_l2:
	mov	si,offset cmd_keyword	; Point to the keyword they entered
	mov	cl,[si]			; Get the length of it
	inc	cl			; Allow for length byte
	rep	cmpsb			; Is this the one they entered?
	je	docm_l3			; Yep - call the routine
	add	bx,4			; Point to next table entry
	jmp	short docm_l1		; Loop back
docm_l3:
	call	keywords[2][bx]		; Call the appropriate routine
	ret				; Return to Main

do_command	endp

	subttl	Command keyword handlers
	page

;*****************************************************************************
;
; Command keyword handlers
;
;*****************************************************************************


;*****************************************************************************
;
; Handle the "?" or "INFO" keyword.
; Display information about the cache.
;
;*****************************************************************************
kw_info		proc	near

	mov	al,5			; Get the cache info
	mov	word ptr cmd_block.VC_buffer[0],offset cache_flags
	mov	word ptr cmd_block.VC_buffer[2],ds
	call	call_driver		; Get the info
	test	cache_flags,CACHE_INSTALLED	; Is the cache installed?
	jnz	kwin_l1			; Yep - display its info
	mov	dx,offset no_cache	; Tell 'em it ain't there
	mov	ah,9			;
	int	21h			;
	jmp	kwin_exit		; Exit now
kwin_l1:
	mov	dx,offset vhversion	; Say what version of VHARD we have
	mov	ah,9			;
	int	21h			;
	mov	ax,vhard_ver		; Get the version word
	push	ax			; Save it
	mov	al,ah			; Display major version
	call	disp_byte		;
	mov	dl,'.'			;
	mov	ah,6			;
	int	21h			;
	pop	ax			; Display minor version
	call	disp_byte		;
	mov	dx,offset crlf_str	; End the line
	mov	ah,9			;
	int	21h			;
	inc	vhard_drive		; Do we have the drive assigned
	jz	kwin_l2			; Nope - don't know what drive
	mov	dx,offset vhdrive_is	; Tell 'em the drive letter
	mov	ah,9			;
	int	21h			;
	mov	dl,vhard_drive		;
	add	dl,'@'			;
	mov	ah,6			;
	int	21h			;
	mov	dl,':'			;
	mov	ah,6			;
	int	21h			;
	mov	dx,offset crlf_str	; End this line
	mov	ah,9			;
	int	21h			;
kwin_l2:
	mov	dx,offset cached_at	; Tell 'em where the cache is now
	mov	ah,9			;
	int	21h			;
	mov	ax,cache_addr[2]	;
	call	disp_word		;
	mov	dl,':'			;
	mov	ah,6			;
	int	21h			;
	mov	ax,cache_addr[0]	;
	call	disp_word		;
	mov	dx,offset crlf_str	; End the line
	mov	ah,9			;
	int	21h			;
	test	cache_flags,CACHE_DIRTY	; Is the cache dirty?
	jz	kwin_l3			; No - just exit
	mov	dx,offset needs_flushing; Tell 'em it needs to be flushed
	mov	ah,9			;
	int	21h			;
kwin_l3:
	test	cache_flags,CACHE_AUTO	; AUTOflush enabled?
	jz	kwin_exit		; Nope - exit now
	mov	dx,offset auto_enabled	; Tell 'em about it
	mov	ah,9			;
	int	21h			;
kwin_exit:
	ret				; Return to caller

kw_info		endp


;*****************************************************************************
;
; Output the value in AX as 4 hex digits
;
;*****************************************************************************
disp_word	proc	near

	push	ax			; Save the value
	mov	al,ah			; Do the high byte first
	call	disp_byte		;
	pop	ax			; Now, the low byte
disp_byte:
	push	ax			; Save the low nybble
	mov	cl,4			; Move the high nybble down
	shr	al,cl			;
	call	disp_nybble		; Display the nybble
	pop	ax			; Get low nybble back
disp_nybble:
	and	al,0fh			; Keep the low nybble
	add	al,90h			; Convert to ASCII hex digit
	daa				;
	adc	al,40h			;
	daa				;
	mov	dl,al			; Send it to the display
	mov	ah,6			;
	int	21h			;
	ret				; Return to caller

disp_word	endp


;*****************************************************************************
;
; Handle the "ON" keyword
;
;*****************************************************************************
kw_on		proc	near

	mov	ah,51h			; Get our PSP
	int	21h			;
	mov	es,bx			;
	mov	bx,768			; Need 12K at least
	mov	ah,4ah			;
	int	21h			;
	jnc	kwon_l1			; If we got it, install
	mov	dx,offset no_memory	; Report insufficient memory
	mov	ah,9			;
	int	21h			;
	mov	byte ptr ax_value[0],5	; Exit code
	jmp	short kwon_exit		; Exit now
kwon_l1:
	mov	word ptr cmd_block.VC_buffer[0],0	; Set cache ptr
	mov	word ptr cmd_block.VC_buffer[2],es	;  to PSP:0
	mov	al,1			; Enable the cache
	call	call_driver		;
	jnc	kwon_l2			; If we did, continue
	call	report_error		; Report the error
	jmp	short kwon_exit		; Exit now
kwon_l2:
	mov	byte ptr ax_value[1],31h; Change exit fn to TSR exit
	mov	dx_value,768		; Set DX to # of paragraphs needed
	mov	ah,30h			; Get our DOS version
	int	21h			;
	cmp	al,3			; At least 3.x?
	jb	kwon_exit		; Nope - no auto-flush available
	mov	ax,1000h		; See if SHARE is installed
	int	2fh			;
	or	al,al			; Is it?
	jz	kwon_exit		; Nope - no auto-flush
	mov	al,4			; Enable cache auto-flush
	call	call_driver		;
kwon_exit:
	ret				; Return to caller

kw_on		endp


;*****************************************************************************
;
; Handle the "OFF" keyword.
;
;*****************************************************************************
kw_off		proc	near

	mov	al,0			; Disable the cache
	call	call_driver		;
	jnc	kwof_l1			; If no error, release the memory
	call	report_error		; Report the error
	jmp	short kwof_exit		; Exit now
kwof_l1:
;
; VHARDCTL set our VC_buffer to point to the existing cache
;
	mov	es,word ptr cmd_block.VC_buffer[2]	; Get segment to free
	mov	ah,49h			; Function to release memory
	int	21h			; Get rid of the memory
kwof_exit:
	ret				; Return to caller

kw_off		endp


;*****************************************************************************
;
; Handle the "MANUAL" keyword
;
;*****************************************************************************
kw_manual	proc	near

	mov	al,3			; Disable auto-flush
	call	call_driver		;
	jnc	kwmn_exit		; Exit if no error
	call	report_error		; Report the error
kwmn_exit:
	ret				; Return to caller

kw_manual	endp


;*****************************************************************************
;
; Handle the "AUTO" keyword
;
;*****************************************************************************
kw_auto		proc	near

	mov	ah,30h			; Get DOS version
	int	21h			;
	cmp	al,3			; Is it at least 3.x?
	jb	kwau_err1		; Nope - can't do autoflush
	mov	ax,1000h		; See if SHARE is installed
	int	2fh			;
	cmp	al,0			; Is it?
	jz	kwau_err2		; Nope - report error
	mov	al,4			; Enable auto-flush
	call	call_driver		;
	jnc	kwau_exit		; Exit if it went
	call	report_error		; Report an error
kwau_exit:
	ret				; Return to caller
;
kwau_err1:
	mov	dx,offset bad_dos_ver	;
	jmp	short kwau_err3		;
kwau_err2:
	mov	dx,offset no_share	;
kwau_err3:
	mov	ah,9			; Display the error message
	int	21h			;
	mov	ax,4c04h		; Exit with appropriate error code
	int	21h			;

kw_auto		endp


;*****************************************************************************
;
; Handle the "FLUSH" keyword
;
;*****************************************************************************
kw_flush	proc	near

	mov	al,2			; Send the "Flush Cache" command
	call	call_driver		;
	jnc	kwfl_exit		; Exit if it went
	mov	byte ptr ax_value[0],1	; Set exit code
	call	report_error		; Report the error
kwfl_exit:
	ret				; Return to caller

kw_flush	endp


;*****************************************************************************
;
; Call VHARDCTL.
;
; Call with command code in AL.
;
; Returns with CF = 1 if VC_status <> STS_OK
;
;*****************************************************************************
call_driver	proc	near

	mov	cmd_block.VC_cmd_code,CMD_CACHE	; It's a cache command
	mov	cmd_block.VC_track,al		; Set the subcommand
	mov	cmd_block.VC_status,STS_OK	; Init to "OK" status
	mov	ax,4403h		; IOCTL write function
	mov	dx,offset cmd_block	; Point to the data to write
	mov	cx,size VH_CMD		; Number of bytes to write
	mov	bx,vhctl_handle		; Handle to write to
	int	21h			; Do it
	cmp	cmd_block.VC_status,STS_OK	; Did it fly?
	je	clld_ok			; Yes - return CF=0
	stc				; Return error flag
	jmp	short clld_exit		; Exit now
clld_ok:
	clc				; No error
clld_exit:
	ret				; Return to caller

call_driver	endp


;*****************************************************************************
;
; Common error-reporting routine
;
;*****************************************************************************
report_error	proc	near

assume	ds:vhcache_data

	mov	bl,cmd_block.VC_status	; Get the return status
	cmp	bl,STS_BAD_ERROR	; See if it was an unknown error
	jne	rpte_l1			; No - point to proper error message
	mov	si,offset unk_err	; Point to "Unknown error" message
	jmp	short rpte_l2		; Continue
rpte_l1:
	sub	bh,bh			; Make the error code an offset
	shl	bx,1			;
	mov	si,error_messages[bx]	; Point to the error message
rpte_l2:
	mov	dx,offset err_prefix	;
	mov	ah,9			;
	int	21h			;
	mov	dx,si			;
	mov	ah,9			;
	int	21h			;
	mov	dx,offset crlf_str	;
	mov	ah,9			;
	int	21h			;
	mov	byte ptr ax_value[0],1	; Set exit code
	ret				; Return to caller

report_error	endp


vhcache_code	ends

	end	start
