	PAGE	,132
	TITLE	GALLOC - Global memory allocator

.sall
.xlist
include kernel.inc
include protect.inc
.list

.386

DataBegin

externB  Kernel_flags
externB  fCheckFree
externW  pGlobalHeap
externW  Win386_Blocks
externW  FreeArenaCount
	 
gsearch_state_machine	dw	0

DataEnd

sBegin	CODE
assumes CS,CODE

externNP gcompact
externNP gmovebusy
externNP gslide
externNP galign
externNP gwin386discard
externNP GetDPMIFreeSpace
externNP InnerShrinkHeap
	  
externNP get_physical_address
externNP set_physical_address
externNP alloc_arena_header
externNP free_arena_header
ife PMODE32
externNP get_blotto
endif
externNP PreallocArena
externNP DPMIProc

;-----------------------------------------------------------------------;
; gsearch								;
;									;
; Searches from start to finish for a free global object to allocate	;
; space from.  For a fixed request it tries to get the space as low as	;
; possible, moving movable out of the way if neccessary.  For movable	;
; requests it also starts at the bottom.  For discardable code it	;
; starts from the top, only using the first free block it finds.	;
;  If at first blush it can't find a block it compacts memory and tries	;
; again.								;
;  When it finally finds a block it gsplices it in, makes sure the	;
; arena	headers are fine, and returns the allocated block.		;
;  Called from within the global memory manager's critical section.	;
;									;
; Arguments:								;
;	AX = allocations flags						;
;	EBX = #bytes							;
;	CX = owner field value						;
;	DS:DI = address of global arena information structure		;
;									;
; Returns:								;
;	AX = data address of block allocated or NULL			;
;	BX = ga_prev or ga_next						;
;	DX = allocation flags						;
;									;
; Error Returns:							;
;	ZF = 1								;
;	AX = 0								;
;	BX = ga_prev or ga_next						;
;	DX = size of largest free block					;
;									;
; Registers Preserved:							;
;	DI,DS								;
;									;
; Registers Destroyed:							;
;	CX,SI,ES							;
;									;
; Calls:								;
;	galign								;
;	gfindfree							;
;	fmovebusy							;
;	gcheckfree							;
;	gcompact							;
;	gsplice								;
;	gzero								;
;	gmarkfree							;
;									;
; History:								;
;									;
;  Wed Jul 22, 1987 11:15:19p  -by-  David N. Weise  [davidw]		;
; Fixed BOGUS BLOCK freeing yet again.					;
; 									;
;  Sun May 10, 1987 11:29:38p  -by-  David N. Weise  [davidw]		;
; Added the state machine to handle the case of wanting to search	;
; both global arenas.							;
; 									;
;  Sat Feb 28, 1987 06:31:11p  -by-  David N. Weise   [davidw]		;
; Putting in support for allocating discardable code from EEMS land.	;
; 									;
;  Tue Dec 30, 1986 01:54:50p  -by-  David N. Weise   [davidw]		;
; Made sure it freed any bogus blocks created.				;
; 									;
;  Thu Nov 20, 1986 04:00:06p  -by-  David N. Weise   [davidw]		;
; Rewrote it use the global free list.  Also made it put fixed requests	;
; as low as possible and to search again after a compact.		;
; 									;
;  Mon Nov 17, 1986 11:41:49a  -by-  David N. Weise   [davidw]		;
; Added support for the free list of global partitions.			;
; 									;
;  Tue Sep 23, 1986 04:35:39p  -by-  David N. Weise   [davidw]		;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

	assumes ds,nothing
	assumes es,nothing

cProc	gsearch,<PUBLIC,NEAR>
cBegin nogen

	CheckKernelDS	fs
	ReSetKernelDS	fs

	mov	gsearch_state_machine,codeOFFSET grow_heap

look_again:
	push	ebx			; Save requested size
	push	cx			; Save owner
	push	ax			; Save flags
	clc				; Instead of add
	call	galign			; Get requested size in EDX
	push	edx			; Save adjusted requested size

; see if there is any one free space large enough for the request first

	mov	cx,[di].gi_free_count
	jcxz	were_hosed_from_start
	mov	esi,[di].phi_first
is_there_one:
	mov	esi,ds:[esi].pga_freenext
	cmp	edx,ds:[esi].pga_size
	jbe	short got_one
	loop	is_there_one
were_hosed_from_start:
	jmp	space_not_found

got_one:
	mov	ebx,pga_prev		; search backwards
	test	al,GA_ALLOCHIGH
	jz	short alloc_low

;------ allocate disc code -------

	public	alloc_high
alloc_high:
	mov	cx,[di].gi_free_count
	mov	esi,[di].phi_last		; Start with last entry.
alloc_high_loop:
	mov	esi,ds:[esi].pga_freeprev
	cmp	edx,ds:[esi].pga_size
	jbe	afound			; Yes, exit search loop
	loop	alloc_high_loop
	jmp	space_not_found
	   

;------ allocate moveable ---------

	public	alloc_low
alloc_low:
	mov	ebx,pga_next		; Search forwards.
	test	al,GA_MOVEABLE
	jz	short alloc_fixed
	call	gcheckfree		; Room for requested size?
	jb	short space_not_found
	jmp	afound

;------ allocate fixed ------------

	public	alloc_fixed
alloc_fixed:
	mov	esi,[di].phi_first	; Start with first entry.
	mov	cx,[di].hi_count
	mov	esi,ds:[esi+ebx]	; Skip first sentinel
