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 fBooting externB fCheckFree externW pGlobalHeap externW Win386_Blocks externW FreeArenaCount gsearch_state_machine dw 0 gsearch_compact_first dw 0 public ffixedlow ffixedlow db 0 DataEnd sBegin CODE assumes CS,CODE externNP gcompact externNP gmovebusy externNP gslide externNP galign externNP genter externNP gleave ifdef WOW externFP MyGetAppWOWCompatFlagsEx endif externNP gwin386discard 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, 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 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 ; maybe ALLOC_DOS or regular fixed. test ah, GA_ALLOC_DOS jnz alloc_fixed_loop test fBooting, 2 ; are we past KERNEL loading jnz alloc_fixed_loop ; N: give him under 1MB line ; check to see if we want old behaviour ; this is to fix some mm drivers that mmsystem.dll will load test ffixedlow, 1 ; want FIXED under 1MB? jnz alloc_fixed_loop ; Y: give him under 1MB line ; start fixed blocks > 1MB. This will leave room for GlobalDOSAllocs ; Memory < 1MB will also get used for moveable blocks as we start ; from phi_first for these. These can be moved out of the way when ; we need to alloc GA_DOS_ALLOC requests. fast_forward_to_1MB_line: ; need to store this ptr ; for speed? cmp ds:[esi].pga_address, 100000h ; > 1Mb? jae skip_not_there ; OK for fixed allocs now mov esi,ds:[esi+ebx] loop fast_forward_to_1MB_Line ; when here it means we have no free blocks > 1MB!!! ; but have a block < 1MB. space_not_found will discard ; enough to move blocks up to generate room for this fixed blk. jmp space_not_found skip_not_there: if KDEBUG cmp [esi].pga_owner, GA_NOT_THERE je @f krDebugOut , "NOT_THERE block not there!" @@: endif mov esi,ds:[esi+ebx] 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 ;------------------------------------------------------------------ ; ; ChangeAllocFixedBehaviour ; GlobalAlloc(FIXED) used to return address < 1MB if possible in 3.1 ; It doesn't anymore in Chicago. You need to call this API if you ; want the old behaviour. Bad things will happen if you switch to old ; behaviour and forget to switch it back. ; MMSYSTEM.DLL loads some drivers that may expect this behaviour and ; they are the only callers of this fun. at the time of its writing. ; ENTRY = flags ; 0 = chicago behaviour ; 1 = win31 behaviour ; EXIT ; old bahaviour before this change (can be used to restore) ; ;------------------------------------------------------------------ cProc ChangeAllocFixedBehaviour, parmW flags cBegin GENTER32 CheckKernelDS FS ReSetKernelDS fs mov ax, flags xchg al, ffixedlow GLEAVE32 cEnd cProc GrowHeap, 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 , "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, 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, 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, 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, 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 , "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, mov ds:[ecx].pga_handle,0 cCall free_arena_header, mov edx,OldArena mov ds:[edx].pga_handle,0 cCall free_arena_header, 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, 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 , "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, 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, 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, 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, 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, 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, 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,, 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, 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, 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, ; 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, 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, 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, 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, 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, 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,,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,,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,,[di].phi_last,bx all_done: pop ds pop esi pop cx pop ebx pop eax ret cEnd nogen endif cProc ValidateFreeSpaces, cBegin nogen ret cEnd nogen sEnd CODE end