;*****************************************************************************
;*									     *
;*  HIMEM.ASM -						    Chip Anderson    *
;*									     *
;*	Extended Memory Specification Driver -				     *
;*	    Copyright 1988, Microsoft Corporation			     *
;*									     *
;*	The HMA was originally envisioned by Ralph Lipe.		     *
;*	Original XMS spec designed by Aaron Reynolds, Chip Anderson, and     *
;*	Tony Gosling.  Additional spec suggestions by Phil Barrett and David *
;*	Weise of Microsoft, Ed McNierney of Lotus and Bob Smith of Qualitas. *
;*									     *
;*	MoveExtMemory function written by Tony Gosling.			     *
;*									     *
;*	AT&T 6300 support added by Bill Hall, Olivetti ATC, Cupertino, CA    *
;*	HP Vectra support added by Ralph Peterson, Hewlett Packard Corp.     *
;*									     *
;*****************************************************************************

XMSVersion	equ	0200h
HimemVersion	equ	0206h

; Version -
;
;   1.00	- Written					4/17/88
;   1.01	- Added Global/Temporary Enable of A20		4/18/88
;   1.02	- Don't use DOS to change interrupts		4/19/88
;		- Return 1 for success in Disable cases		4/19/88
;		- Prevent disables if A20 enabled at start	4/19/88
;		- Added INT 2F handler check			4/19/88
;		- Used smarter hooking routine			4/19/88
;   1.03	- Optimized some code				4/20/88
;		- Temporarily Disable properly			4/20/88
;		- Disable INT's while changing vars		4/20/88
;   1.04	- Check to see if A20 works during init		4/23/88
;		- Fixed PS/2 support				4/23/88
;   1.05	- Added QueryExtMemory and AllocExtMemory	4/25/88
;   1.06	- Never remove the INT 15h hook once it is in	4/27/88
;		- Changed ExtMemory calls to provide "full" MM	4/27/88
;		- Force A20 line off on PS2's at init time	4/27/88
;   1.07	- Added Multiple A20 Handler support		5/01/88
;		- Added popff macro				5/01/88
;		- REP MOVSW words not bytes			5/04/88
;   1.08	- Made popff relocateable			5/04/88
;		- Added support for the AT&T 6300 Plus		5/05/88
;		- Added support for the HP Vectra		5/06/88
;   1.09	- Added CEMM recognition code			5/16/88
;		- Don't automatically trash BX in HMMControl	5/17/88
;   1.10	- Change PS/2 ID routine to use Watchdog timer	6/02/88
;		- Changed CEMM recognition string to just VDISK 6/07/88
;		- Fixed 2 instance bug				6/14/88
;   1.11	- Changed INT 2F multiplex number to 43h	6/23/88
;		- Fixed HP Vectra support for older Vectras	6/23/88
;		- Fixed Block Move register trashing bug	6/25/88
;   2.00	- Updated to XMS v2.00				7/10/88
;		- Reworked initialization code			7/12/88
;		- Reworked reentrancy issues			7/13/88
;		- Finialized A20 handling			7/14/88
;		- Added parameter support			7/14/88
;   2.01	- Official version with Vecta and 6300 support	7/15/88
;   2.02	- Removed INT 1 from MoveExtMemory		7/19/88
;		- Fixed minor problems in QueryExtMemory	7/19/88
;   2.03	- Added 386 Big Mode MoveBlock			8/04/88
;		- Added Compaq "Built-In" Memory support	8/05/88
;		- Fixed 64K-free Installation bug		8/05/88
;		- Change PS/2 detection code yet again		8/08/88
;   2.04	- Fixed "A20 On" Message bug (ugh)		8/09/88
;		- Fixed A20 Init testing code			8/09/88
;		- Cleaned up for publication			8/09/88
;   2.05	- Bug Fix: Subtract 64K from first EMB for HMA 11/30/88
;		- Bug Fix: Check for NUMHANDLES=0 case	       12/14/88
;   2.06	- Source code update				2/07/89
;		  Bug Fix: Restore 286 initialization code	3/21/89

	    name    Himem
	    title   'HIMEM.SYS - Microsoft XMS Device Driver'

;*--------------------------------------------------------------------------*
;*	EQUATES								    *
;*--------------------------------------------------------------------------*

DEFHANDLES		equ	32		; Default # of EMB handles
MAXHANDLES		equ	128		; Max # of EMB handles

cXMSFunctions		equ	12h		; = # of last function+1!

FREEFLAG		equ	00000001b	; EMB Flags
USEDFLAG		equ	00000010b
UNUSEDFLAG		equ	00000100b

; XMS Error Codes.
ERR_NOTIMPLEMENTED	equ	080h
ERR_VDISKFOUND		equ	081h
ERR_A20			equ	082h
ERR_GENERAL		equ	08Eh
ERR_UNRECOVERABLE	equ	08Fh

ERR_HMANOTEXIST		equ	090h
ERR_HMAINUSE		equ	091h
ERR_HMAMINSIZE		equ	092h
ERR_HMANOTALLOCED	equ	093h

ERR_OUTOMEMORY		equ	0A0h
ERR_OUTOHANDLES		equ	0A1h
ERR_INVALIDHANDLE	equ	0A2h
ERR_SHINVALID		equ	0A3h
ERR_SOINVALID		equ	0A4h
ERR_DHINVALID		equ	0A5h
ERR_DOINVALID		equ	0A6h
ERR_LENINVALID		equ	0A7h
ERR_OVERLAP		equ	0A8h
ERR_PARITY		equ	0A9h
ERR_EMBUNLOCKED		equ	0AAh
ERR_EMBLOCKED		equ	0ABh
ERR_LOCKOVERFLOW	equ	0ACh
ERR_LOCKFAIL		equ	0ADh

ERR_UMBSIZETOOBIG	equ	0B0h
ERR_NOUMBS		equ	0B1h
ERR_INVALIDUMB		equ	0B2h

.386p

; In order to address memory above 1 MB on the AT&T 6300 PLUS, it is
; necessary to use the special OS-MERGE hardware to activate lines
; A20 to A23.  However, these lines can be disabled only by resetting
; the processor.  The address to which to return after reset are placed
; at 40:A2, noted here as RealLoc1.

BiosSeg SEGMENT USE16 AT 40h	      ; Used to locate 6300 PLUS reset address

		org	00A2h
RealLoc1	dd	0

BiosSeg ends


; Macro to avoid the 286 POPF bug.  Performs a harmless IRET to simulate a
;   popf.  Some 286s allow interrupts to sneak in during a real popf.

popff	    macro
	    push    cs
	    call    pPPFIRet	; Defined as the offset of any IRET
	    endm

;*--------------------------------------------------------------------------*
;*	    SEGMENT DEFINITION						    *
;*--------------------------------------------------------------------------*

CODE	    SEGMENT PARA PUBLIC USE16 'CODE'

	    assume  cs:code,ds:code,es:code

	    org	    0

; The Driver Header definition.
Header		dd	-1	    ; Link to next driver, -1 = end of list
		dw	1010000000000000b
				    ; Device attributes, Non-IBM bit set
		dw	Strategy    ; "Stategy" entry point
		dw	Interrupt   ; "Interrupt" entry point
		db	'XMSXXXX0'  ; Device name


;****************************************************************************
;*									    *
;*  Data Structures and Global Variables -				    *
;*									    *
;****************************************************************************

; The driver Request Header structure definition.
ReqHdr struc
    ReqLen	db	?
    Unit	db	?
    Command	db	?
    Status	dw	?
    Reserved	db	8 dup (?)
    Units	db	?
    Address	dd	?
    pCmdLine	dd	?
ReqHdr ends

; An EMB Handle structure definition.
Handle struc			; Handle Table Entry
    Flags	db	?	; Unused/InUse/Free
    cLock	db	?	; Lock count
    Base	dw	?	; 32-bit base address in K
    Len		dw	?	; 32-bit length in K
Handle ends

; Extended Memory Move Block structure definition.
MoveExtendedStruc struc
    bCount	    dd	?	; Length of block to move
    SourceHandle    dw	?	; Handle for souce
    SourceOffset    dd	?	; Offset into source
    DestHandle	    dw	?	; Handle for destination
    DestOffset	    dd	?	; Offset into destination
MoveExtendedStruc ends

; The Global variables.
pPPFIRet	dw	PPFIRet ; The offset of an IRET for the POPFF macro
pReqHdr		dd	?	; Pointer to MSDOS Request Header structure
pInt15Vector	dw	15h*4,0 ; Pointer to the INT 15 Vector
PrevInt15	dd	0	; Original INT 15 Vector
PrevInt2F	dd	0	; Original INT 2F Vector
fHMAInUse	db	0	; High Memory Control Flag, != 0 -> In Use
fCanChangeA20	db	0	; A20 Enabled at start?
fHMAMayExist	db	0	; True if the HMA could exist at init time
fHMAExists	db	0	; True if the HMA exists
fVDISK		db	0	; True if a VDISK device was found
EnableCount	dw	0	; A20 Enable/Disable counter
fGlobalEnable	dw	0	; Global A20 Enable/Disable flag
KiddValley	dw	0	; The address of the handle table 
KiddValleyTop	dw	0	; Points to the end of the handle table
MinHMASize	dw	0	    ; /HMAMIN= parameter value
cHandles	dw	DEFHANDLES  ; # of handles to allocate

cImplementedFuncs db	cXMSFunctions-3	    ; Omit the UMB functions
					    ; and ReallocEMB

A20Handler	dw	0	; Offset of the A20 Handler

BIMBase 	dw	0	; Base address and Lenght of remaining Compaq
BIMLength	dw	0	;   Built-In Memory (set at Init time)

MemCorr		dw	0	; KB of memory at FA0000 on AT&T 6300 Plus.
				;      This is used to correct INT 15h,
				;      Function 88h return value.
OldStackSeg	dw	0	; Stack segment save area for 6300 Plus.
				;      Needed during processor reset.

;*--------------------------------------------------------------------------*
;*									    *
;*  Strategy -								    *
;*									    *
;*	Called by MS-DOS when ever the driver is accessed.		    *
;*									    *
;*  ARGS:   ES:BX = Address of Request Header				    *
;*  RETS:   Nothing							    *
;*  REGS:   Preserved							    *
;*									    *
;*--------------------------------------------------------------------------*

Strategy    proc    far

	    ; Save the address of the request header.
	    mov	    word ptr cs:[pReqHdr],bx
	    mov	    word ptr cs:[pReqHdr][2],es
	    ret

Strategy    endp


;*--------------------------------------------------------------------------*
;*									    *
;*  Interrupt -								    *
;*									    *
;*	Called by MS-DOS immediately after Strategy routine		    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   Return code in Request Header's Status field		    *
;*  REGS:   Preserved							    *
;*									    *
;*--------------------------------------------------------------------------*

Interrupt   proc    far

	    ; Save the registers including flags.
	    push    ax		    ; We cannot use pusha\popa because
	    push    bx		    ;	we could be on an 8086 at this point
	    push    cx
	    push    dx
	    push    ds
	    push    es
	    push    di
	    push    si
	    push    bp
	    pushf

	    ; Set DS=CS for access to global variables.
	    push    cs
	    pop	    ds

	    les	    di,[pReqHdr]	; ES:DI = Request Header

	    mov     bl,es:[di.Command]	; Get Function code in BL

	    or	    bl,bl		; Only Function 00h (Init) is legal
	    jz	    short IInit

	    cmp     bl,16		; Test for "legal" DOS functions
	    jle     short IOtherFunc