alloc_fixed_loop:
	push	cx
	push	esi
	call	is_there_theoretically_enough_space
	cmp	eax,edx
	jb	short nope
	pop	esi
	pop	cx
	call	can_we_clear_this_space
	jz	short anext1
	call	gcheckfree		; Yes, room for requested size?
	jb	short anext1
					; Now we have enough free space,
					; slide it back as far as possible.
	push	eax			; This is to prevent us blocking
	push	edx			; the growth of moveable blocks.
	push	ebx			; AND to keep pagelocked code together
	call	PreallocArena
	jz	short no_sliding

	mov	ebx, pga_prev		; Sliding backwards
keep_sliding:
	call	gslide
	jnz	keep_sliding

no_sliding:
	pop	ebx
	pop	edx
	pop	eax

	pop	edx
	pop	cx
	test	ch, GA_ALLOC_DOS
	push	cx
	push	edx
	jz	afound
	cmp	ds:[esi].pga_address, 100000h	; > 1Mb?
	jb	afound
	jmps	gsearch_fail

nope:
	or	eax,eax
	jz	short hosed_again
anext:
	add	sp, 6			; get rid of CX, ESI on the stack
anext1:
	mov	esi,ds:[esi+ebx]
	loop	alloc_fixed_loop
	jmps	and_again

hosed_again:
	pop	esi
	pop	cx
and_again:

; no one space big enough, try compacting

	public	space_not_found
space_not_found:
	pop	edx			; get adjusted size
	pop	ax			; get flags
	push	ax
	push	edx
;	test	al,GA_ALLOCHIGH
;	jnz	short ask_for_what_we_need
;	add	edx,0400h		; ask for 1k more
;	jnc	short ask_for_what_we_need	; no overflow
;	mov	edx,-1
ask_for_what_we_need:

	jmp	gsearch_state_machine
		
;------------------------------

public	do_compact			; for debugging

do_compact:
	mov	gsearch_state_machine,codeOFFSET gsearch_fail
	call	gcompact

over_compact:
	pop	edx
	pop	ax
	pop	cx
	pop	ebx
	jmp	look_again

public	grow_heap			; for debugging

grow_heap:
	mov	gsearch_state_machine,codeOFFSET do_compact

	push	edx			; can we get the space from DPMI?
	call	GrowHeap
	pop	edx
	jnc	short over_compact

	call	InnerShrinkHeap 	; try to give back DPMI blocks so DPMI
	jz	short do_compact	;   mmgr can defragment its memory

	push	edx			; gave some back, try to get it again
	call	GrowHeap
	pop	edx
	jnc	over_compact
doomed:

;------------------------------

public	gsearch_fail			; for debugging

gsearch_fail:				; get size of largest free block
	.errnz	doomed-gsearch_fail
	xor	edx,edx
	mov	cx,[di].gi_free_count
	jcxz	gs_failure
	mov	esi,[di].phi_first
largest_loop:
	mov	esi,ds:[esi].pga_freenext
	mov	eax,ds:[esi].pga_size
	cmp	edx,eax
	jae	short new_smaller
	mov	edx,eax
new_smaller:
	loop	largest_loop
gs_failure:
	pop	eax			; adjusted requested size
	pop	ax			; AX = flags

	pop	cx			; CX = owner field
	pop	eax			; waste requested size
	xor	eax,eax			; Return zero, with ZF = 1
	ret

; Here when we have a block big enough.
;   ES:DI = address of block
;   AX = size of block, including header
;   DX = requested size, including header
;   BX = ga_prev if backwards search and ga_next if forwards search

afound:
	mov	ecx,ds:[esi].pga_size	; Use actual size of free block
	sub	ecx,edx			; (found size - requested size)
	jecxz	no_arena_needed
	call	PreallocArena
	jnz	short no_arena_needed

;
; Try to detect infinite loop here.  If we are out of arenas, and
; the compact failed once, then another one isn't going to do much
; good.
;
	cmp	gsearch_state_machine,codeOFFSET gsearch_fail
	je	gsearch_fail

	pop	edx			; get adjusted size
	pop	ax			; get flags
	push	ax
	push	edx
	and	ds:[di].gi_cmpflags, NOT (GA_NODISCARD+GA_NOCOMPACT)
	or	ds:[di].gi_cmpflags, COMPACT_ALLOC
	mov	edx, -1			; Discard the world!
	jmp	do_compact

no_arena_needed:
	mov	eax,ds:[esi].pga_freeprev
	call	gdel_free		; remove the alloc block from freelist
	jecxz	aexitx
	cmp	bl,pga_prev		; Yes, scanning forwards or backwards?
	je	short abackward		; Backwards.
	call	gsplice			; FS:ESI = block we are allocating
	jmps	aexit			; EDX = block to mark as free
abackward:
	neg	edx
	add	edx,ds:[esi].pga_size	; Scanning backwards.  Put extra space
	call	gsplice
	xchg	edx, esi
	jmps	aexit

; Here with allocated block
;   AX = data address or zero if nothing allocated
;   ES:DI = address of block to mark as busy and zero init if requested
;   EDX = address of block to mark as free

aexitx:
	xor	edx,edx			; Assume nothing extra to free
