;/* himem4.asm
; *
; * Microsoft Confidential
; * Copyright (C) Microsoft Corporation 1988-1991
; * All Rights Reserved.
; *
; * Modification History
; *
; * Sudeepb 14-May-1991 Ported for NT XMS support
; *
; * williamh 25-Sept-1992 Added RequestUMB and ReleaseUMB
; *
; * daveh 1-Feb-1994 Changed to do mem management on 32 bit side
; */
	page	95,160
	title	himem4 - block allocation stuff

	.xlist
	include	himem.inc
	include xmssvc.inc
        include vint.inc
	.list

;	The stuff we provide:

	public	Version
	public	QueryExtMemory
	public	AllocExtMemory
	public	FreeExtMemory
	public	LockExtMemory
	public	UnlockExtMemory
	public	GetExtMemoryInfo
	public	ReallocExtMemory
	public	end_of_hiseg
	public	textseg
	public	KiddValley
	public	KiddValleyTop
	public	cHandles
	public	RequestUMB
	public	ReleaseUMB

;	externals from himem.asm

	extrn	PrevInt15:dword
	extrn	Moveit:word
	extrn	fHMAMayExist:byte
	extrn	fHMAExists:byte
	extrn	winbug_fix:word
	extrn	FLclEnblA20:far
	extrn	FLclDsblA20:far
_text	ends

funky	segment	word public 'funky'
	assume	cs:funky,ds:_text

	extrn	end_of_funky_seg:near

		org	HISEG_ORG

	public	LEnblA20
	public	LDsblA20

end_of_hiseg	dw	end_of_funky_seg
textseg		dw	_text
KiddValley	dw	end_of_funky_seg; The address of the handle table
KiddValleyTop	dw	0	; end of handle table
cHandles	dw	DEFHANDLES ; number of handles
LEnblA20	dd	_text:FLclEnblA20
LDsblA20	dd	_text:FLclDsblA20
;*----------------------------------------------------------------------*
;*									*
;*  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    far

	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     VHooked
	mov	dl,[fHMAMayExist]		; No, return the status at
	ret					;  init time.

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

Version	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 far
        
        mov     bl,0                    ; assume no error
        XMSSVC  XMS_QUERYEXTMEM
        test    dx,dx
        jne     QEM20
        
        mov     bl,ERR_OUTOMEMORY
QEM20:          
        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				*
;*									*
;*	Notice:  called internally from ReallocExtMemory		*
;*									*
;*  INTERNALLY NON-REENTRANT						*
;*									*
;*----------------------------------------------------------------------*

hFreeBlock	dw  ?
hUnusedBlock	dw  ?

AllocExtMemoryNear proc near
AllocExtMemoryNear endp
AllocExtMemory proc far

        call     FunkyCLI              ; This is a non-reentrant function

; Scan the handle table looking for an unused handle 

	xor	ax,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     [bx].Flags,UNUSEDFLAG ; Is this block unused?
	jne     AEMNexth		; No, get the next handle

	mov	[hUnusedBlock],bx	; save this guy away
        jmp     AEMGotHandle
        
AEMNexth:
	add	bx,SIZE Handle		; go check the next handle
	loop    AEMhLoop

;	We are at the end of the handle table and we didn't an unused 
;       handle

	jmp	AEMOOHandles		; No, Case 4 - We're out of handles

AEMGotHandle:

	mov	di,[hUnusedBlock]

	XMSSVC	XMS_ALLOCBLOCK		; ax=Base and dx=Size
	or	ax,ax
	jz	AEMOOMemory

	mov	[di].Base,ax
	mov	[di].Len,dx
	mov	[di].Flags,USEDFLAG	; New.Flags = USED
 
	if	keep_cs
	mov	ax,callers_cs
	mov	[si].Acs,ax		; keep track of allocator's cs:
	endif

	mov	dx,[hUnusedBlock]
	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