IBogusFunc: mov     ax,8003h		; Return "Unknown Command"
	    jmp     short IExit

IOtherFunc: xor     ax,ax		; Return zero for unsupported functions
	    jmp     short IExit

	    ; Initialize the driver.
IInit:	    call    InitDriver

	    les	    di,[pReqHdr]	; Restore ES:DI = Request Header

IExit:	    or	    ax,0100h		; Turn on the "Done" bit
	    mov	    es:[di.Status],ax	; Store return code

	    ; Restore the registers.
	    popff
	    pop	    bp
	    pop	    si
	    pop	    di
	    pop	    es
	    pop	    ds
	    pop	    dx
	    pop	    cx
	    pop	    bx
	    pop	    ax
	    ret

Interrupt   endp


;*--------------------------------------------------------------------------*
;*									    *
;*  Int2FHandler -							    *
;*									    *
;*	Hooks Function 43h, Subfunction 10h to return the		    *
;*	address of the High Memory Manager Control function.		    *
;*	Also returns 80h if Function 43h, Subfunction 0h is requested.	    *
;*									    *
;*  ARGS:   AH = Function, AL = Subfunction				    *
;*  RETS:   ES:BX = Address of XMMControl function (if AX=4310h)	    *
;*	    AL = 80h (if AX=4300)					    *
;*  REGS:   Preserved except for ES:BX (if AX=4310h)			    *
;*	    Preserved except for AL    (if AX=4300h)			    *
;*									    *
;*--------------------------------------------------------------------------*

Int2FHandler proc   far

	    sti				    ; Flush any queued interrupts

	    cmp	    ah,43h		    ; Function 43h?
	    jne     short I2FNextInt
	    or	    al,al		    ; Subfunction 0?
	    jne     short I2FNextSub	    ; No, continue

	    ; Indicate that an XMS driver is installed.
	    mov	    al,80h		    ; Return 80h in AL
PPFIRet:    iret			    ; Label sets up the POPFF macro

I2FNextSub: cmp	    al,10h		    ; Subfunction 10?
	    jne     short I2FNextInt	    ; No, goto next handler

	    ; Return the address of the XMS Control function in ES:BX.
	    push    cs
	    pop	    es
	    mov	    bx,offset XMMControl
	    iret

	    ; Continue down the Int 2F chain.
I2FNextInt: cli				    ; Disable interrupts again
	    jmp	    cs:[PrevInt2F]

Int2FHandler endp


;*--------------------------------------------------------------------------*
;*									    *
;*  ControlJumpTable -							    *
;*									    *
;*	Contains the address for each of the XMS Functions.		    *
;*									    *
;*--------------------------------------------------------------------------*

ControlJumpTable label word
	    dw	    Version		    ; Function 00h
	    dw	    RequestHMA		    ; Function 01h
	    dw	    ReleaseHMA		    ; Function 02h
	    dw	    GlobalEnableA20	    ; Function 03h
	    dw	    GlobalDisableA20	    ; Function 04h
	    dw	    LocalEnableA20	    ; Function 05h
	    dw	    LocalDisableA20	    ; Function 06h
	    dw	    IsA20On		    ; Function 07h
	    dw	    QueryExtMemory	    ; Function 08h
	    dw	    AllocExtMemory	    ; Function 09h
	    dw	    FreeExtMemory	    ; Function 0Ah
	    dw	    MoveBlock		    ; Function 0Bh
	    dw	    LockExtMemory	    ; Function 0Ch
	    dw	    UnlockExtMemory	    ; Function 0Dh
	    dw	    GetExtMemoryInfo	    ; Function 0Eh

	    ; We don't implement Realloc in this version.
;	    dw	    ReallocExtMemory	    ; Function 0Fh

	    ; We don't implement the UMB functions.
;	    dw	    RequestUMB		    ; Function 14
;	    dw	    ReleaseUMB		    ; Function 15


;*--------------------------------------------------------------------------*
;*									    *
;*  XMMControl -							    *
;*									    *
;*	Main Entry point for the Extended Memory Manager		    *
;*									    *
;*  ARGS:   AH = Function, AL = Optional parm				    *
;*  RETS:   AX = Function Success Code, BL = Optional Error Code	    *
;*  REGS:   AX, BX, DX and ES may not be preserved depending on function    *
;*									    *
;*  INTERNALLY REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

XMMControl  proc   far

	    jmp	    short XCControlEntry	; For "hookability"
	    nop					; NOTE: The jump must be a 
	    nop					;  short jump to indicate
	    nop					;  the end of any hook chain.
						;  The nop's allow a far jump
						;  to be patched in.
XCControlEntry:
	    ; Preserve the following registers.
	    push    cx
	    push    si
	    push    di
	    push    ds
	    push    es
	    pushf

	    ; Save DS in ES.
	    push    ds
	    pop	    es		    ; NOTE: ES cannot be used for parms!
	    
	    ; Set DS equal to CS.
	    push    cs
	    pop	    ds
	    
	    ; Preserve the current function number.
	    push    ax

	    ; Is this a call to "Get XMS Version"?
	    or	    ah,ah
	    jz	    short XCCallFunc	  ; Yes, don't hook INT 15h yet
	    
	    ; Is this a valid function number?
	    cmp	    ah,[cImplementedFuncs]
	    jb	    short XCCheckHook
	    pop	    ax		    ; No, Un-preserve AX and return an error
	    xor	    ax,ax
	    mov	    bl,ERR_NOTIMPLEMENTED
	    jmp	    short XCExit

	    ; Is INT 15h already hooked?
XCCheckHook:pushf
	    cli			    ; This is a critical section
	    
	    cmp	    word ptr [PrevInt15][2],0	; Is the segment non-zero?
	    jne     short XCCheckVD

	    ; Try to claim all remaining extended memory.
	    call    HookInt15

	    ; Was a VDISK device found?
XCCheckVD:  popff		    ; End of critical section
	    cmp	    [fVDISK],0
	    je	    short XCCallFunc
	    pop	    ax		    ; Yes, Un-preserve AX and return an error
	    xor	    ax,ax
	    mov	    bl,ERR_VDISKFOUND
	    xor	    dx,dx
	    jmp	    short XCExit

	    ; Call the appropriate API function.	    
XCCallFunc: pop	    ax		    ; Restore AX
	    mov	    al,ah
	    xor	    ah,ah
	    shl	    ax,1
	    mov	    di,ax	    ; NOTE: DI cannot be used for parms!
	    
	    call    word ptr [ControlJumpTable+di]

	    ; Restore the preserved registers.
XCExit:	    popff		    ; NOTE: Flags must be restored immediately
	    pop	    es		    ;	after the call to the API functions.
	    pop	    ds
	    pop	    di
	    pop	    si
	    pop	    cx
	    ret

XMMControl  endp


;*--------------------------------------------------------------------------*
;*									    *
;*  Get XMS Version Number -				    FUNCTION 00h    *
;*									    *
;*	Returns the XMS version number					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = XMS Version Number					    *
;*	    BX = Internal Driver Version Number				    *
;*	    DX = 1 if HMA exists, 0 if it doesn't			    *
;*  REGS:   AX, BX and DX are clobbered					    *
;*									    *
;*  INTERNALLY REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

Version	    proc    near

	    mov	    ax,XMSVersion
	    mov	    bx,HimemVersion
	    xor	    dh,dh

	    ; Is Int 15h hooked?
	    cmp	    word ptr [PrevInt15][2],0	; Is the segment non-zero?
	    jne     short VHooked
	    mov	    dl,[fHMAMayExist]		; No, return the status at
	    ret					;  init time.

VHooked:    mov	    dl,[fHMAExists]		; Yes, return the real status
	    ret

Version	    endp


;*--------------------------------------------------------------------------*
;*									    *
;*  HookInt15 -								    *
;*									    *
;*	Insert the INT 15 hook						    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None							    *
;*  REGS:   AX, BX, CS, DI, SI, and Flags are clobbered			    *
;*									    *
;*  EXTERNALLY NON-REENTRANT						    *
;*	Interrupts must be disabled before calling this function.	    *
;*									    *
;*--------------------------------------------------------------------------*

HookInt15   proc    near

	    push    es
    
	    ; Has a VDISK device been installed?	    
	    call    IsVDISKIn
	    cmp	    [fVDISK],0
	    je	    short HINoVD    ; No, continue
	    pop	    es		    ; Yes, return without hooking
	    ret
	    
HINoVD:	    mov	    ah,88h	    ; Is 64K of Extended memory around?
	    int	    15h
	    sub	    ax,[MemCorr]    ; 6300 Plus may have memory at FA0000h
	    cmp	    ax,64
	    jb	    short HIInitMemory	; Less than 64K free?  Then no HMA.
	    mov	    [fHMAExists],1

HIInitMemory:
	    ; Init the first handle to be one huge free block.
	    mov	    bx,[KiddValley]
	    mov	    [bx.Flags],FREEFLAG
	    mov	    [bx.Len],ax
	    mov     [bx.Base],1024

	    ; Reserve room for the HMA if it exists.	11-30-88 ChipA
	    cmp	    [fHMAExists],0
	    je	    short HICont
	    add     [bx.Base],64
	    sub     [bx.Len],64

	    ; See if any Compaq "Built In Memory" exists.
HICont:     mov     ax,[BIMBase]
	    or	    ax,ax
	    jz	    short HIHookEmHorns

	    mov     cx,[BIMLength]

	    ; Fill out the next handle entry.
	    add     bx,size Handle
	    mov	    [bx.Flags],FREEFLAG
	    mov     [bx.Len],cx
	    mov     [bx.Base],ax

	    ; Save the current INT 15 vector.
HIHookEmHorns:
	    les     si,dword ptr pInt15Vector

	    ; Exchange the old vector with the new one.
	    mov	    ax,offset Int15Handler
	    xchg    ax,es:[si][0]
	    mov	    word ptr [PrevInt15][0],ax
	    mov	    ax,cs
	    xchg    ax,es:[si][2]
	    mov	    word ptr [PrevInt15][2],ax
	    
	    pop	    es
	    ret

HookInt15   endp


;*--------------------------------------------------------------------------*
;*									    *
;*  IsVDISKIn -								    *
;*									    *
;*	Looks for drivers which use the IBM VDISK method of allocating	    *
;*  Extended Memory.  XMS is incompatible with the VDISK method.	    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None.  Sets "fVDISK" accordingly				    *
;*  REGS:   AX, CX, SI, DI and Flags are clobbered			    *
;*									    *
;*  INTERNALLY REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

pVDISK	    label   dword
	    dw	    00013h
	    dw	    0FFFFh
	    
szVDISK	    db	    'VDISK'	       