aexit:
	pop	ecx			; waste adjusted requested size
	pop	cx			; Restore flags
	pop	ds:[esi].pga_owner	; Mark block as busy with owner field value
	add	sp, 4			; waste requested size
	mov	ds:[esi].pga_lruprev,edi
	mov	ds:[esi].pga_lrunext,edi

	push	esi
	mov	esi, edx		; Free any extra space
	mov	edx, eax		; Previous free block
	call	gmarkfree
	pop	esi

	mov	dx, cx
	mov	al,GA_SEGTYPE
	and	al,dl
	test	dh,GAH_NOTIFY
	jz	short no_notify
	or	al,GAH_NOTIFY
no_notify:
	mov	ds:[esi].pga_flags,al	; Store segment type bits
	mov	eax,esi			; AX = address of client data

	test	cl,GA_ZEROINIT		; Want it zeroed?
	jz	short aexit1		; No, all done

	push	eax
ifdef WOW
;; On NT we try never to set selectors when we don't need to since it is a
;; slow operation - a system call.  In this case we can use selector 23h
;; which points to all flat vdm memory as data
	push	es
	push	bx
	push	edi

	mov	bx,FLAT_SEL
	mov	es,bx

	mov	ecx,ds:[esi].pga_size	; Yes, zero paragraphs
	push	ecx

	shr	ecx, 2			; # dwords to clear
	mov	edi, ds:[eax].pga_address
	xor	eax, eax

	cld
	rep	stos	dword ptr es:[edi]

	pop	ecx
	pop	edi
	pop	bx
	pop	es
else
	cCall	get_blotto
	mov	ecx,ds:[esi].pga_size	; Yes, zero paragraphs
	push	bx
	mov	bx,ax			; from beginning of client data
	call	gzero			; zero them
	pop	bx
endif; WOW
	pop	eax
aexit1:

	or	eax,eax
	ret				; Return AX points to client portion
	UnSetKernelDS	FS		; of block allocated.
cEnd nogen		  

cProc	GrowHeap,<PUBLIC,NEAR>
cBegin nogen
	CheckKernelDS	FS
	ReSetKernelDS	fs
	cmp	FreeArenaCount, 4	; 3 for below, 1 for a gsplice later
	jb	gh_fail

	pushad	      
	push	edx			; Save requested size
					; If they want more than 64k, assume they
					; want a big block and just allocate that amount.
	cmp	edx, 64*1024		; Want more than 64k?
	jae	short ask_for_it	;   yes, just round up
	mov	edx, 128*1024		;   no, try for 128k
ask_for_it:	     					   
	mov	ebx, edx
	add	ebx, 4096-1		; Round up to 4k multiple
	and	bx, NOT (4096-1)

	push	ebx			; Length we will ask for
	mov	cx, bx
	shr	ebx, 16
	DPMICALL 0501h			; Allocate Memory Block
					; Get our memory
	jnc	short got_more_memory
	pop	ebx			; Toss length we asked for

					; Couldn't get our 1st choice, how
	call	GetDPMIFreeSpace	;   much is available
	mov	ebx, eax		;   ebx = largest available

	pop	edx			; Requested size
	cmp	ebx, edx		; Enough for request?
	jbe	SHORT gh_fail_pop

	push	edx			; Expected on stack below
	push	ebx			; Length we will ask for
	mov	cx, bx
	shr	ebx, 16
	DPMICALL 0501h			; Allocate Memory Block
					; Get our memory
	jnc	short got_more_memory
	add	sp, 8			; Toss requested size & len asked for

gh_fail_pop:
	popad
gh_fail:
	stc				; No chance mate!
	ret
					; Now we have a new block
					; Sort it in to the heap
					; Create NOT THERE blocks to
got_more_memory:			; bracket the new block
	inc	Win386_Blocks
	pop	edx			; Length of block allocated
if KDEBUG
	mov	eax, edx
	shr	eax, 16
	krDebugOut <DEB_TRACE OR DEB_KrMemMan>, "GrowHeap: #ax#DX allocated"
endif
	shl	ebx, 16
	mov	bx, cx			; EBX has linear address
	pop	ecx			; Toss original length
	shl	esi, 16
	mov	si, di			; ESI has WIN386 handle
	xor	edi, edi
	cCall	alloc_arena_header,<ebx>
	mov	ecx, eax		; First not there arena
	mov	[ecx].pga_size, edi
	mov	[ecx].pga_sig, GA_SIGNATURE
	mov	[ecx].pga_owner, GA_NOT_THERE
	mov	[ecx].pga_handle, di
	mov	[ecx].pga_flags, di
	mov	[ecx].pga_lrunext, esi	; Save WIN386 handle here
	mov	[ecx].pga_lruprev, edi
	cCall	alloc_arena_header,<ebx>
	mov	[eax].pga_size, edx	; Free block
	push	ebx			; Save address
	add	edx, ebx		; Address of end of block
	mov	ebx, eax
	mov	[ecx].pga_next, ebx
	mov	[ebx].pga_prev, ecx
	mov	[ebx].pga_owner, di
	cCall	alloc_arena_header,<edx>
	mov	edx, eax
	mov	[ebx].pga_next, edx
	mov	[edx].pga_prev, ebx
	mov	[edx].pga_size, edi
	mov	[edx].pga_owner, GA_NOT_THERE
	mov	[edx].pga_handle, di
	mov	[edx].pga_flags, di
	mov	[edx].pga_sig, GA_SIGNATURE
	mov	[edx].pga_lrunext, edi
	mov	[edx].pga_lruprev, edi

	pop	eax			; Address of block
sort_it:
	mov	esi, [edi].phi_first
	cmp	eax, [esi].pga_address	; Below start of heap?
	ja	short sort_loop		;   no, sort it in