;*----------------------------------------------------------------------*
;*									*
;*  FreeExtMemory -					FUNCTION 0Ah    *
;*									*
;*	Frees 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, CX, DX, SI, DI and Flags clobbered			*
;*									*
;*	called internally from ReallocExtMemory				*
;*									*
;*  INTERNALLY NON-REENTRANT						*
;*									*
;*----------------------------------------------------------------------*

FreeExtMemoryNear proc near
FreeExtMemoryNear endp

FreeExtMemory proc far
        call    FunkyCLI                ; This is a non-reentrant function

	call    ValidateHandle		; Make sure handle is valid
	jnc	FEMBadh
	mov	si,dx			; Move the handle into SI

	cmp	[si].cLock,0		; make sure it points to unlocked block
	jne     FEMLockedh

	mov	[si].Flags,UNUSEDFLAG	;  mark it as UNUSED
	cmp	[si].Len,0		; if zero length block
	jz	FEMExit			; done if it was zero length

	mov	ax,[si].Base
	mov	dx,[si].Len
	XMSSVC	XMS_FREEBLOCK		; ax=base dx=size in k
	or	ax,ax
	je	FEMBadh

FEMExit:

	mov     ax,1			; Return success
	xor	bl,bl
	ret

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 far

        call    FunkyCLI                ; This is a non-reentrant function

	call    ValidateHandle		; Is the handle valid?
	jnc     LEMBadh
	mov     bx,dx			; Move the handle into BX

;	Are we at some preposterously large limit?

	cmp	[bx].cLock,0FFh
	je	LEMOverflow

	inc	[bx].cLock		; lock the block

	mov	dx,[bx].Base		; return the 32-bit address of base
	mov	bx,dx
	shr	dx,6
	shl	bx,10

	mov	ax,1			; return success
	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 far

        call    FunkyCLI                ; This is a non-reentrant function

	call    ValidateHandle		; Is the handle valid?
	jnc     UEMBadh
	mov	bx,dx			; Move the handle into BX

	cmp	[bx].cLock,0		; is handle locked?
	je	UEMUnlocked		; No, return error

	dec	[bx].cLock		; Unlock the block

	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	far

        call    FunkyCLI        ; This is a non-reentrant function

	call    ValidateHandle	; is the handle valid?
	jnc     GEMBadh
	mov     si,dx		; Move the handle into SI

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

	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		*
;*	    BX = new size for block					*
;*  RETS:   AX = 1 if successful, 0 otherwise.	BL = Error code		*
;*  REGS:   trashes si,di,bx,cx,dx					*
;*									*
;*  INTERNALLY NON-REENTRANT						*
;*									*
;*----------------------------------------------------------------------*

;	Define our memory move structure for calling the move function

ReallocExtMemory	proc	far

        call    FunkyCLI        ; This is a non-reentrant function
        
	call    ValidateHandle	; is the handle valid?
 	mov	si,dx		; Move the handle into SI
	mov	dx,bx		; Move the new length into dx
	mov	bl,ERR_INVALIDHANDLE
	jnc     REMError

	cmp	[si].cLock,0	; We can only work on unlocked EMBs
	mov	bl,ERR_EMBLOCKED
	jnz	REMError

        mov     bx,dx
        cmp     [si].Len,bx
        je      REMExit
        
        mov     ax,[si].Base
        mov     dx,[si].Len
        XMSSVC  XMS_REALLOCBLOCK        ; ax = old base, dx = old size 
        cmp     cx,0                    ; cx = new base, bx = new size
        je      REM20
        
        mov     [si].Base,cx
        mov     [si].Len,bx
REMExit:
	mov	ax,1			; succesful return
	xor	bl,bl			; non-documented no-error return
	ret
 
REM20: 
        mov     bl,ERR_OUTOMEMORY
REMError:
	xor	ax,ax
	ret
 
ReallocExtMemory	endp