IsVDISKIn   proc    near

	    ; Look for "VDISK" starting at the 4th byte of extended memory.
	    call    LocalEnableA20		; Turn on A20

	    push    ds
	    push    es

	    ; Set up the comparison.
	    lds	    si,cs:pVDISK
	    push    cs
	    pop	    es
	    mov	    di,offset szVDISK
	    mov	    cx,5
	    cld
	    rep     cmpsb			; Do the comparison

	    pop     es				; Restore ES and DS
	    pop     ds

	    jz	    short IVIFoundIt
	    mov     [fVDISK],0			; No VDISK device found
	    jmp	    short IVIExit
	    
	    ; "VDISK" was found.
IVIFoundIt: mov	    [fVDISK],1

IVIExit:    call    LocalDisableA20
	    ret 				; Turn off A20

IsVDISKIn   endp


;*--------------------------------------------------------------------------*
;*									    *
;*  Int15Handler -							    *
;*									    *
;*	Hooks Function 88h to return zero as the amount of extended	    *
;*	memory available in the system.					    *
;*									    *
;*	Hooks Function 87h and preserves the state of A20 across the	    *
;*	block move.							    *
;*									    *
;*  ARGS:   AH = Function, AL = Subfunction				    *
;*  RETS:   AX = 0 (if AH == 88h)					    *
;*  REGS:   AX is clobbered						    *
;*									    *
;*--------------------------------------------------------------------------*

I15RegSave  dw	    ?
 
Int15Handler proc   far

	    ; Is this a request for the amount of free extended memory?
	    cmp	    ah,88h
	    jne     short I15HCont	; No, continue

	    xor     ax,ax		; Yes, return zero
	    iret

	    ; Is it a Block Move?
I15HCont:   cmp	    ah,87h
	    jne     short I15HNext	; No, continue

	    ; Int 15h Block Move:
	    cli 			; Make sure interrupts are off

	    ; Store the A20 line's state.
	    pusha			; Preserve the registers
	    call    IsA20On
	    mov	    cs:[I15RegSave],ax
	    popa			; Restore the registers

	    ; Call the previous Int 15h handler.
	    pushf			; Simualate an interrupt
	    call    cs:[PrevInt15]
	    pusha			; Preserve previous handler's return

	    ; Was A20 on before?
	    cmp	    cs:[I15RegSave],0
	    je	    short I15HExit	; No, continue
	    mov     ax,1
	    call    cs:[A20Handler]	; Yes, turn A20 back on

I15HExit:   popa			; Restore the previous handler's return
	    iret

	    ; Continue down the Int 15h chain.
I15HNext:   jmp     cs:[PrevInt15]

Int15Handler endp


;*--------------------------------------------------------------------------*
;*									    *
;*  RequestHMA -					    FUNCTION 01h    *
;*									    *
;*	Give caller control of the High Memory Area if it is available.	    *
;*									    *
;*  ARGS:   DX = HMA space requested in bytes				    *
;*  RETS:   AX = 1 if the HMA was reserved, 0 otherwise.  BL = Error Code   *
;*  REGS:   AX, BX and Flags clobbered					    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

RequestHMA  proc   near

	    cli			    ; This is a non-reentrant function.
				    ;	Flags are restored after the return.	    
	    mov	    bl,ERR_HMAINUSE
	    cmp	    [fHMAInUse],1   ; Is the HMA already allocated?
	    je	    short RHRetErr

	    mov	    bl,ERR_HMANOTEXIST
	    cmp	    [fHMAExists],0  ; Is the HMA available?
	    je	    short RHRetErr

	    mov	    bl,ERR_HMAMINSIZE
	    cmp	    dx,[MinHMASize] ; Is this guy allowed in?
	    jb	    short RHRetErr

	    mov	    ax,1
	    mov	    [fHMAInUse],al  ; Reserve the High Memory Area
	    xor     bl,bl	    ; Clear the error code
	    ret

RHRetErr:   xor     ax,ax	    ; Return failure with error code in BL
	    ret

RequestHMA  endp


;*--------------------------------------------------------------------------*
;*									    *
;*  ReleaseHMA -					    FUNCTION 02h    *
;*									    *
;*	Caller is releasing control of the High Memory area		    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if control is released, 0 otherwise.	 BL = Error Code    *
;*  REGS:   AX, BX and Flags clobbered					    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

ReleaseHMA  proc   near

	    cli			    ; This is a non-reentrant function
	    
	    ; Is the HMA currently in use?
	    mov	    al,[fHMAInUse]
	    or	    al,al
	    jz	    short RLHRetErr ; No, return error
	    
	    ; Release the HMA and return success.
	    mov	    [fHMAInUse],0
	    mov	    ax,1
	    xor	    bl,bl
	    ret
	    
RLHRetErr:  xor	    ax,ax
	    mov	    bl,ERR_HMANOTALLOCED
	    ret		   

ReleaseHMA  endp


;*--------------------------------------------------------------------------*
;*									    *
;*  GlobalEnableA20 -					    FUNCTION 03h    *
;*									    *
;*	Globally enable the A20 line					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if the A20 line is enabled, 0 otherwise.  BL = Error	    *
;*  REGS:   AX, BX and Flags clobbered					    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

GlobalEnableA20 proc near

	    cli				; This is a non-reentrant function

	    ; Is A20 already globally enabled?
	    cmp	    [fGlobalEnable],1
	    je	    short GEARet

	    ; Attempt to enable the A20 line.
	    call    LocalEnableA20
	    or	    ax,ax
	    jz	    short GEAA20Err

	    ; Mark A20 as globally enabled.
	    mov	    [fGlobalEnable],1

	    ; Return success.
GEARet:	    mov	    ax,1
	    xor	    bl,bl
	    ret

	    ; Some A20 error occured.
GEAA20Err:  mov	    bl,ERR_A20
	    xor	    ax,ax
	    ret

GlobalEnableA20 endp


;*--------------------------------------------------------------------------*
;*									    *
;*  GlobalDisableA20 -					    FUNCTION 04h    *
;*									    *
;*	Globally disable the A20 line					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if the A20 line is disabled, 0 otherwise.  BL = Error    *
;*  REGS:   AX, BX and Flags are clobbered				    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

GlobalDisableA20 proc near

	    cli				; This is a non-reentrant function

	    ; Is A20 already globally disabled?
	    cmp	    [fGlobalEnable],0
	    je	    short GDARet

	    ; Attempt to disable the A20 line.
	    call    LocalDisableA20
	    or	    ax,ax
	    jz	    short GDAA20Err

	    ; Mark A20 as globally disabled.
	    mov	    [fGlobalEnable],0

	    ; Return success.
GDARet:	    mov	    ax,1
	    xor	    bl,bl
	    ret

	    ; Some A20 error occured.
GDAA20Err:  mov	    bl,ERR_A20
	    xor	    ax,ax
	    ret

GlobalDisableA20 endp


;*--------------------------------------------------------------------------*
;*									    *
;*  LocalEnableA20 -					    FUNCTION 05h    *
;*									    *
;*	Locally enable the A20 line					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if the A20 line is enabled, 0 otherwise.  BL = Error	    *
;*  REGS:   AX, BX and Flags clobbered					    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

LocalEnableA20 proc near

	    cli				; This is a non-reentrant function
	    
	    cmp	    [fCanChangeA20],1	; Can we change A20?
	    jne     short LEARet	; No, don't touch A20

	    ; Only actually enable A20 if the count is zero.
	    cmp	    [EnableCount],0
	    jne     short LEAIncIt

	    ; Attempt to enable the A20 line.
	    mov	    ax,1
	    call    [A20Handler]	; Call machine-specific A20 handler
	    or	    ax,ax
	    jz	    short LEAA20Err

LEAIncIt:   inc	    [EnableCount]

	    ; Return success.
LEARet:	    mov	    ax,1
	    xor	    bl,bl
	    ret

	    ; Some A20 error occurred.
LEAA20Err:  mov	    bl,ERR_A20
	    xor	    ax,ax
	    ret
	    
LocalEnableA20 endp


;*--------------------------------------------------------------------------*
;*									    *
;*  LocalDisableA20 -					    FUNCTION 06h    *
;*									    *
;*	Locally disable the A20 line					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if the A20 line is disabled, 0 otherwise.  BL = Error    *
;*  REGS:   AX, BX and Flags are clobbered				    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

LocalDisableA20 proc near

	    cli				; This is a non-reentrant function
	    
	    cmp	    [fCanChangeA20],0	; Can we change A20?
	    je	    short LDARet	; No, don't touch A20

	    ; Make sure the count's not zero.
	    cmp	    [EnableCount],0
	    je	    short LDAA20Err

	    ; Only actually disable if the count is one.
	    cmp	    [EnableCount],1
	    jne     short LDADecIt

	    xor	    ax,ax
	    call    [A20Handler]	; Call machine-specific A20 handler
	    or	    ax,ax
	    jz	    short LDAA20Err

LDADecIt:   dec	    [EnableCount]

	    ; Return success.
LDARet:	    mov	    ax,1
	    xor	    bl,bl
	    ret

	    ; Some A20 error occurred.
LDAA20Err:  mov     bl,ERR_A20
	    xor     ax,ax
	    ret
			
LocalDisableA20 endp


;*--------------------------------------------------------------------------*
;*									    *
;*  IsA20On -						    FUNCTION 07h    *
;*									    *
;*	Returns the state of the A20 line				    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if the A20 line is enabled, 0 otherwise		    *
;*  REGS:   AX, CX, DI, SI and Flags clobbered				    *
;*									    *
;*  INTERNALLY REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

LowMemory   label   dword	    ; Set equal to 0000:0080
	    dw	    00080h
	    dw	    00000h

HighMemory  label   dword
	    dw	    00090h	    ; Set equal to FFFF:0090
	    dw	    0FFFFh

IsA20On	    proc    near

	    push    ds
	    push    es

	    lds	    si,cs:LowMemory	; Compare the four words at 0000:0080
	    les	    di,cs:HighMemory	;   with the four at FFFF:0090
	    mov	    cx,4
	    cld
	    repe    cmpsw

	    pop	    es
	    pop	    ds
	    xor	    ax,ax

	    jcxz    short IAONoWrap ; Are the two areas the same?
	    inc	    ax		    ; No, return A20 Enabled

IAONoWrap:  xor     bl,bl
	    ret			    ; Yes, return A20 Disabled

IsA20On	    endp


;*--------------------------------------------------------------------------*
;*									    *
;*  QueryExtMemory -					    FUNCTION 08h    *
;*									    *
;*	Returns the size of the largest free extended memory block in K	    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = Size of largest free block in K.  BL = Error code	    *
;*	    DX = Total amount of free extended memory in K		    *
;*  REGS:   AX, BX, DX, DI, SI and Flags clobbered			    *
;*									    *
;*  INTERNALLY REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

QueryExtMemory proc near

	    ; Init the error code in DL.
	    mov	    dl,ERR_OUTOMEMORY
    
	    ; Scan for the largest block marked FREE.
	    xor	    di,di		    ; DI = Max. size found so far
	    xor	    si,si		    ; SI = Total amount of free memory
	    mov	    bx,KiddValley
	    mov     cx,[cHandles]	    ; Loop through the handle table