;	int	3			; [this code never reached]
	mov	[esi].pga_address, eax	;   yes, adjust sentinel
	jmps	link_it_in		; Sentinel now points to new block

sort_loop:
	mov	esi, [esi].pga_next
	cmp	[esi].pga_next, esi	; At end?
	je	short sort_found	;   yes, put here
	cmp	[esi].pga_owner, GA_NOT_THERE
	jne	short sort_loop
	mov	esi, [esi].pga_prev	; Will go after previous block.

sort_found:				; Block will go after ESI
	cmp	[esi].pga_next, esi	; This the sentinel?
	jne	short link_it_in	;   no, link it in
	mov	eax, [edx].pga_address	;   yes, adjust sentinel
	mov	[esi].pga_address, eax
	sub	eax, [di].gi_reserve	; Adjust fence
	mov	[di].gi_disfence_lo, ax
	shr	eax, 16
	mov	[di].gi_disfence_hi, ax
	mov	esi, [esi].pga_prev	; New block goes before sentinel

link_it_in:				; Link it in after ESI
	mov	[ecx].pga_prev, esi
	xchg	[esi].pga_next, ecx
	mov	[edx].pga_next, ecx
	mov	[ecx].pga_prev, edx

	add	[di].hi_count, 3	; Three more entries in heap
	mov	esi, ebx
	xor	edx, edx
	call	gmarkfree		; To be picked up next time around
	popad
	clc
	ret
	UnSetKernelDS	FS
cEnd nogen

;-----------------------------------------------------------------------;
; is_there_theoretically_enough_space
;
; Starting at the given arena checks to see if there are enough
;  continuous free and unlocked moveable blocks.
;
; Entry:
;	CX = arenas to search
;	EDX = size requested
;	DS = BurgerMaster
;	FS:ESI = arena to start from
;
; Returns:
;	EAX = 0 =>  not enough space and no more memory left to search
;	EAX = 1 =>  not enough space in this block, but maybe....
;	 otherwise
;	EAX = size of block
;
; Registers Destroyed:
;	CX,ESI
;
; Registers Preserved:
;	BX,DX,DI,ES
;
; History:
;  Mon 05-Sep-1988 15:21:14  -by-  David N. Weise  [davidw]
; Moved it here from gsearch so that grealloc could use it as well.
;-----------------------------------------------------------------------;

	assumes ds,nothing
	assumes es,nothing

cProc	is_there_theoretically_enough_space,<PUBLIC,NEAR>
cBegin nogen

	xor	eax,eax
ittes:
	cmp	ds:[esi].pga_owner,di
	jne	short is_it_moveable
	add	eax,ds:[esi].pga_size
	push	ebx
	push	eax
	mov	bx,word ptr [di].gi_disfence_hi ; See if begin of reserve area
	shl	ebx, 16
	mov	bx,word ptr [di].gi_disfence_lo ;  is above end of free block
	mov	eax, ds:[esi].pga_address
	add	eax, ds:[esi].pga_size		; Address of end of free block
	sub	eax,ebx	 	  
	ja	short ittes_above_fence	; All below fence?
ittes_below_fence:
	pop	eax			;  yes, we can use it
	pop	ebx
	jmps	this_ones_free

ittes_above_fence:
	cmp	eax, ds:[di].gi_reserve
	jae	ittes_below_fence
	mov	ebx, eax		; portion above the fence
	pop	eax			; Total size so far
	sub	eax,ebx			; No, Reduce apparent size of free block
	pop	ebx
	cmp	eax,edx
	jae	short theoretically_enough
	jmps	absolutely_not

is_it_moveable:
	cmp	ds:[esi].pga_owner,GA_NOT_THERE	; Against end of heap partition?
	je	short theoretically_not		;   no room here.
	test	ds:[esi].pga_handle,GA_FIXED	; See if movable.
	jnz	short theoretically_not
	cmp	ds:[esi].pga_count,0
	jne	short theoretically_not		; See if locked.
	add	eax,ds:[esi].pga_size
this_ones_free:
	cmp	eax,edx
	jae	short theoretically_enough
	mov	esi,ds:[esi].pga_next
	loop	ittes

; For the case of gsearch we should never get here for two reasons.
;  1) It should be impossible to have no discardable code loaded, in
;  this case we would have failed in the above loop.  2) We checked
;  at the very start for a free block somewhere that could have
;  satisfied the request.  In our mucking around to load as low as
;  possible we destroyed this free block and we did not produce a free
;  block we could use.	However we know from debugging code in 2.03
;  that this happens extremely rarely.	Because of the rareness of
;  this event we will not try to recover, instead we simply fail the call.

absolutely_not:
	mov	eax,-1			; return EAX = 0
theoretically_not:
	inc	eax			; DX is even, => cmp the same.
theoretically_enough:
	ret
cEnd nogen



;-----------------------------------------------------------------------;
; can_we_clear_this_space
;
; Attempts to make a free space starting at the address moved in.
; To do this it moves moveable out of the area.  The only subtlety
; involves a free block that stradles the end of wanted area.  This
; may get broken into two pieces, the lower piece gets temporary marked
; as allocated and BOGUS, the upper piece will remain free.
;
; Entry:
;	CX = max number of blocks to look at
;	EDX = size wanted in bytes
;	DS = BurgerMaster
;	FS:ESI = beginning arena
;
; Returns:
;	ZF = 0
;	 FS:ESI points to free space
;	ZF = 1
;	 couldn't free the space up
;
; Registers Destroyed:
;	AX,SI
;
; History:
;  Mon 05-Sep-1988 16:48:31  -by-  David N. Weise  [davidw]
; Moved it out of gsearch so that grealloc could use it.
;-----------------------------------------------------------------------;

	assumes ds,nothing
	assumes es,nothing

