;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1988 - 1991
; *                      All Rights Reserved.
; */
; Check for existence of a VDisk header.  Check the beginning of the segment
; addressed by the INT 19 vector, and, if XMS is available to give us
; A20 toggling, check at the 1Mb boundary.  We don't do the latter check
; if we can't do the A20 switch.
;
; Return the size of the VDisk if found.
;

; Checking for a VDISK header at 1Mb is currently disabled.  This is because
; the XMS calls used to access the HMA will cause INT 15 memory to
; be claimed if there are currently no Himem or XMS users, and the MEM
; command shouldn't disturb the memory environment in that way.
; We rely on VDisk allocators to use the INT 19 approach to signal
; their memory usage.  According to Ray Duncan, this may not be
; entirely reliable, but this is probably better than adding the
; code necessary to do lots of INT 15 block moves to interrogate
; the extended memory arena.

CHECKXMS	equ	0		; set to non-zero to enable checking
					; of the HMA for VDisk headers.

.MODEL	SMALL
.CODE

	extrn	_XMM_Installed	:Near
	extrn	_XMM_QueryA20	:Near
	extrn	_XMM_EnableA20	:Near
	extrn	_XMM_DisableA20 :Near

;----------------------------------------------------------------------------
;
; following piece of code will be moved into a para boundary. And the para
; address posted in seg of int 19h vector. Offset of int 19h will point to
; VDint19. This is to protect HMA from apps which use VDISK header method
; to determine free extended memory.
;
; For more details read "power programming" column by Ray Duncan in the
; May 30 1989 issue of PC Magazine (pp 377-388) [USING EXTENDED MEMORY,PART 1]
;
;----------------------------------------------------------------------------
;
StartVDHead	label	byte
;
;-------------- what follows is a dummy device driver header (not used by DOS)
;
		dd	0		; link to next device driver
		dw	8000h		; device attribute
		dw	0		; strategy routine offset
		dw	0		; interrupt routine offset
		db	1		; number of units
		db	7 dup(0)	; reserved area
VDiskSig1	db	'VDISK'

VLEN1		equ	($-offset VDiskSig1)

		db	'  V3.3'	; vdisk label
		db	15 dup (0)	; pad
VDiskEnd1	dw	0		; bits 0-15 of free HMA
		db	11h		; bits 16-23 of free HMA (1M + 64K)

VDInt19:
		db	0eah		; jmp to old vector
OldVDInt19	dd	?		; Saved int 19 vector

EndVDHead	label	byte
;
;
VDiskHMAHead	db	0,0,0		; non-bootable disk
VDiskSig2	db	'VDISK'

VLEN2		equ	($-offset VDiskSig2)

		db	'3.3'		; OEM - signature
		dw	128		; number of bytes/sector
		db	1		; sectors/cluster
		dw	1		; reserved sectors
		db	1		; number of FAT copies
		dw	64		; number of root dir entries
		dw	512		; number of sectors
		db	0feh		; media descriptor
		dw	6		; number of sectors/FAT
		dw	8		; sectors per track
		dw	1		; number of heads
		dw	0		; number of hodden sectors
VDiskEnd2	dw	440h		; Start of free HMA in K (1M+64K)
EndVDiskHMAHead	label	byte
;
;
;----------------------------------------------------------------------------
;
; procedure : IsVDiskInstalled
;
;		Checks for the presence of VDISK header at 1MB boundary
;		& INT 19 vector.  Returns number of Kb used as Vdisk
;
; Inputs  : none
; Outputs : AX = size of VDisk in Kb, 0 if none found
; Uses	  : AX, CX
;
;----------------------------------------------------------------------------
;
		public _CheckVDisk

_CheckVDisk	proc   near
		push	bp
		push	si		; Save regs
		push	di
		push	es
		push	ds

		mov	ax,3519h	; Get Int Vector 19h
		int	21h
					; set registers for CMPS
		mov	di, offset VDiskSig1 - offset StartVDHead
		mov	cx, VLEN1
		push	cs
		pop	ds
		mov	si, offset VDiskSig1
		rep	cmpsb
IF NOT CHECKXMS
		jnz	cvd_NoDisk
ELSE
		jnz	cvd_checkXMS	; jump if we didn't find it
ENDIF

;
; Get the first free address in Kb, and determine the number of Kb used
; above 1Mb.  First free address in a 24-bit address, so divide by 1024
; to get number of Kb
;
		mov	di,offset VDiskEnd1 - offset StartVDHead
		mov	ax,es:[di]+1	; load top 16 bits of end address
		shr	ax,1
		shr	ax,1		; fast divide of 24 bits by 1024
		test	es:[di],03FFh	; check for rounding
		jz	@F
		inc	ax		; round up if needed
@@:
IF NOT CHECKXMS
		jmp	short cvd_End

ELSE
		pop	ds		; clear top of stack
		jmp	short cvd_End	; AX now has size in Kb


;
; Ensure that A20 is on before we check above 1Mb.  If XMS is not
; installed, we punt, and assume no VDisk
;

cvd_checkXMS:
		pop	ds		; get DS again
		call	_XMM_Installed
		or	ax,ax
		jz	cvd_NoDisk	; No XMS, assume no VDisk

;
; Get and save current A20 state, get A20 on
;
		call	_XMM_QueryA20
		push	ax		; save current state
		or	ax,ax		; already on?
		jnz	cvd_A20On	; yes, don't turn it on
		call	_XMM_EnableA20	; turn it on

cvd_A20On:
		push	ds		; save DS again
		mov	ax, 0ffffh
		mov	ds, ax
		mov	si, 10h+(offset VDiskSig2 - offset VDiskHMAHead)
		mov	ax,cs
		mov	es,ax
		mov	di, offset VDiskSig2
		mov	cx, VLEN2
		rep	cmpsb
		jne	@F		; if no header, turn off A20 now
					; get first free address in Kb
		mov	si,offset VDiskEnd2 - offset VDiskHMAHead
		mov	ax,[si]
@@:
		pop	ds		; get original DS again
		pop	ax		; get original A20 state
		pushf			; save result of header check
		or	ax,ax		; was A20 already on?
		jnz	@F		; jump if yes
		call	_XMM_DisableA20 ; else turn it off again

@@:
		popf			; get result of header check
		je	cvd_End 	; jump if present

ENDIF		; CHECKXMS

cvd_NoDisk:
		mov	ax,1024 	; set up to return 0

cvd_End:
		sub	ax,1024 	; discount first 1Mb from first
					; free address to get size in Kb
IF NOT CHECKXMS
		pop	ds
ENDIF
		pop	es
		pop	di
		pop	si
		pop	bp
		ret
_CheckVDisk endp

		end