QEMLoop:    cmp	    [bx.Flags],FREEFLAG
	    jne     short QEMBottom

	    ; Add this free block to the total.
	    add     si,[bx.Len] 	    ; CHANGED 7/19/88

	    ; Is this block larger?
	    mov	    ax,[bx.Len]
	    cmp	    di,ax
	    jae     short QEMBottom	    ; CHANGED 7/19/88
	    
	    ; Yes, save it away.
	    mov	    di,ax
	    xor     dl,dl		    ; We ain't Out o' Memory
	    
QEMBottom:  add	    bx,SIZE Handle
	    loop    QEMLoop

	    ; Setup the return.
	    mov	    ax,di
	    mov	    bl,dl		    ; Retrieve the error code
	    mov     dx,si		    ; CHANGED 7/19/88
	    ret

QueryExtMemory endp


;*--------------------------------------------------------------------------*
;*									    *
;*  AllocExtMemory -					    FUNCTION 09h    *
;*									    *
;*	Reserve a block of extended memory				    *
;*									    *
;*  ARGS:   DX = Amount of K being requested				    *
;*  RETS:   AX = 1 of successful, 0 otherwise.	BL = Error Code		    *
;*	    DX = 16-bit handle to the allocated block			    *
;*  REGS:   AX, BX, DX and Flags clobbered				    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

; Algorithm -
;
;   Scan the Handle Table looking for BOTH an unused handle and
;	a free block which is large enough:
;
;   1.	If both are found -
;	    Mark the free block as used and adjust its size.
;	    Make the unused handle a free block containing the remaining
;		unallocated portion of the original free block.
;
;   2.	If only an unused handle is found -
;	    We're out of memory.
;
;   3.	If only a properly sized free block is found -
;	    We only have one handle left.
;	    Mark the free block as used.  The requester gets all of the
;		block's memory.
;
;   4.	If neither are found -
;	    We're out of memory.

hFreeBlock	dw  ?
hUnusedBlock	dw  ?

AllocExtMemory proc near

	    cli 			; This is a non-reentrant function
	    
	    ; Scan the handle table looking for BOTH an unused handle and 
	    ;	a free block which is large enough.
	    xor	    ax,ax
	    mov	    [hFreeBlock],ax
	    mov	    [hUnusedBlock],ax
	    mov	    bx,KiddValley
	    mov     cx,[cHandles]	; Loop through the handle table

	    ; Have we already found a free block which is large enough?
AEMhLoop:   cmp     [hFreeBlock],0
	    jne     short AEMUnused	; Yes, see if this one is unused
	    
	    ; Is this block free?
	    cmp     [bx.Flags],FREEFLAG
	    jne     short AEMUnused	; No, see if it is unused
	    
	    ; Is it large enough?
	    cmp	    dx,[bx.Len]
	    ja	    short AEMNexth	; No, get the next handle

	    ; Save this guy away.
	    mov	    [hFreeBlock],bx
	    jmp	    short AEMBottom
	    
AEMUnused:  ; Have we already found an unused handle?
	    cmp     [hUnusedBlock],0
	    jne     short AEMNexth	; Yes, get the next handle

	    ; Is this block unused?
	    cmp     [bx.Flags],UNUSEDFLAG
	    jne     short AEMNexth	; No, get the next handle
	    
	    ; Save this guy away.
	    mov	    [hUnusedBlock],bx
			
	    ; Have we found what we are looking for?
	    cmp	    [hFreeBlock],0
	    je	    short AEMNexth
AEMBottom:  cmp	    [hUnusedBlock],0
	    jne     short AEMGotBoth	; Yes, continue
	    
AEMNexth:   ; Get the next handle
	    add	    bx,SIZE Handle
	    loop    AEMhLoop
	    
	    ; We are at the end of the handle table and we didn't find both
	    ; things we were looking for.  Did we find a free block?
	    mov     si,[hFreeBlock]
	    or	    si,si
	    jnz     short AEMRetSuc	; Yes, Case 3 - Alloc the entire block
	    
	    ; Did we find an unused handle?
	    cmp	    [hUnusedBlock],0
	    je	    short AEMOOHandles	; No, Case 4 - We're out of handles
	    
	    ; Case 2 - Is this a request for a zero-length handle?
	    or	    dx,dx
	    jnz     short AEMOOMemory	; No, we're out of memory
	    
	    ; Reserve the zero-length handle.
	    mov	    si,[hUnusedBlock]
	    mov	    [hFreeBlock],si
	    jmp	    short AEMRetSuc
	    
AEMGotBoth: ; We're at Case 1 above.
	    ;	Mark the free block as used (done below) and adjust its size.
	    ;	Make the unused handle a free block containing the remaining
	    ;	   unallocated portion of the original free block.
	    mov	    si,[hFreeBlock]
	    mov	    di,[hUnusedBlock]
	    
	    ; Unused.Base = Old.Base + request
	    mov	    ax,[si].Base
	    add	    ax,dx
	    mov	    [di].Base,ax
	    
	    ; New.Len = request
	    mov	    ax,dx
	    xchg    [si].Len,ax
	    
	    ; Unused.Len = Old.Len - request
	    sub	    ax,dx
	    mov	    [di].Len,ax
	    mov	    [di].Flags,FREEFLAG	    ; Unused.Flags = FREE
	    
AEMRetSuc:  ; Complete the allocation.
	    mov	    [si].Flags,USEDFLAG	    ; New.Flags = USED
	    mov	    dx,[hFreeBlock]
	    mov	    ax,1
	    xor	    bl,bl
	    ret

AEMOOMemory:mov	    bl,ERR_OUTOMEMORY
	    jmp	    short AEMErrRet
	    
AEMOOHandles:
	    mov	    bl,ERR_OUTOHANDLES
AEMErrRet:  xor	    ax,ax		    ; Return failure
	    mov	    dx,ax
	    ret		       
	    
AllocExtMemory endp


;*--------------------------------------------------------------------------*
;*									    *
;*  ValidateHandle -							    *
;*									    *
;*	Validates an extended memory block handle			    *
;*									    *
;*  ARGS:   DX = 16-bit handle to the extended memory block		    *
;*  RETS:   Carry is set if the handle is valid				    *
;*  REGS:   Preserved except the carry flag				    *
;*									    *
;*--------------------------------------------------------------------------*

ValidateHandle proc near

	    pusha			    ; Save everything
	    mov	    bx,dx		    ; Move the handle into BX

	    ; The handle must be equal to or above "KiddValley".
	    cmp	    bx,[KiddValley]
	    jb	    short VHOne
	    
	    ; The handle must not be above "KiddValleyTop".
	    cmp	    bx,[KiddValleyTop]
	    ja	    short VHOne
	    
	    ; (The handle-"KiddValley") must be a multiple of a handle's size.
	    sub	    dx,[KiddValley]
	    mov	    ax,dx
	    xor	    dx,dx
	    mov	    cx,SIZE Handle
	    div	    cx			    
	    or	    dx,dx		    ; Any remainder?		
	    jnz     short VHOne 	    ; Yup, it's bad

	    ; Does the handle point to a currently USED block?
	    cmp	    [bx.Flags],USEDFLAG
	    jne     short VHOne 	    ; This handle is not being used.
	    
	    ; The handle looks good to me...
	    popa			    ; Restore everything
	    stc 			    ; Return success
	    ret

VHOne:	    ; It's really bad.
	    popa			    ; Restore everything
	    clc 			    ; Return failure
	    ret
	    
ValidateHandle endp


;*--------------------------------------------------------------------------*
;*									    *
;*  FreeExtMemory -					    FUNCTION 0Ah    *
;*									    *
;*	Frees a block of extended memory which was allocated via AllocExt   *
;*									    *
;*  ARGS:   DX = 16-bit handle to the extended memory block		    *
;*  RETS:   AX = 1 if successful, 0 otherwise.	BL = Error code		    *
;*  REGS:   AX, BX, CX, DX, SI, DI and Flags clobbered			    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

FreeExtMemory proc near

	    cli 			    ; This is a non-reentrant function
	    
	    ; Make sure that the handle is valid.
	    call    ValidateHandle
	    jnc     short FEMBadh
	    mov	    si,dx		    ; Move the handle into SI
	    
	    ; Make sure that the handle points to a FREE block.
	    cmp	    [si].cLock,0
	    jne     short FEMLockedh
	    
	    ; Mark the block as FREE and cancel any locks.
	    mov	    [si].Flags,FREEFLAG
	    mov	    [si].cLock,0
	    
	    ; Is an adjacent block also free?
FEMScanIt:  mov	    bx,[si].Base	    ; BX = Bottom of block
	    mov	    ax,bx
	    add	    ax,[si].Len		    ; AX = Top of block
	    
	    ; Search for an adjacent FREE block.
	    mov	    di,[KiddValley]	    ; DI = Handle being scanned
	    
	    mov     cx,cHandles 	    ; Loop through the handle table
FEMLoopTop: cmp	    [di].Flags,FREEFLAG	    ; Is this block free?
	    jne     short FEMNexth	    ; No, continue

	    ; Is this block just above the one we are freeing?
	    mov	    dx,[di].Base
	    cmp     dx,ax
	    je	    short FEMBlockAbove     ; Yes, coaless upwards

	    ; Is it just below?
	    add	    dx,[di].Len
	    cmp     dx,bx
	    je	    short FEMBlockBelow     ; Yes, coaless downwards
	    
FEMNexth:   add	    di,SIZE Handle
	    loop    FEMLoopTop

	    ; No adjacent blocks to coaless.
	    mov     ax,1		    ; Return success
	    xor	    bl,bl
	    ret

	    ; Exchange the pointer to the "upper" and "lower" blocks.
FEMBlockBelow:
	    xchg    si,di

	    ; Move the free block above into the current handle.
FEMBlockAbove:
	    mov	    dx,[si].Len
	    add     dx,[di].Len 	    ; Combine the lengths
	    mov	    [si].Len,dx
	    mov     [di].Flags,UNUSEDFLAG   ; Mark old block's handle as UNUSED
	    jmp	    short FEMScanIt	    ; Rescan the list

FEMBadh:    mov	    bl,ERR_INVALIDHANDLE
	    jmp	    short FEMErrExit
	    
FEMLockedh: mov	    bl,ERR_EMBLOCKED
FEMErrExit: xor     ax,ax		    ; Return failure
	    ret
	    
FreeExtMemory endp	      


;*--------------------------------------------------------------------------*
;*									    *
;*  LockExtMemory -					    FUNCTION 0Ch    *
;*									    *
;*	Locks a block of extended memory				    *
;*									    *
;*  ARGS:   DX = 16-bit handle to the extended memory block		    *
;*  RETS:   AX = 1 of successful, 0 otherwise.	BL = Error code		    *
;*	    DX:BX = 32-bit linear address of the base of the memory block   *
;*  REGS:   AX, BX, DX and Flags clobbered				    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

LockExtMemory proc near

	    cli 			    ; This is a non-reentrant function
	    
	    ; Is the handle valid?
	    call    ValidateHandle
	    jnc     short LEMBadh
	    mov     bx,dx		    ; Move the handle into BX

	    ; Are we at some preposterously large limit?
	    cmp	    [bx.cLock],0FFh
	    je	    short LEMOverflow

	    ; Lock the block.
	    inc	    [bx.cLock]
	    
	    ; Return the 32-bit address of its base.
	    mov	    dx,[bx.Base]
	    mov	    bx,dx
	    shr	    dx,6
	    shl	    bx,10

	    ; Return success.
	    mov	    ax,1
	    ret
	    