cProc	can_we_clear_this_space,<PUBLIC,NEAR>
cBegin nogen
	CheckKernelDS	FS
	ReSetKernelDS	FS
	
	push	ecx
	push	esi
	cmp	di,[di].gi_free_count	; any free blocks?
	jz	short cwcts_fail
	mov	eax, esi		; Beginning of space we want.
	cmp	di,ds:[esi].pga_owner	; Is it free?
	jnz	short can_we_move_it
	mov	ecx,edx
	cmp	ecx,ds:[esi].pga_size
	ja	short asdf
	or	eax,eax			; return ZF = 0
cwcts_fail:
	pop	esi
	pop	ecx
	ret

asdf:	mov	esi,ds:[esi].pga_next

	public can_we_move_it
can_we_move_it:
	push	ebx
	push	ecx
	push	edx
	push	esi
	cmp	ds:[esi].pga_owner,GA_BOGUS_BLOCK
	jnz	short not_bogus
	xor	edx,edx
	call	gmarkfree
	jmp	restart
not_bogus:
	mov	ebx, ds:[eax].pga_address
	add	ebx, edx		; EBX is the end of the space we want
	mov	cx,[di].gi_free_count
	mov	edx,ds:[esi].pga_size	; Yes, try to find a place for the
	mov	esi,[di].phi_first	;  moveable block
look_loop:
	call	PreAllocArena		; Needed for gmovebusy or gsplice
	jz	couldnt_clear_it
	mov	esi,ds:[esi].pga_freenext
	push	esi
	mov	esi, ds:[esi].pga_address
	cmp	esi, ds:[eax].pga_address ; It defeats our purpose to move the
	jb	short check_this_out	  ;  block to a free space we want.
	cmp	esi, ebx
	jb	short is_there_hope
check_this_out:
	pop	esi
	push	eax
	call	gcheckfree
	push	ecx
	jb	short inopportune_free_space
	pop	ecx
	pop	eax

	pop	edx			; EDX = moveable block for gmovebusy
	mov	ebx,pga_next
	call	gmovebusy		; Move moveable block out of the way
	mov	esi,edx			; Replace the ESI on the stack,
					;  the free block may have grown
					;  downward with the gmovebusy.
	pop	edx
	pop	ecx
	pop	ebx
	pop	ecx			; WAS pop esi but esi set above now
	pop	ecx
	jmp	can_we_clear_this_space

inopportune_free_space:
	pop	ecx
	pop	eax
	loop	look_loop
	jmps	couldnt_clear_it

	public is_there_hope
is_there_hope:
	pop	esi
	push	eax
	push	ecx

	mov	ecx, ds:[esi].pga_address
	add	ecx, ds:[esi].pga_size		; ECX end of block
			 	  
	mov	ax, [di].gi_disfence_hi
	shl	eax, 16
	mov	ax, [di].gi_disfence_lo		; EAX == fence

	sub	eax, ecx			; Fence - End
	jae	short below_reserved		; Block is below fence
	neg	eax				; End - Fence
	cmp	eax, ds:[di].gi_reserve
	jae	short below_reserved		; Block is above reserved
	sub	ecx, eax			; End - (End - Fence)
						; Gives Fence in ECX
below_reserved:
	sub	ecx, ebx			; Adjust size of free block
	jbe	inopportune_free_space		; No room here

overlap:
	cmp	ecx,edx				; Is it big enough?
	jbe	short inopportune_free_space

	mov	edx, ebx
	sub	edx, ds:[esi].pga_address	; Calculate overlap

	pop	ecx
	pop	eax

; cut off the first piece for the original alloc

	push	ds:[esi].pga_freeprev
	call	gdel_free
	call	gsplice

; DS:ESI = addr of block to mark as busy, FS:EDX = addr of block to mark as free

	mov	ds:[esi].pga_owner,GA_BOGUS_BLOCK
	mov	ds:[esi].pga_lruprev,edi
	mov	ds:[esi].pga_lrunext,edi
	mov	esi, edx
	pop	edx			; previous free block
	call	gmarkfree		; Free any extra space
restart:
	pop	edx			; WAS pop es
	pop	edx
	pop	ecx
	pop	ebx
	pop	esi
	pop	ecx
	jmp	can_we_clear_this_space

; If here then failure! see if we made a bogus block!

couldnt_clear_it:
	pop	esi			; recover block we wanted moved
check_again:
	mov	edx,ds:[esi].pga_next
	cmp	edx, ds:[edx].pga_next	  ; At end of arenas?
	je	short no_bogus_block	  ;   Yes, let's go
	cmp	ds:[edx].pga_address, ebx ; EBX points to where bogus block
	ja	short no_bogus_block	  ;   would be.
	je	short is_it_bogus
	mov	esi,edx
	jmps	check_again
is_it_bogus:
	cmp	ds:[esi].pga_owner,GA_BOGUS_BLOCK
	jnz	short no_bogus_block
	xor	edx,edx
	call	gmarkfree
no_bogus_block:

if KDEBUG
	mov	cx,[di].hi_count
	mov	esi,[di].phi_first