;*----------------------------------------------------------------------*
;*									*
;*  FindAdjacent unused blocks						*
;*									*
;*	Scan through handle list looking for blocks adjacent		*
;*	  to a given handle.						*
;*									*
;*  ARGS:   SI handle of original block					*
;*  RETS:   DI = handle of adjacent block below or zero if none		*
;*	BP = handle of adjacent block above or zero if none		*
;*									*
;*  TRASHES: AX,BX,CX,DX						*
;*									*
;*  messes with handle table - not reentrant - assumes ints disabled	*
;*									*
;*----------------------------------------------------------------------*

FindAdjacent	proc	near

	mov	ax,[si].Base		; look for blocks ending here
	mov	dx,[si].Len
	add	dx,ax			; and ending here

	xor	di,di			; initialize to fail condition
	mov	bp,di

	mov	bx,[KiddValley]		; prepare to loop thru handle tab
	mov	cx,[cHandles]

	push    si			; preserve original handle

FindAdj1:
	cmp	[bx].Flags,FREEFLAG
	jnz	FindAdj3		; ignore blocks that aren't UNUSED
	mov	si,[bx].Base
	cmp	dx,si			; found beg block?
	jnz	FindAdj2		;  skip if not
	mov	bp,bx			;  remember the handle
	or	di,di			; did we already find a follower?
	jnz	FindAdj9		;  we're done if so

FindAdj2:
	add	si,[bx].Len		; find length
	cmp	si,ax			; does this block end at spec addr?
	jnz	FindAdj3		;  skip if not
	mov	di,bx			;  remember the handle
	or	bp,bp			; did we already find a leader?
	jnz	FindAdj9		;  we're done if so

FindAdj3:
	add	bx,SIZE handle
	loop    FindAdj1

FindAdj9:
	pop	si			; restore original handle
	ret
;

FindAdjacent	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	VHOne

;	The handle must not be above "KiddValleyTop".

	cmp	bx,[KiddValleyTop]
	ja	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     VHOne 		; Yup, it's bad

;	Does the handle point to a currently USED block?

	cmp	[bx].Flags,USEDFLAG
	jne     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

BlkMovX	proc	near
	assume	ds:_text
	jmp	MoveIt
BlkMovX	endp

;-----------------------------------------------------------------------;
;This is the routine  for XMS function 16, request UMB.
;
; Input:
;	(DX) = requested size in paragraphs
;
; Output:
;	(AX) = 1 if request is granted and
;		 (BX) = segment address of the requested block
;		 (DX) = actual size allocated in paragraphs
;	     = 0 if requtest failed and
;		 (BL) = 0B0h if a smaller UMB is available
;		 (BL) = 0B1h if no UMBs are available
;		 (DX) = largest UMB available.
;Modified: AX, BX, DX
;
;NOTE:
;The funcition was implemented in the 32bits xms because
;any memory we need for house keeping purpose are kept in extented memory
;rather than in the UMB itself(DOS arena). Of course, it has the penalty
;of ring transition each time a request being made. However, the major
;allocator of UMBs is IO.SYS, the device driver of MS-DOS for devicehigh
;and loadhigh during bootstrap. This should adjust the penalty a little bits.
;
;-----------------------------------------------------------------------;
RequestUMB	proc	far
	XMSSVC	XMS_REQUESTUMB
	ret
RequestUMB	endp


;-----------------------------------------------------------------------;
;This is the routine for XMS function 17, release UMB
;
; Input:
;	(DX) = segment of the UMB block to be released
;
; Output:
;	(AX) = 1 if the block was released successfully.
;	(AX) = 0 if the block couldn't be released and
;		 (BL) = 0B2h if the given segment is invalid
; Modified: AX, BX
;
;Note:
; See note in RequestUMB
;-----------------------------------------------------------------------;

ReleaseUMB	proc	far
	XMSSVC	XMS_RELEASEUMB
	ret
ReleaseUMB	endp

        PUBLIC FunkyCLI
FunkyCLI:
        FCLI
        ret

        PUBLIC FunkySTI
FunkySTI:
        FSTI
        ret

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

funky	ends
	end