LEMBadh:    mov	    bl,ERR_INVALIDHANDLE
	    jmp	    short LEMErrExit

LEMOverflow:mov	    bl,ERR_LOCKOVERFLOW
LEMErrExit: xor     ax,ax		    ; Return failure
	    mov	    dx,ax
	    ret 
	    
LockExtMemory endp


;*--------------------------------------------------------------------------*
;*									    *
;*  UnlockExtMemory -					    FUNCTION 0Dh    *
;*									    *
;*	Unlocks a block of extended memory				    *
;*									    *
;*  ARGS:   DX = 16-bit handle to the extended memory block		    *
;*  RETS:   AX = 1 if successful, 0 otherwise.	BL = Error code		    *
;*  REGS:   AX, BX and Flags clobbered					    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

UnlockExtMemory proc near

	    cli				; This is a non-reentrant function
    
	    ; Is the handle valid?
	    call    ValidateHandle
	    jnc     short UEMBadh
	    mov	    bx,dx		; Move the handle into BX

	    ; Does the handle point to a locked block?
	    cmp	    [bx.cLock],0
	    je	    short UEMUnlocked	; No, return error

	    ; Unlock the block.
	    dec	    [bx.cLock]

	    mov     ax,1		; Return success
	    xor     bl,bl
	    ret
	    
UEMUnlocked:mov	    bl,ERR_EMBUNLOCKED
	    jmp	    short UEMErrExit

UEMBadh:    mov	    bl,ERR_INVALIDHANDLE
UEMErrExit: xor	    ax,ax
	    ret		   

UnlockExtMemory endp


;*--------------------------------------------------------------------------*
;*									    *
;*  GetExtMemoryInfo -					    FUNCTION 0Eh    *
;*									    *
;*	Gets other information about an extended memory block		    *
;*									    *
;*  ARGS:   DX = 16-bit handle to the extended memory block		    *
;*  RETS:   AX = 1 if successful, 0 otherwise.	BL = Error code		    *
;*	    BH = EMB's lock count					    *
;*	    BL = Total number of unused handles in the system		    *
;*	    DX = EMB's length						    *
;*  REGS:   AX, BX, CX, DX and Flags clobbered				    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

GetExtMemoryInfo proc	near

	    cli 			    ; This is a non-reentrant function
	    
	    ; Is the handle valid?
	    call    ValidateHandle
	    jnc     short GEMBadh
	    mov     si,dx		    ; Move the handle into SI

	    ; Count the number of handles which are not currently being used.
	    xor     al,al
	    mov     bx,[KiddValley]
	    mov     cx,[cHandles]	    ; Loop through the handle table
GEMLoop:    cmp     [bx.Flags],USEDFLAG     ; Is this handle in use?
	    je	    short GEMNexth		  ; Yes, continue
	    inc     al			    ; No, increment the count
GEMNexth:   add     bx,SIZE Handle
	    loop    GEMLoop

	    ; Setup the return.
	    mov     dx,[si.Len] 	    ; Length in DX
	    mov     bh,[si.cLock]	    ; Lock count in BH
	    mov     bl,al
	    mov	    ax,1
	    ret
	    
GEMBadh:    mov	    bl,ERR_INVALIDHANDLE
	    xor	    ax,ax
	    ret

GetExtMemoryInfo endp
	    

;*--------------------------------------------------------------------------*
;*									    *
;*  ReallocExtMemory -					    FUNCTION 0Fh    *
;*									    *
;*	Reallocates a block of extended memory				    *
;*									    *
;*  ARGS:   DX = 16-bit handle to the extended memory block		    *
;*  RETS:   AX = 1 if successful, 0 otherwise.	BL = Error code		    *
;*  REGS:								    *
;*									    *
;*  INTERNALLY NON-REENTRANT						    *
;*									    *
;*--------------------------------------------------------------------------*

; FUNCTION NOT YET IMPLEMENTED


;*--------------------------------------------------------------------------*
;*									    *
;*  NOTE: RequestUMB and ReleaseUMB will not be implemented by HIMEM.	    *
;*									    *
;*--------------------------------------------------------------------------*


;*--------------------------------------------------------------------------*
;*									    *
;*  MoveExtMemory -					    FUNCTION 0Bh    *
;*									    *
;*	Copys a block of memory from anywhere to anywhere		    *
;*									    *
;*  ARGS:   ES:SI = Pointer to an Extended Memory Block Move Structure	    *
;*		(NOTE: Originally passed in as DS:SI)			    *
;*  RETS:   AX = 1 of successful, 0 otherwise.	BL = Error code.	    *
;*  REGS:   Everybody clobbered						    *
;*									    *
;*  INTERNALLY REENTRANT (believe it or not)				    *
;*									    *
;*--------------------------------------------------------------------------*

EVEN				; Must be word aligned.

MoveBlock:

MEM3_Data	label	byte	; Start of MoveBlock data
MoveBlock286:

include xm286.asm


;*--------------------------------------------------------------------------*

MoveBlock386:

include xm386.asm

EndMoveBlock386:


;****************************************************************************
;*									    *
;* A20 Handler Section: 						    *
;*									    *
;* The Init code copies the proper A20 Handler in place just below the	    *
;*   proper MoveBlock routine.						    *
;*									    *
;* NOTE: A20 HANDLERS MUST ONLY HAVE RELATIVE JUMPS!  HOWEVER ANY CALLS TO  *
;*	 FUNCTIONS OUTSIDE OF THE HANDLER MUST BE NON-RELATIVE!	 The best   *
;*	 method is to call thru a variable such as ControlJumpTable[n].	    *
;*									    *
;****************************************************************************

;*--------------------------------------------------------------------------*
;*									    *
;*  AT_A20Handler -					    HARDWARE DEP.   *
;*									    *
;*	Enable/Disable the A20 line on non-PS/2 machines		    *
;*									    *
;*  ARGS:   AX = 0 for Disable, 1 for Enable				    *
;*  RETS:   AX = 1 for success, 0 otherwise				    *
;*  REGS:   AX, CX and Flags clobbered					    *
;*									    *
;*--------------------------------------------------------------------------*

AT_A20Handler proc   near

	    or	    ax,ax
	    jz	    short AAHDisable

AAHEnable:  call    Sync8042	; Make sure the Keyboard Controller is Ready
	    jnz     short AAHErr

	    mov	    al,0D1h	; Send D1h
	    out	    64h,al
	    call    Sync8042
	    jnz     short AAHErr

	    mov	    al,0DFh	; Send DFh
	    out	    60h,al
	    call    Sync8042
	    jnz     short AAHErr

	    ; Wait for the A20 line to settle down (up to 20usecs)
	    mov	    al,0FFh	; Send FFh (Pulse Output Port NULL)
	    out	    64h,al
	    call    Sync8042
	    jnz     short AAHErr
	    jmp	    short AAHExit

AAHDisable: call    Sync8042	; Make sure the Keyboard Controller is Ready
	    jnz     short AAHErr

	    mov	    al,0D1h	; Send D1h
	    out	    64h,al
	    call    Sync8042
	    jnz     short AAHErr

	    mov	    al,0DDh	; Send DDh
	    out	    60h,al
	    call    Sync8042
	    jnz     short AAHErr

	    ; Wait for the A20 line to settle down (up to 20usecs)
	    mov	    al,0FFh	; Send FFh (Pulse Output Port NULL)
	    out	    64h,al
	    call    Sync8042
	    
AAHExit:    mov	    ax,1
	    ret
	    
AAHErr:	    xor	    ax,ax
	    ret
	    
AT_A20Handler endp


;*--------------------------------------------------------------------------*

Sync8042    proc    near

	    xor	    cx,cx
S8InSync:   in	    al,64h
	    and	    al,2
	    loopnz  S8InSync
	    ret

Sync8042    endp

EndAT_A20Handler:


;*--------------------------------------------------------------------------*
;*									    *
;*  PS2_A20Handler -					    HARDWARE DEP.   *
;*									    *
;*	Enable/Disable the A20 line on PS/2 machines			    *
;*									    *
;*  ARGS:   AX = 0 for Disable, 1 for Enable				    *
;*  RETS:   AX = 1 for success, 0 otherwise				    *
;*  REGS:   AX, CX and Flags clobbered					    *
;*									    *
;*--------------------------------------------------------------------------*

PS2_PORTA   equ	    0092h
PS2_A20BIT  equ	    00000010b

PS2_A20Handler proc   near

	    or	    ax,ax
	    jz	    short PAHDisable

PAHEnable:  in	    al,PS2_PORTA    ; Get the current A20 state
	    test    al,PS2_A20BIT   ; Is A20 already on?
	    jnz     short PAHErr

	    or	    al,PS2_A20BIT   ; Turn on the A20 line
	    out	    PS2_PORTA,al

	    xor	    cx,cx	    ; Make sure we loop for awhile
PAHIsItOn:  in	    al,PS2_PORTA    ; Loop until the A20 line comes on
	    test    al,PS2_A20BIT
	    loopz   PAHIsItOn
	    jz	    short PAHErr    ; Unable to turn on the A20 line
	    jmp	    short PAHExit

PAHDisable: in	    al,PS2_PORTA    ; Get the current A20 state
	    and	    al,NOT PS2_A20BIT	; Turn off the A20 line
	    out	    PS2_PORTA,al

	    xor	    cx,cx	    ; Make sure we loop for awhile
PAHIsItOff: in	    al,PS2_PORTA    ; Loop until the A20 line goes off
	    test    al,PS2_A20BIT
	    loopnz  PAHIsItOff
	    jnz     short PAHErr    ; Unable to turn off the A20 line

PAHExit:    mov	    ax,1
	    ret
	    
PAHErr:	    xor	    ax,ax
	    ret		   

PS2_A20Handler endp

EndPS2_A20Handler:


;*--------------------------------------------------------------------------*
;*									    *
;*  $6300Plus_A20Handler -				    HARDWARE DEP.   *
;*									    *
;*     Enable/Disable address lines A20-A23 on AT&T 6300 Plus		    *
;*									    *
;*  ARGS:   AX = 0 for Disable, 1 for Enable				    *
;*  RETS:   AX = 1 for success, 0 otherwise				    *
;*  REGS:   AX, BX, and Flags clobbered					    *
;*									    *
;*  Note:   Don't want to do two back to back disables on PLUS,		    *
;*	    so we call IsA20On to see if it is necessary.		    *
;*  Warning:  The calcuation of the ReturnBack label depends on the	    *
;*	      expectation that this routine is being moved at init time.    *
;*									    *
;*--------------------------------------------------------------------------*

PLUS_PORT   equ	    03F20h
PLUS_STATUS equ     03FA0h
PLUS_SET    equ     10000000b	; Turn on A20-A23
PLUS_RESET  equ     00010000b	; Turn off A20-A23 and point to our routine

$6300PLUS_A20Handler proc   near
            mov     bx,ax
            push    dx
            mov     dx,PLUS_STATUS
            in      al,dx
            pop     dx
            and     ax,1
            cmp     ax,bx
            jne     short $6AHEnable
	    mov     ax,1
            ret                             ; No, just return