bogus_all:
	cmp	ds:[esi].pga_owner,GA_BOGUS_BLOCK
	jnz	short not_me
	int	3
not_me:	mov	esi,ds:[esi].pga_next
	loop	bogus_all
endif
	pop	edx
	pop	ecx
	pop	ebx
	pop	esi
	pop	ecx
	xor	eax,eax			; return ZF = 1
	ret
	UnSetKernelDS	FS
cEnd nogen

;-----------------------------------------------------------------------;
; gcheckfree								;
;									;
; Checks the size of the passed free block against the passed desired	;
; size, making sure that the limitations of the code reserve area are	;
; not violated for objects other than discardable code.	It also checks	;
; for Phantoms.								;
;									;
; Arguments:								;
;	FS:ESI = address of free block					;
;	DX = #bytes needed						;
;	DS:DI = address of global arena information structure		;
;									;
; Returns:								;
;	CF = 0 block big enough						;
;	EAX = apparent size of free block				;
;									;
; Error Returns:							;
;	none								;
;									;
; Registers Preserved:							;
;	All								;
;									;
; Registers Destroyed:							;
;									;
; Calls:								;
;	nothing								;
;									;
; History:								;
;									;
;  Thu 27-Apr-1989  10:38:05   -by-  David N. Weise   [davidw]		;
; Fixed this to work in pmode.						;
;									;
;  Thu Apr 02, 1987 10:45:22p  -by-  David N. Weise   [davidw]		;
; Added Phantom support.						;
; 									;
;  Tue Sep 23, 1986 05:54:51p  -by-  David N. Weise   [davidw]		;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

cProc	gcheckfree,<PUBLIC,NEAR>
cBegin nogen
	mov	eax,ds:[esi].pga_size	; Compute size of free block
	test	byte ptr [di].gi_cmpflags,GA_DISCCODE
	jnz	short gcftest		; Discardable code not restricted

	push	ebx
	mov	ebx, [di].phi_last		; Last sentinel
	mov	ebx, ds:[ebx].pga_address
	cmp	ebx, ds:[esi].pga_address	; Above sentinel?
	jbe	short gcftest1			;  yes, not in reserved area!
	 
	push	eax  
	mov	bx,word ptr [di].gi_disfence_hi ; See if begin of reserve area
	shl	ebx, 16
	mov	bx,word ptr [di].gi_disfence_lo ;  is above end of free block
	add	eax, ds:[esi].pga_address	; EAX address of end of block
	sub	ebx, eax
	pop	eax  
	jae	short gcftest1		; Yes, return actual size of free block
	neg	ebx
	sub	eax,ebx			; No, Reduce apparent size of free block
	ja	short gcftest1		; Is it more than what is free?

	xor	eax,eax			; Yes, then apparent size is zero
					; Nothing left, set apparent size to 0
gcftest1:
	pop	ebx
	jmps	gcftest
gcfrsrv1:				; Yes, then apparent size is zero
	xor	eax,eax			; Nothing left, set apparent size to 0
gcftest:
	cmp	eax,edx			; Return results of the comparison
	ret
cEnd nogen


;-----------------------------------------------------------------------;
; gsplice [should be named gslice,					;
;	   since splice means combine two into one - donc]		;
;									;
; Splits one block into two.						;
;									;
; Arguments:								;
;	EDX = size in bytes of new block to make			;
;	DS:ESI = address of existing block				;
;	DS:DI = address of global arena information structure		;
;									;
; Returns:								;
;	EDX = address of new block
;									;
; Error Returns:							;
;	nothing								;
;									;
; Registers Preserved:							;
;	AX,BX,DX,DI,DS,ES						;
;									;
; Registers Destroyed:							;
;	CX								;
;									;
; Calls:								;
;	nothing								;
; History:								;
;									;
;  Tue Sep 23, 1986 03:50:30p  -by-  David N. Weise   [davidw]		;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

cProc	gsplice,<PUBLIC,NEAR>,<EAX,EBX>
cBegin

	mov	ebx,ds:[esi].pga_size
	sub	ebx,edx			; get size of 2nd new block made
	mov	eax, ds:[esi].pga_address
	add	eax, edx
	cCall	alloc_arena_header,<eax>

	inc	[di].hi_count		; Adding new arena entry
	mov	ecx, eax
	xchg	ds:[esi].pga_next,ecx	; and old.next = new
	mov	ds:[ecx].pga_prev,eax	; [old old.next].prev = new

	mov	ds:[eax].pga_next,ecx	; new.next = old old.next
	mov	ds:[eax].pga_prev,esi

	mov	ds:[eax].pga_size,ebx
	mov	ds:[eax].pga_sig,GA_SIGNATURE
	mov	ds:[eax].pga_owner,di	; Zero owner & handle fields
	mov	ds:[eax].pga_flags,0	; For good measure.
	mov	ds:[eax].pga_handle,di

	mov	ds:[esi].pga_size,edx

	mov	edx, eax
gsplice_ret:
cEnd

;-----------------------------------------------------------------------;
; gjoin									;
;									;
; Merges a block into his previous neighbor.				;
;									;
; Arguments:								;
;	FS:ESI = address of block to remove				;
;	DS:DI  = address of global arena information structure		;
;									;
; Returns:								;
;	nothing								;
;									;
; Error Returns:							;
;	nothing								;
;									;
; Registers Preserved:							;
;	AX,BX,CX,DX,DI,SI,DS,ES						;
;									;
; Registers Destroyed:							;
;	SI								;
;									;
; Calls:								;
;	gdel_free							;
;									;
; History:								;
;									;
;  Mon Nov 17, 1986 11:41:49a  -by-  David N. Weise   [davidw]		;
; Added support for the free list of global partitions.			;
; 									;
;  Tue Sep 23, 1986 03:58:00p  -by-  David N. Weise   [davidw]		;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

