        PAGE    ,132
        TITLE   GALLOC - Global memory allocator

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

.386

DataBegin

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

DataEnd

sBegin  CODE
assumes CS,CODE

externNP gcompact
externNP gmovebusy
externNP gslide
externNP galign
ifdef WOW
externFP MyGetAppWOWCompatFlagsEx
        ; WOW doesn't decommit pages this way
else
externNP gwin386discard
endif
externNP GetDPMIFreeSpace
externNP InnerShrinkHeap
          
externNP get_physical_address
externNP set_physical_address
externNP alloc_arena_header
externNP free_arena_header
ifndef WOW_x86
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:                                                              ;
;                                                                       ;
;                                                                       ;
;  19-Aug-95 davehart:  Win 3.1 tries to grow the heap before           ;
;  compacting it, for WOW we want to compact first to be a good         ;
;  multitasking neighbor.                                               ;
;                                                                       ;
;  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

        cmp     word ptr [di].gi_free_count, 96
        jae     short compact_first
        mov     gsearch_compact_first, 0
        mov     gsearch_state_machine,codeOFFSET grow_heap
        jmp     short look_again

compact_first:
        mov     gsearch_compact_first, 1
        mov     gsearch_state_machine,codeOFFSET do_compact_nodiscard

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
        jmp     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_nodiscard            ; for debugging

do_compact_nodiscard:
        mov     gsearch_state_machine,codeOFFSET grow_heap

        ;
        ; Before growing the heap try compacting without
        ; discarding.  This step isn't executed unless there
        ; were 96 or more free blocks at entry to gsearch.
        ;

        test    ds:[di].gi_cmpflags, GA_NODISCARD
        jnz     short dcn_nodiscard

        or      ds:[di].gi_cmpflags, GA_NODISCARD
        call    gcompact
        and     ds:[di].gi_cmpflags, NOT GA_NODISCARD
        jmp     short over_compact

dcn_nodiscard:
        call    gcompact
        jmp     short over_compact


public  do_compact                      ; for debugging

do_compact:
        mov     gsearch_state_machine,codeOFFSET gsearch_fail

        ;
        ; If we tried compacting before and GA_NODISCARD was set,
        ; there is no need to compact again, since we already
        ; compacted with GA_NODISCARD before attempting to
        ; grow the heap.  If GA_NODISCARD was not set, our earlier
        ; compact forced it on, so it's worth trying again since
        ; we may be able to discard enough to satisfy the request.
        ;

        cmp     gsearch_compact_first, 0
        je      short @f
        test    ds:[di].gi_cmpflags, GA_NODISCARD
        jnz     short 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      ; heap grew, go look again

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

        push    edx                     ; gave some back, try to get it again
        call    GrowHeap
        pop     edx
        jnc     short over_compact      ; heap grew, go look again
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

;
; 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_x86
;; 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:[esi].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_x86
        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      short 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)

        cCall   MyGetAppWOWCompatFlagsEx ; check if we need to pad it
        test    ax, WOWCFEX_BROKENFLATPOINTER
        jz      short @f
        add     ebx, 4096*4             ; make it a bit bigger
@@:

        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

; Input - DWORD - Old Arena offset from burgermaster
;
; Output - None

cProc   FreeHeapDib,<PUBLIC,FAR>
        parmD   OldArena
cBegin
	CheckKernelDS	FS
        ReSetKernelDS   fs

        ; Make sure arena before and after are GA_NOT_THERE
        mov     ebx,OldArena
        mov     edx,ds:[ebx].pga_prev
        cmp     ds:[edx].pga_owner,GA_NOT_THERE
        jne     short fhd5
        mov     ecx,ds:[ebx].pga_next
        cmp     ds:[ecx].pga_owner,GA_NOT_THERE
        je      short fhd7
fhd5:
if KDEBUG
        krDebugOut <DEB_TRACE OR DEB_KrMemMan>, "FreeHeapDIB: Corrupt DIB Block"
endif
fhd7:
        ;Free all the three arenas. First fixup the arena list.
        ; edx - First GA_NOT_THERE arena
        ; ebx - Actual DIB arean
        ; ecx - Last GA_NOT_THERE arena

        mov eax,ds:[edx].pga_prev
        mov ebx,ds:[ecx].pga_next
        mov ds:[eax].pga_next,ebx
        mov ds:[ebx].pga_prev,eax


        mov ds:[edx].pga_handle,0
        cCall   free_arena_header,<edx>

        mov ds:[ecx].pga_handle,0
        cCall   free_arena_header,<ecx>

        mov edx,OldArena
        mov ds:[edx].pga_handle,0
        cCall   free_arena_header,<edx>

        xor     di,di
        sub     [di].hi_count, 3        ; Three less entries in heap
        dec     Win386_Blocks
	UnSetKernelDS	FS
cEnd

; Input - DWORD - Dib Address
;         DWORD - Old Arena offset from burgermaster
;
; Output - eax = new arena if operation successful
;          eax = NULL if operation failed

cProc   GrowHeapDib,<PUBLIC,FAR>
        parmD   OldArena
        parmD   NewAddress
cBegin
	CheckKernelDS	FS
	ReSetKernelDS	fs
	cmp	FreeArenaCount, 4	; 3 for below, 1 for a gsplice later
        jae     short ghd_start
        xor     eax,eax
	ret

        ; Now we have a new block. Sort it in to the heap. Create
        ; NOT THERE blocks as well.

ghd_start:
        inc     Win386_Blocks
        mov     ebx,OldArena
        mov     edx,ds:[ebx].pga_size   ; Length of block
if KDEBUG
	mov	eax, edx
	shr	eax, 16
        krDebugOut <DEB_TRACE OR DEB_KrMemMan>, "GrowHeapDIB: #ax#DX allocated"
endif
        mov     ebx,NewAddress          ; Ebx is the address of new block
        mov     esi, ebx                ; 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     ; DIB 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
        push    ecx
        mov     ecx,OldArena
        mov     ax, [ecx].pga_handle
        mov     [ebx].pga_handle,ax
        mov     ax, [ecx].pga_owner
        mov     [ebx].pga_owner,ax
        mov     al, [ecx].pga_count
        mov     [ebx].pga_count,al
        inc     [ebx].pga_count         ; make sure it doesn't move
        mov     al, [ecx].pga_pglock
        mov     [ebx].pga_pglock,al
        mov     al, [ecx].pga_flags
        mov     [ebx].pga_flags,al
        mov     al, [ecx].pga_selcount
        mov     [ebx].pga_selcount,al
        mov     [ebx].pga_lrunext, edi
        mov     [ebx].pga_lruprev, edi
        pop     ecx
	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
	mov	esi, [edi].phi_first
	cmp	eax, [esi].pga_address	; Below start of heap?
        ja      short ghd_sort_loop         ;   no, sort it in
;	int	3			; [this code never reached]
	mov	[esi].pga_address, eax	;   yes, adjust sentinel
        jmps    ghd_link_it_in              ; Sentinel now points to new block

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

ghd_sort_found:                         ; Block will go after ESI
	cmp	[esi].pga_next, esi	; This the sentinel?
        jne     short ghd_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

ghd_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     eax,ebx
	UnSetKernelDS	FS
cEnd

;-----------------------------------------------------------------------;
; 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:
ifdef WOW
        ; WOW doesn't decommit pages this way
else
        call    gwin386discard
endif
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