$6AHEnable: 
            pushf
            sti
            mov     al,PLUS_SET
            or      bx,bx                   ; Zero if disable
            jnz     short $6AHNext
            mov     al,PLUS_RESET

$6AHNext:   push    dx                      ; Set/reset the port
            mov     dx,PLUS_PORT
            out     dx,al
            pop     dx
            or      bx,bx
            jnz     short $6AHNext1
            call    $6300Reset              ; Reset the processor
$6AHNext1:
            popff
            mov ax,1
            ret

$6300Plus_A20Handler endp


;*--------------------------------------------------------------------------*
;*									    *
;* $6300Reset -						   HARDWARE DEP.    *
;*									    *
;* Reset the 80286 in order to turn off the address lines on the 6300 PLUS. *
;* This is the only way to do this on the current hardware.		    *
;* The processor itself is reset by reading or writing port 03F00h.	    *
;*									    *
;*  Uses flags.								    *
;*									    *
;*--------------------------------------------------------------------------*

$6300Reset  proc    near

	    pusha			    ; Save world
	    push    ds			    ; Save segments
	    push    es
	    mov	    ax,BiosSeg		    ; Point to the BIOS segment
	    mov	    ds,ax		    ; ds -> 40h

	    ; Setup the reset return address.
assume ds:nothing
	    push    word ptr ds:[RealLoc1]  ; Save what might have been here
	    push    word ptr ds:[RealLoc1+2]

            ; Load our return address, remembering that we will be relocated
            ;   at init time.
            mov     word ptr ds:[RealLoc1+2],cs
            mov     ax,cs:[A20Handler]
            add     ax,offset ReturnBack-offset $6300Plus_A20Handler
            mov     word ptr ds:[RealLoc1],ax 
            mov     cs:[OldStackSeg],ss     ; Save the stack segment, too

	    ; Reset the processor - turning off A20 in the process.
	    mov	    dx,03F00h
	    in	    ax,dx

	    ; We shouldn't get here.  Halt the machine if we do.
	    nop
	    nop
	    nop
	    nop
	    cli
	    hlt

ReturnBack: mov	    ss,cs:[OldStackSeg]		; Start the recovery
	    pop	    word ptr ds:[RealLoc1+2]	; ROM code has set ds->40h
	    pop	    word ptr ds:[RealLoc1]
	    pop	    es
	    pop	    ds

assume ds:code
            xor     al,al
            mov     dx,PLUS_PORT
            out     dx,al
            popa
            ret

$6300Reset endp

End6300Plus_Handler:


;*--------------------------------------------------------------------------*
;*									    *
;*  HP_A20Handler -					    HARDWARE DEP.   *
;*									    *
;*	Enable/Disable the A20 line on HP Vectra machines		    *
;*									    *
;*  ARGS:   AX = 0 for Disable, 1 for Enable				    *
;*  RETS:   AX = 1 for success, 0 otherwise				    *
;*  REGS:   AX, CX and Flags clobbered					    *
;*									    *
;*--------------------------------------------------------------------------*

HP_A20Handler proc   near

	    or	    ax,ax
	    jz	    short HAHDisable

HAHEnable:  call    HPSync8042	; Make sure the Keyboard Controller is ready
	    jnz     short HAHErr

	    mov	    al,0DFh	; Send DFh
	    out	    64h,al
	    call    HPSync8042
	    jnz     short HAHErr

	    mov	    al,0DFh	; Send second DFh
	    out	    64h,al
	    call    HPSync8042
	    jnz     short HAHErr
	    jmp	    short HAHExit

HAHDisable: call    HPSync8042	  ; Make sure the Keyboard Controller is Ready
	    jnz     short HAHErr

	    mov	    al,0DDh	; Send DDh
	    out	    64h,al
	    call    HPSync8042
	    jnz     short HAHErr

	    mov	    al,0DDh	; Send second DDh
	    out	    64h,al
	    call    HPSync8042
	    jnz     short HAHErr

HAHExit:    mov	    ax,1
	    ret

HAHErr:	    xor	    ax,ax
	    ret
	    
HP_A20Handler endp


;*--------------------------------------------------------------------------*

HPSync8042  proc    near

	    xor	    cx,cx
H8InSync:   in	    al,64h
	    and	    al,2
	    loopnz  H8InSync
	    ret

HPSync8042  endp

EndHP_A20Handler:


;*--------------------------------------------------------------------------*
;*									    *
;*  Extended Memory Handle Table -					    *
;*									    *
;*--------------------------------------------------------------------------*

; Actually, the handle table will be located just after the end of whichever
; A20 Handler is installed and will overwrite the others.  In large cases
; however, it could extend beyond all of these guys and crunch the Init code.
; Hence this buffer.  If the driver ever gets over 64K (heaven forbid) this
; scheme should be reworked.

; Guarantee space for all handles.  Kinda overkill but any extra will be 
; discarded with the Init code.

HandleFiller	    dw	    MAXHANDLES * SIZE Handle DUP(?)


;****************************************************************************
;*									    *
;*  NOTE: Code below here will be discarded after driver initialization.    *
;*									    *
;****************************************************************************

;*--------------------------------------------------------------------------*
;*									    *
;*  InitDriver -							    *
;*									    *
;*	Called when driver is Initialized.				    *
;*									    *
;*  ARGS:   ES:DI = Address of the Request Header			    *
;*  RETS:   AX = 0, pHdr.Address = Bottom of resident driver code	    *
;*  REGS:   AX, CX and Flags are clobbered				    *
;*									    *
;*--------------------------------------------------------------------------*

InitLine    dw	    MoveBlock	    ; The line which defines what will be dis-
				    ;	carded after the driver is initialized
InitDriver  proc    near

	    ; Display the sign on message.
	    mov	    ah,9h
	    mov	    dx,offset SignOnMsg
	    int	    21h

	    ; Is this DOS 3.00 or higher?
	    mov	    ah,30h
	    int	    21h			; Get DOS versions number
	    cmp	    al,3
	    jae     short IDCheckXMS
	    
	    mov	    dx,offset BadDOSMsg
	    jmp     short IDFlushMe2
			
	    ; Is another XMS driver already installed?
IDCheckXMS: mov     ax,4300h
	    int	    2Fh
	    cmp	    al,80h		; Is INT 2F hooked?
	    jne     short IDNotInYet
	    mov	    dx,offset NowInMsg
	    jmp     short IDFlushMe2

	    ; What kind of processor are we on?
IDNotInYet: call    MachineCheck
	    cmp	    ax,1		; Is it an 8086/8088?
	    jne     short IDProcOK
	    mov	    dx,offset On8086Msg
	    jmp     short IDFlushMe

	    ; Is there any Compaq BIM?
IDProcOK:
	    call    GetBIMMemory
	    or	    ax,ax
	    jz	    short IDNoBIM

	    ; How much extended memory is installed?
	    call    GetInt15Memory
	    cmp     ax,64		; Is there >= 64K of extended?
	    jb	    short IDNoHMA	; Nope - No HMA
	    jmp     short IDHMAOK	; Yup - continue

	    ; How much extended memory is installed?
IDNoBIM:    call    GetInt15Memory
	    cmp     ax,64		; Is there >= 64K of extended?
	    jae     short IDHMAOK	; Yup - continue

	    or	    ax,ax		; Is there any extended?
	    jnz     short IDNoHMA	; Yup - No HMA

	    ; We can't find any memory to manage.
	    mov	    dx,offset NoExtMemMsg
IDFlushMe2: jmp     short IDFlushMe
	    
	    ; There isn't enough for the HMA, but there is some extended
	    ; memory which we can manage.
IDNoHMA:    mov     dx,offset NoHMAMsg
	    mov	    ah,9h
	    int	    21h
	    jmp     short IDFinalInit

IDHMAOK:    mov     [fHMAMayExist],1

	    ; Install the proper MoveBlock function.
IDFinalInit:call    InstallMoveBlock

	    ; Install the proper A20 handler.
	    call    InstallA20
	    
	    ; Was A20 on already?
	    call    IsA20On
	    xor	    ax,1
	    mov	    [fCanChangeA20],al	; Yes, don't ever disable it
	    cmp     al,0
	    jne     short IDDiddler
	    
	    ; Display the "A20 Already On" message.
	    mov	    dx,offset A20OnMsg
	    mov     ah,9h		; CHANGED - 8/9/88
	    int	    21h
	    
	    ; Can we successfully diddle A20?	CHANGED - 8/9/88
IDDiddler:  call    LocalEnableA20	; Try to enable A20
	    or	    ax,ax		; Did we do it?
	    jz	    short IDBadA20	; Nope - phoosh ourselves

	    call    IsA20On		; Is A20 on?
	    or	    ax,ax
	    jz	    short IDBadA20	; Nope - phoosh ourselves

	    call    LocalDisableA20	; Try to disable A20
	    or	    ax,ax		; Did we do it?
	    jnz     short IDA20Cont	; Yup - continue

	    cmp     [fCanChangeA20],1	; Has A20 been permenantly enabled?
	    je	    short IDGetParms	; Yup - continue
	    jmp     short IDBadA20	; Nope - phoosh ourselves
	    
IDA20Cont:  call    IsA20On		; Is A20 off?
	    or	    ax,ax
	    jz	    short IDGetParms	; Yup - continue

	    ; A20 ain't doing it.
IDBadA20:   mov	    dx,offset BadA20Msg

	    ; Display the message in DX followed by the "Flush" message.
IDFlushMe:  mov	    ah,9h
	    int	    21h
	    mov	    dx,offset FlushMsg
	    mov	    ah,9h
	    int	    21h
	    
	    ; Discard the entire driver.
	    xor	    ax,ax
	    mov	    [InitLine],ax
	    les	    di,[pReqHdr]
	    mov	    es:[di.Units],al
	    jmp	    short IDReturn
	    
	    ; Process the command line parameters.
IDGetParms: call    GetParms
	    
	    ; Initialize the Handle Table.
	    call    InitHandles

	    ; Display the success messages.
	    cmp	    [fHMAMayExist],0
	    je	    short IDIgnition
	    mov	    dx,offset HMAOKMsg
	    mov	    ah,9h
	    int	    21h

	    ; "Turn On" the driver.
IDIgnition: call    HookInt2F

	    ; Discard the initialization code.
IDReturn:   les	    di,[pReqHdr]
	    mov	    ax,[InitLine]
	    mov	    word ptr es:[di.Address][0],ax
	    mov	    word ptr es:[di.Address][2],cs

	    ; Return success.
	    xor	    ax,ax
	    ret

InitDriver  endp

	    
;*--------------------------------------------------------------------------*
;*									    *
;*  HookInt2F -								    *
;*									    *
;*	Insert the INT 2F hook						    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None							    *
;*  REGS:   AX, SI, ES and Flags are clobbered				    *
;*									    *
;*  EXTERNALLY NON-REENTRANT						    *
;*	Interrupts must be disabled before calling this function.	    *
;*									    *
;*--------------------------------------------------------------------------*