cProc	gjoin,<PUBLIC,NEAR>
cBegin nogen
if KDEBUG
	cmp	esi, ds:[esi].pga_prev
	jne	short ok
int 3
int 3
ok:
endif
	push	eax			; assumes one is on freelist
	push	edx
	dec	[di].hi_count
	call	gdel_free
	mov	eax,ds:[esi].pga_size
	mov	edx,ds:[esi].pga_next	; Get address of block after
	mov	esi,ds:[esi].pga_prev	; Get address of block before
	cCall	free_arena_header,<ds:[edx].pga_prev>	; Free arena being removed
	mov	ds:[edx].pga_prev,esi	; Fix up block after
	mov	ds:[esi].pga_next, edx	; and the one before
	add	ds:[esi].pga_size,eax	; Recompute size of block
	pop	edx
	pop	eax
	ret
cEnd nogen


;-----------------------------------------------------------------------;
; gzero									;
;									;
; Fills the given area with zeros.					;
;									;
; Arguments:								;
;	BX = address of first paragraph					;
;	ECX = Bytes to clear						;
;									;
; Returns:								;
;	BX = 0								;
;									;
; Error Returns:							;
;									;
; Registers Preserved:							;
;	AX,DX,DI,SI,DS,ES						;
;									;
; Registers Destroyed:							;
;	CX								;
; Calls:								;
;	nothing								;
;									;
; History:								;
;									;
;  Tue Sep 23, 1986 04:08:55p  -by-  David N. Weise   [davidw]		;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

cProc	gzero,<PUBLIC,NEAR>
cBegin nogen

; Assumptions: on entry, BX contains selector to start of block, and is
; for a scratch descriptor that can be modified.  ECX contains the # of
; bytes to be zeroed.
  

	push	es
	push	eax
	push	edi
	push	ecx

	shr	ecx, 2			; # dwords to clear
	mov	es, bx
	xor	eax, eax
	xor	edi, edi

	cld
	rep	stos	dword ptr es:[edi]

	pop	ecx
	pop	edi
	pop	eax
	pop	es
	ret

cEnd nogen


;-----------------------------------------------------------------------;
; gmarkfree                                                             ;
; 									;
; Marks a block as free, coalesceing it with any free blocks before	;
; or after it.  This does not free any handles.				;
;									;
; Arguments:								;
;	EDX     = the first free object before this one			;
;		0 if unknown						;
;		Ring 1 if no free list update wanted			;
;		Ring 0 if free list update required			;
;	FS:ESI = block to mark as free.					;
;	DS:DI  = address of global arena information structure		;
; 									;
; Returns:								;
;	ZF = 1 if freed a fixed block					;
;	 DX = 0								;
;	ZF = 0 if freed a moveable block				;
;	 DX = handle table entry					;
;	FS:ESI = block freed (may have been coalesced)			;
; 									;
; Error Returns:							;
; 									;
; Registers Preserved:							;
;	AX,BX,CX,DX,DS							;
; 									;
; Registers Destroyed:							;
;	none								;
;									;
; Calls:								;
;	gjoin								;
;	gadd_free							;
; 									;
; History:								;
; 									;
;  Mon Nov 17, 1986 11:41:49a  -by-  David N. Weise   [davidw]		;
; Added support for the free list of global partitions.			;
; 									;
;  Sun Nov 09, 1986 01:35:08p  -by-  David N. Weise   [davidw]		;
; Made the debugging version fill all free space with CCCC.		;
; 									;
;  Wed Sep 24, 1986 10:27:06p  -by-  David N. Weise   [davidw]          ;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

cProc	gmarkfree,<PUBLIC,NEAR>
cBegin nogen

	call	gadd_free
	or	esi,esi
	jz	short gmf_exit

; Mark this block as free by clearing the owner field.

	mov	ds:[esi].pga_sig,GA_SIGNATURE
	mov	ds:[esi].pga_owner,di	; Mark as free
	mov	ds:[esi].pga_flags,0	; For good measure.

; Remember the handle value in DX, before setting to zero.

	xor	dx,dx
	xchg	ds:[esi].pga_handle,dx

; Try to coalesce with next block, if it is free

	push	ds:[esi].pga_prev	; save previous block
	mov	esi,ds:[esi].pga_next	; ESI = next block
	cmp	ds:[esi].pga_owner,di	; Is it free?
	jne	short free2		; No, continue
	call	gjoin			; Yes, coalesce with block we are freeing
free2:
	pop	esi			; ESI = previous block
	cmp	ds:[esi].pga_owner,di	; Is it free?
	jne	short free3		; No, continue
	mov	esi,ds:[esi].pga_next	; Yes, coalesce with block we are freeing;
	call	gjoin
free3:
	cmp	ds:[esi].pga_owner,di	; Point to free block?
	je	short free4		; Yes, done
	mov	esi,ds:[esi].pga_next	; No, leave ES pointing at free block
free4:
	call	gwin386discard
gmf_exit:
	or	dx,dx
	ret
cEnd nogen