HookInt2F   proc    near

	    ; Save the current INT 2F vector
	    cli
	    xor	    ax,ax
	    mov	    es,ax
	    mov	    si,2Fh * 4		    ; ES:SI = Address of 2F vector

	    ; Exchange the old vector with the new one
	    mov	    ax,offset Int2FHandler
	    xchg    ax,es:[si][0]
	    mov	    word ptr [PrevInt2F][0],ax
	    mov	    ax,cs
	    xchg    ax,es:[si][2]
	    mov	    word ptr [PrevInt2F][2],ax
	    sti
	    ret

HookInt2F   endp


;*--------------------------------------------------------------------------*
;*									    *
;*  MachineCheck -							    *
;*									    *
;*	Determines the CPU type.					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if we're on an 8086/8088 or an 80186, 0 otherwise	    *
;*  REGS:   AX and Flags are clobbered					    *
;*									    *
;*--------------------------------------------------------------------------*

; NOTE: This is the "official" Intel method for determining CPU type.

MachineCheck proc   near

	    xor     ax,ax		; Move 0 into the Flags register
	    push    ax
	    popf
	    pushf			; Try and get it back out
	    pop     ax
	    and     ax,0F000h		; If the top four bits are set...
	    cmp     ax,0F000h
	    je	    short MC_8086	; ...it's an 8086 machine

	    mov     ax,0F000h		; Move F000h into the Flags register
	    push    ax
	    popf
	    pushf			; Try and get it back out
	    pop     ax
	    and     ax,0F000h		; If the top four bits aren't set...
	    jz	    short MC_80286	; ...it's an 80286 machine

	    ; We're on an 80386 machine
	    mov     ax,3
	    ret

MC_80286:   ; We're on an 80286 machine
	    mov     ax,2
	    ret

MC_8086:    ; We're on an 8086 machine
	    mov     ax,1
	    ret
	    
MachineCheck endp

	    
;*--------------------------------------------------------------------------*
;*									    *
;*  GetBIMMemory -							    *
;*									    *
;*	Look for Compaq 'Built In Memory' and add it to the pool of	    *
;*  available memory							    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = Amount of BIM memory found				    *
;*  REGS:   AX, BX, CX, and Flags are clobbered				    *
;*									    *
;*--------------------------------------------------------------------------*

; "Built In Memory" (BIM) starts at FE00:0000h and grows downward.  It is
; controlled by a data structure at F000:FFE0h.  Changing the data structure
; involves un-write-protecting the ROMs (!) by flipping bit 1 of 80C00000.

pBIMSTRUCT  equ     0FFE0h
AVAILABLE   equ     0		; Set to -1 if BIM isn't around
TOTALBIM    equ     2		; Total amount of BIM in the system
AVAILBIM    equ     4		; Amount of BIM available in paragraphs
LASTUSED    equ     6		; Paragraph address of last (lowest) used
				;  paragraph of BIM

pCOMPAQ     label   dword
	    dw	    0FFE8h
	    dw	    0F000h
	    
szCOMPAQ    db	    '03COMPAQ'

pRAMRELOC   label   dword
	    dw	    00000h
	    dw	    080C0h

GDT_TYPE    struc
	    dw	    0,0,0,0
	    dw	    0,0,0,0

S_LIMIT     dw	    1
S_BASE_LOW  dw	    0
S_BASE_HI   db	    0
S_RIGHTS    db	    93h
S_RESERVED  dw	    0

D_LIMIT     dw	    1
D_BASE_LOW  dw	    0000h
D_BASE_HI   db	    0C0h
D_RIGHTS    db	    93h
D_RES386    db	    0
D_BASE_XHI  db	    080h

	    dw	    0,0,0,0
	    dw	    0,0,0,0
GDT_TYPE    ends

BIMGDT	    GDT_TYPE <>

BIMBuffer   dw	    ?


GetBIMMemory proc near

	    xor     ax,ax

	    ; Are we on a Compaq 386 machine?
	    push    es

	    ; Set up the comparison.
	    les     di,cs:pCOMPAQ
	    mov     si,offset szCOMPAQ
	    mov     cx,8
	    cld
	    rep     cmpsb		    ; Do the comparison

	    jne     short FCMNoMem2	    ; Nope, return

	    ; Is there a 32-bit memory board installed?
	    mov     bx,pBIMSTRUCT
	    mov     bx,es:[bx]		    ; De-reference the pointer
	    mov     dx,es:[bx+AVAILABLE]    ; -1 means no board is installed
	    inc     dx
	    jz	    short FCMNoMem2	    ; Nope, return

	    ; How much memory is available and where does it start?
	    mov     dx,es:[bx+AVAILBIM]     ; Size in paragraphs
	    or	    dx,dx		    ; Any left?
	    jz	    short FCMNoMem
	    mov     cx,dx		    ; CX = Size in paragraphs
	    mov     ax,es:[bx+LASTUSED]
	    sub     ax,cx		    ; AX = Starting location - F0000h
					    ;	     in paragraphs
	    push    es			    ; Save for a rainy day...
	    push    bx
	    push    ax

	    ; Change AX to the starting location in K.
	    shr     ax,4
	    add     ax,0F000h
	    shr     ax,2

	    ; Change CX to the size in K.
	    shr     cx,6

	    ; Store away for use by HookInt15().
	    mov     [BIMBase],ax
	    mov     [BIMLength],cx

	    ; Un-WriteProtect the ROMs.
	    mov     si,offset BIMGDT		    ; Set up the BlockMove GDT
	    mov     ax,cs
	    mov     es,ax
	    mov     cx,16
	    mul     cx
	    add     ax,offset BIMBuffer
	    adc     dl,0
	    mov     [si.S_BASE_LOW],ax
	    mov     [si.S_BASE_HI],dl
	    mov     cx,1

	    mov     word ptr [BIMBuffer],0FEFEh     ; FEh unlocks the ROMs

	    mov     ah,87h			    ; Do the BlockMove
	    int     15h
	    or	    ah,ah			    ; Was there an error?
	    jz	    short FCMReserve		    ; Nope - continue

	    ; Return error.
	    pop     ax				    ; Clean up
	    pop     bx
	    pop     es
	    xor     ax,ax
	    mov     [BIMBase],ax
	    mov     [BIMLength],ax
FCMNoMem2:  jmp     short FCMNoMem

	    ; Change the ROM values to reserve the BIM stuff.
FCMReserve: pop     ax
	    pop     bx
	    pop     es
	    mov     word ptr es:[bx+AVAILBIM],0      ; Reserve all remaining BIM
	    mov     word ptr es:[bx+LASTUSED],ax

	    ; Re-WriteProtect the ROMs.
	    push    cs
	    pop     es
	    mov     si,offset BIMGDT		    ; Set up the BlockMove GDT

	    mov     word ptr [BIMBuffer],0FCFCh     ; FCh unlocks the ROMs

	    mov     ah,87h			    ; Do the BlockMove
	    int     15h

	    mov     ax,1			    ; Return success

FCMNoMem:   pop     es
	    ret

GetBIMMemory endp


;*--------------------------------------------------------------------------*
;*									    *
;*  GetInt15Memory -							    *
;*									    *
;*	Returns the amount of memory INT 15h, Function 88h says is free	    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = Amount of free extended memory in K-bytes		    *
;*  REGS:   AX and Flags are clobbered					    *
;*									    *
;*--------------------------------------------------------------------------*
	    
GetInt15Memory proc near

	    ; Check for 6300 Plus (to set "MemCorr").
	    call    Is6300Plus

	    ; Get the amount of extended memory Int 15h says is around.
	    mov	    ah,88h
	    clc
	    int	    15h		    ; Is Function 88h around?
	    jnc     short GIM100
	    xor	    ax,ax	    ; No, return 0
	    ret
	    
GIM100:	    sub	    ax,[MemCorr]    ; Compensate for 6300 Plus machines
	    ret

GetInt15Memory endp			   
	    
	    
;*--------------------------------------------------------------------------*
;*									    *
;*  InstallMoveBlock -					    HARDWARE DEP.   *
;*									    *
;*	Attempt to install the proper MoveBlock function.		    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None							    *
;*  REGS:   AX, CX, DI, SI, ES and Flags are clobbered			    *
;*									    *
;*--------------------------------------------------------------------------*

InstallMoveBlock proc near

	    ; Are we on a 386 machine?
	    call    MachineCheck
	    cmp     ax,3
	    je	    short IMBOn386	    ; Yes, install the 386 routine

	    ; Install the 286 MoveBlock routine.
	    mov     [InitLine],offset EndMoveBlock286
	    call    InitMoveExtended286
	    ret

	    ; Install the 386 MoveBlock routine.
IMBOn386:   mov     si,offset MoveBlock386
	    mov     cx,(offset EndMoveBlock386 - offset MoveBlock386)

	    ; REP MOV the routine into position.
	    cld
	    push    cs
	    pop	    es
	    mov     di,offset MoveBlock
	    add     [InitLine],cx
	    rep movsb
	    call    InitMoveBlock386
	    ret

InstallMoveBlock endp


;*--------------------------------------------------------------------------*
;*									    *
;*  InitMoveBlock386 -					    HARDWARE DEP.   *
;*									    *
;*	Initializes the 386 MoveBlock routine				    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None							    *
;*  REGS:   AX, CX, DX and Flags are clobbered				    *
;*									    *
;*--------------------------------------------------------------------------*

InitMoveBlock386 proc near

	    mov     ax,cs			; dx has CS of codeg
	    mov     [patch3],ax 		; Patch code
	    mov     cx,16
	    mul     cx
	    mov     [descCS].LO_apDesc386,ax	; Set up selector for our CS
	    mov     [descCS].MID_apDesc386,dl
	    add     ax,offset code:OurGDT	; Calculate Base of GDT
	    adc     dx,0
	    mov     [GDTPtr.LO_apBaseGdt],ax
	    mov     [GDTPtr.HI_apBaseGdt],dx
	    mov     ax,offset code:MoveExtended386+MEM3_Offset
	    mov     ControlJumpTable[0Bh*2],ax
	    ret

InitMoveBlock386 endp


;*--------------------------------------------------------------------------*
;*									    *
;*  InstallA20 -					    HARDWARE DEP.   *
;*									    *
;*	Attempt to install the proper A20 Handler			    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None							    *
;*  REGS:   AX, CX, DI, SI, ES and Flags are clobbered			    *
;*									    *
;*--------------------------------------------------------------------------*

InstallA20  proc near
	    
	    ; Are we on a 6300 Plus?
	    call    Is6300Plus
	    or	    ax,ax
	    jz	    short IAChkPS2

	    ; Yes, relocate 6300 Plus A20 handler.
	    mov	    si,offset $6300PLUS_A20Handler
	    mov	    cx,(offset End6300PLUS_Handler - offset $6300PLUS_A20Handler)
	    jmp	    short IAMoveIt

	    ; Are we on a PS/2?
IAChkPS2:   call    IsPS2Machine
	    cmp	    ax,1
	    jne     short IACheckHP

	    ; Yes, relocate the PS/2 A20 handler.
	    mov	    si,offset PS2_A20Handler
	    mov	    cx,(offset EndPS2_A20Handler - offset PS2_A20Handler)
	    jmp	    short IAMoveIt

	    ; Are we on a HP Vectra?
IACheckHP:  call    IsHPMachine
	    cmp	    ax,1
	    jne     short IAOnAT

	    ; Yes, relocate the HP A20 handler.
	    mov	    si,offset HP_A20Handler
	    mov     cx,(offset EndHP_A20Handler - offset HP_A20Handler)
	    jmp     short IAMoveIt

IAOnAT:     mov     si,offset AT_A20Handler
	    mov     cx,(offset EndAT_A20Handler - offset AT_A20Handler)

	    ; REP MOV the proper handler into position.
IAMoveIt:   cld
	    push    cs
	    pop	    es
	    mov     di,[InitLine]
	    mov     [A20Handler],di
	    add	    [InitLine],cx
	    rep movsb
	    ret

InstallA20  endp


;*--------------------------------------------------------------------------*
;*									    *
;*  GetParms -								    *
;*									    *
;*	Get any parameters off of the HIMEM command line		    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None							    *
;*  REGS:   AX, BX, CX, DX, DI, SI, ES and Flags clobbered		    *
;*									    *
;*  Side Effects:   cHandles and MinHMASize may be changed		    *
;*									    *
;*--------------------------------------------------------------------------*

GPRegSave   dw	    ?

GetParms    proc    near

	    push    ds

	    cld
	    les	    di,[pReqHdr]
	    lds	    si,es:[di.pCmdLine]	    ; DS:SI points to first char
					    ; after "DEVICE="
	    ; Scan until we see a frontslash or the end of line.
GPBadParm:	      
GPNextChar: lodsb
	    cmp	    al,'/'
	    je	    short GPGotOne
	    cmp	    al,13
	    jne     short GPNextChar
GPDatsAll:  pop	    ds
	    ret

	    ; Save what we found and get the number after it.
GPGotOne:   lodsb
	    mov	    cs:[GPRegSave],ax
	    
	    ; Scan past the rest of the parm for a number, EOL, or a space.
GPNeedNum:  lodsb
	    cmp	    al,' '
	    je	    short GPBadParm
	    cmp	    al,13
	    je	    short GPBadParm
	    cmp	    al,'0'
	    jb	    short GPNeedNum
	    cmp	    al,'9'
	    ja	    short GPNeedNum
	    
	    ; Read the number at DS:SI into DX.
	    xor	    dx,dx
GPNumLoop:  sub	    al,'0'
	    cbw
	    add	    dx,ax
	    lodsb
	    cmp	    al,' '
	    je	    short GPNumDone
	    cmp	    al,13
	    je	    short GPNumDone
	    cmp	    al,'0'
	    jb	    short GPBadParm
	    cmp	    al,'9'
	    ja	    short GPBadParm
	    shl	    dx,1		    ; Stupid multiply DX by 10
	    mov	    bx,dx
	    shl	    dx,1
	    shl	    dx,1
	    add	    dx,bx
	    jmp	    short GPNumLoop
	    
	    ; Which parameter are we dealing with here?
GPNumDone:  xchg    ax,cs:[GPRegSave]
	    cmp	    al,'H'		    ; HMAMIN= parameter?
	    je	    short GPGotMin
	    cmp	    al,'N'		    ; NUMHANDLES= parameter?
	    jne     short GPBadParm

	    ; Process /NUMHANDLES= parameter.
GPGotHands: cmp	    dx,MAXHANDLES
	    ja	    short GPBadParm

	    or	    dx,dx		    ; Zero?
	    jz	    short GPBadParm

	    mov     cs:[cHandles],dx	    ; Store it

	    ; Print descriptive message.
	    mov	    dx,offset StartMsg
	    call    GPPrintIt
	    mov	    ax,cs:[cHandles]
	    call    GPPrintAX
	    mov	    dx,offset HandlesMsg
	    call    GPPrintIt
	    jmp	    short GPNextParm

	    ; Process /HMAMIN= parameter.
GPGotMin:   cmp	    dx,64
	    ja	    short GPBadParm

	    push    dx
	    mov	    cs:[MinHMASize],dx

	    ; Print a descriptive message.
	    mov	    dx,offset HMAMINMsg
	    call    GPPrintIt
	    mov	    ax,cs:[MinHMASize]
	    call    GPPrintAX
	    mov	    dx,offset KMsg
	    call    GPPrintIt

	    pop	    dx
	    mov	    cl,10		    ; Convert from K to bytes
	    shl	    dx,cl
	    mov	    cs:[MinHMASize],dx

	    ; Were we at the end of the line?
GPNextParm: mov	    ax,cs:[GPRegSave]
	    cmp	    al,13
	    je	    short GPExit
	    jmp     GPNextChar

GPExit:	    pop	    ds
	    ret
	    
GetParms    endp

;*--------------------------------------------------------------------------*

GPPrintIt   proc    near

	    push    ds			    ; Save current DS
	    push    cs			    ; Set DS=CS
	    pop	    ds
	    mov	    ah,9h
	    int	    21h
	    pop	    ds			    ; Restore DS
	    ret

GPPrintIt   endp

;*--------------------------------------------------------------------------*

GPPrintAX   proc    near

	    mov	    cx,10
	    xor	    dx,dx
	    div	    cx
	    or	    ax,ax
	    jz	    short GPAPrint
	    push    dx
	    call    GPPrintAX
	    pop	    dx
GPAPrint:   add	    dl,'0'
	    mov	    ah,2h
	    int	    21h
	    ret

GPPrintAX   endp


;*--------------------------------------------------------------------------*
;*									    *
;*  InitHandles -							    *
;*									    *
;*	Initialize the Extended Memory Handle Table			    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   None							    *
;*  REGS:   AX, BX, CX, and Flags are clobbered				    *
;*									    *
;*--------------------------------------------------------------------------*

InitHandles proc    near

	    ; Allocate room for the Handle table at the end.
	    mov	    ax,[InitLine]
	    mov	    [KiddValley],ax
	    mov	    ax,SIZE Handle
	    mov	    cx,[cHandles]
	    mul	    cx
	    add	    [InitLine],ax
	    
	    ; Init the Handle table.
	    mov	    bx,KiddValley
	    xor	    ax,ax
	    mov	    cx,[cHandles]
IHTabLoop:  mov	    [bx.Flags],UNUSEDFLAG
	    mov	    [bx.cLock],al
	    mov	    [bx.Base],ax
	    mov	    [bx.Len],ax
	    add	    bx,SIZE Handle
	    loop    IHTabLoop

	    ; Store away the top of the table for handle validation.
	    mov	    [KiddValleyTop],bx
	    ret
	    
InitHandles endp


;*--------------------------------------------------------------------------*
;*									    *
;*  Is6300Plus						   HARDWARE DEP.    *
;*									    *
;*	Check for AT&T 6300 Plus					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if we're on an AT&T 6300 Plus, 0 otherwise		    *
;*  REGS:   AX, flags used.						    *
;*									    *
;*  Side Effects:   MemCorr value updated to 384 if necessary.		    *
;*									    *
;*--------------------------------------------------------------------------*

Is6300Plus  proc near

	    xor	    ax,ax
	    push    bx
	    mov	    bx,0FC00h		    ; Look for 'OL' at FC00:50
	    mov	    es,bx
	    cmp	    es:[0050h],'LO'
	    jne     short I6PNotPlus	    ; Not found
	    mov	    bx,0F000h
	    mov	    es,bx
	    cmp	    word ptr es:[0FFFDh],0FC00h	    ; Look for 6300 PLUS
	    jne     short I6PNotPlus

	    in	    al,66h		    ; Look for upper extended memory
	    and	    al,00001111b
	    cmp	    al,00001011b
	    jne     short I6PNoMem
	    mov	    [MemCorr],384	    ; Save value

I6PNoMem:   mov	    ax,1		    ; We found a PLUS
I6PNotPlus: pop	    bx
	    ret

Is6300Plus  endp


;*--------------------------------------------------------------------------*
;*									    *
;*  IsPS2Machine					    HARDWARE DEP.   *
;*									    *
;*	Check for PS/2 machine						    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if we're on a valid PS/2 machine, 0 otherwise	    *
;*  REGS:   AX	and Flags clobbered					    *
;*									    *
;*--------------------------------------------------------------------------*

IsPS2Machine proc   near

	    mov     ah,0C0h	    ; Get System Description Vector
	    stc
	    int	    15h
	    jc	    short IPMNoPS2  ; Error?  Not a PS/2.

	    ; Do we have a "Micro Channel" computer?
	    mov     al,byte ptr es:[bx+5]   ; Get "Feature Information Byte 1"
	    test    al,00000010b    ; Test the "Micro Channel Implemented" bit
	    jz	    short IPMNoPS2

IPMFoundIt: xor	    ax,ax	    ; Disable A20. Fixes PS2 Ctl-Alt-Del bug
	    call    PS2_A20Handler
	    mov	    ax,1
	    ret

IPMNoPS2:   xor	    ax,ax
	    ret

IsPS2Machine endp


;*--------------------------------------------------------------------------*
;*									    *
;*  IsHPMachine						    HARDWARE DEP.   *
;*									    *
;*	Check for HP Vectra Machine					    *
;*									    *
;*  ARGS:   None							    *
;*  RETS:   AX = 1 if we're on a HP Vectra machine, 0 otherwise		    *
;*  REGS:   AX, ES and Flags clobbered					    *
;*									    *
;*--------------------------------------------------------------------------*

IsHPMachine proc   near

	    mov	    ax,0F000h
	    mov	    es,ax
	    mov	    ax,word ptr es:[0F8h]
	    cmp	    ax,'PH'
	    je	    short IHMFoundIt
	    xor	    ax,ax
	    ret

IHMFoundIt: mov	    ax,1
	    ret

IsHPMachine endp


;*--------------------------------------------------------------------------*
;*	DRIVER MESSAGES							    *
;*--------------------------------------------------------------------------*		 

SignOnMsg   db	13,10,'HIMEM: DOS XMS Driver, Version 2.06 - 3/21/89'
	    db	13,10,'Copyright 1988,1989 Microsoft Corp.'
	    db	13,10,'$'

BadDOSMsg   db	13,10,'ERROR: HIMEM.SYS requires DOS 3.00 or higher.$'
NowInMsg    db	13,10,'ERROR: An Extended Memory Manager is already installed.$'
On8086Msg   db	13,10,'ERROR: HIMEM.SYS requires an 80x86-based machine.$'
NoExtMemMsg db	13,10,'ERROR: No available extended memory was found.$'
BadA20Msg   db	13,10,'ERROR: Unrecognized A20 hardware.$'
FlushMsg    db	13,10,'	      XMS Driver not installed.',13,10,13,10,'$'

StartMsg    db	13,10,'$'
HandlesMsg  db	' extended memory handles available.$'
HMAMINMsg   db	13,10,'Minimum HMA size set to $'
KMsg	    db	'K.$'

NoHMAMsg    db	13,10,'WARNING: The High Memory Area is unavailable.',13,10,'$'
A20OnMsg    db	13,10,'WARNING: The A20 Line was already enabled.',13,10,'$'

HMAOKMsg    db	13,10,'64K High Memory Area is available.',13,10,13,10,'$'

	    db	'This program is the property of Microsoft Corporation.'
	    
	    db	64 dup(0)			; For internationalization

;*==========================================================================*

code	    ends

	    end