;-----------------------------------------------------------------------;
; gadd_free                                                             ;
; 									;
; Links in the given partition into the global free list.		;
;									;
; Arguments:								;
;	EDX    = the first free object before this one			;
;		0 if unknown						;
;		odd if no free list update wanted			;
;	DS:DI  = BurgerMaster						;
;	FS:ESI = free global object to add				;
; 									;
; Returns:								;
; 									;
; Error Returns:							;
; 									;
; Registers Preserved:							;
;	all								;
; 									;
; Registers Destroyed:							;
; 									;
; Calls:								;
;	nothing								;
; 									;
; History:								;
; 									;
;  Sun Nov 09, 1986 02:42:53p  -by-  David N. Weise   [davidw]          ;
; Wrote it.								;
;-----------------------------------------------------------------------;

cProc	gadd_free,<PUBLIC,NEAR>
cBegin nogen
	push	eax
	push	edx

	test	dl,1
	jnz	short no_update_wanted

	or	esi,esi			; this happens with gmovebusy
	jz	short no_update_wanted
		      
	inc	[di].gi_free_count
	mov	edx, esi
	
need_a_home_loop:
	mov	edx, ds:[edx].pga_prev
	cmp	ds:[edx].pga_owner, di		; Found a free block?
	je	short found_a_home
	cmp	ds:[edx].pga_prev, edx		; Sentinel?
	jne	short need_a_home_loop
	
found_a_home:
	mov	eax, ds:[edx].pga_freenext
	mov	ds:[esi].pga_freenext, eax
	mov	ds:[esi].pga_freeprev, edx
	mov	ds:[edx].pga_freenext, esi
	mov	ds:[eax].pga_freeprev, esi

if KDEBUG
	call	check_free_list
endif
gaf_exit:
no_update_wanted:
	pop	edx
	pop	eax
	ret
cEnd nogen


;-----------------------------------------------------------------------;
; gdel_free                                                             ;
; 									;
; Removes a partition from the global free list.			;
; 									;
; Arguments:								;
;	DS:DI = BurgerMaster						;
;	FS:ESI = arena header of partition				;
;									;
; Returns:								;
; 									;
; Error Returns:							;
; 									;
; Registers Preserved:							;
;	All								;
; 									;
; Registers Destroyed:							;
; 									;
; Calls:								;
;	nothing								;
; 									;
; History:								;
; 									;
;  Sun Nov 09, 1986 02:43:26p  -by-  David N. Weise   [davidw]          ;
; Wrote it.								;
;-----------------------------------------------------------------------;

cProc	gdel_free,<PUBLIC,NEAR>
cBegin nogen

	push	edi
	push	esi
	mov	edi,ds:[esi].pga_freeprev
	mov	esi,ds:[esi].pga_freenext
	mov	ds:[edi].pga_freenext,esi
	mov	ds:[esi].pga_freeprev,edi
	pop	esi
	pop	edi

	dec	[di].gi_free_count
if KDEBUG
	call	check_free_list
endif
	ret
cEnd nogen


if KDEBUG
;-----------------------------------------------------------------------;
; check_free_list							;
; 									;
; Checks the global free list for consistency.				;
; 									;
; Arguments:								;
;	none								;
;									;
; Returns:								;
; 									;
; Error Returns:							;
; 									;
; Registers Preserved:							;
;	All								;
; 									;
; Registers Destroyed:							;
; 									;
; Calls:								;
;	nothing								;
;									;
; History:								;
; 									;
;  Wed Oct 29, 1986 10:13:42a  -by-  David N. Weise   [davidw]          ;
; Wrote it.								;
;-----------------------------------------------------------------------;

cProc	check_free_list,<PUBLIC,NEAR>
cBegin nogen
	push	eax
	push	ebx
	push	cx
	push	esi
	push	ds
	SetKernelDS
	cmp	fCheckFree,0
	jnz	short cfl_outta_here
	test	Kernel_flags,kf_check_free
	jnz	short cfl_check_it_out
cfl_outta_here:
	jmp	all_done
cfl_check_it_out:
	mov	ds,pGlobalHeap
	UnSetKernelDS
	mov	esi,[di].phi_first
	mov	cx,[di].gi_free_count
	or	cx,cx
	jz	all_done
	mov	eax, ds:[esi].pga_freenext
	mov	esi, eax
check_chain_loop:
	mov	ebx,ds:[esi].pga_freeprev
	mov	esi,ds:[esi].pga_freenext
	cmp	ds:[ebx].pga_freenext,eax
	jz	short prev_okay
prev_bad:
	push	esi
	mov	esi, eax
	kerror	0FFh,<free_list: prev bad>,bx,si
	mov	eax, esi
	pop	esi
prev_okay:
	cmp	ds:[esi].pga_freeprev,eax
	jnz	short next_bad
	mov	ebx,esi
	jmps	next_okay
next_bad:
	mov	bx, ax
	kerror	0FFh,<free_list: next bad>,bx,es
next_okay:
	mov	eax,esi
	loop	check_chain_loop
	SetKernelDS
	mov	ds,pGlobalHeap
	UnSetKernelDS
	cmp	[di].phi_last,eax
	jz	short all_done
	mov	bx, ax
	kerror	0FFh,<free_list: count bad>,[di].phi_last,bx
all_done:
	pop	ds
	pop	esi
	pop	cx
	pop	ebx
	pop	eax
	ret
cEnd nogen

endif

cProc	ValidateFreeSpaces,<PUBLIC,FAR>
cBegin nogen
	ret
cEnd nogen

sEnd	CODE

end
