1364 lines
56 KiB
NASM
1364 lines
56 KiB
NASM
TITLE GMEM - Register interface to global memory allocator
|
|
|
|
.xlist
|
|
include kernel.inc
|
|
include tdb.inc
|
|
.list
|
|
|
|
.386p
|
|
include protect.inc
|
|
|
|
DataBegin
|
|
|
|
;externW curTDB
|
|
;externW pGlobalHeap
|
|
externW Win386_Blocks
|
|
externW SelTableLen
|
|
externD SelTableStart
|
|
|
|
ifdef WOW
|
|
globalB fInAlloc, 0
|
|
globalW UserSelArray, 0
|
|
globalW SelectorFreeBlock, 0
|
|
endif
|
|
|
|
if ROM
|
|
externW gdtdsc
|
|
endif
|
|
|
|
DataEnd
|
|
|
|
|
|
sBegin CODE
|
|
assumes CS,CODE
|
|
externNP DPMIProc
|
|
ife ROM
|
|
externW gdtdsc
|
|
endif
|
|
|
|
externNP gsplice
|
|
externNP gjoin
|
|
externNP gzero
|
|
externNP gsearch
|
|
externNP gmarkfree
|
|
;externNP gdel_free
|
|
;externNP gcheckfree
|
|
externNP gmovebusy
|
|
externNP gcompact
|
|
externNP glruadd
|
|
externNP glrudel
|
|
externNP glrutop
|
|
externNP gnotify
|
|
externNP is_there_theoretically_enough_space
|
|
externNP can_we_clear_this_space
|
|
|
|
if ROM
|
|
externNP IsROMObject
|
|
endif
|
|
|
|
externNP get_physical_address
|
|
externNP alloc_sel
|
|
externNP alloc_data_sel
|
|
externFP IAllocCStoDSAlias
|
|
externNP pdref
|
|
externNP set_sel_limit
|
|
externNP set_selector_limit32
|
|
externNP set_selector_address32
|
|
externNP mark_sel_PRESENT
|
|
externNP mark_sel_NP
|
|
externNP free_sel
|
|
externNP FreeSelArray
|
|
externNP GrowSelArray
|
|
externNP get_arena_pointer32
|
|
externNP get_temp_sel
|
|
externNP AssociateSelector32
|
|
externNP free_arena_header
|
|
externNP PageLockLinear
|
|
externNP UnlinkWin386Block
|
|
externNP gwin386discard
|
|
externNP PreAllocArena
|
|
|
|
if KDEBUG
|
|
externNP CheckGAllocBreak ; LINTERF.ASM
|
|
endif
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; galign ;
|
|
; ;
|
|
; Aligns the size request for a global item to a valid para boundary. ;
|
|
; ;
|
|
; Arguments: ;
|
|
; EBX = #bytes ;
|
|
; CF = 1 if #paras overflowed. ;
|
|
; ;
|
|
; Returns: ;
|
|
; EDX = #bytes aligned, to next higher multiple of 32 ;
|
|
; ;
|
|
; Error Returns: ;
|
|
; EDX = 0100000h ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; all ;
|
|
; Registers Destroyed: ;
|
|
; none ;
|
|
; Calls: ;
|
|
; nothing ;
|
|
; History: ;
|
|
; ;
|
|
; Mon Sep 22, 1986 03:14:56p -by- David N. Weise [davidw] ;
|
|
; Added this nifty comment block. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
cProc galign,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
|
|
jc short align_error ; Overflow occur?
|
|
lea edx,[ebx+GA_ALIGN_BYTES]; No, add alignment amount
|
|
and dl,GA_MASK_BYTES ; ...modulo alignment boundary
|
|
cmp edx,ebx ; Test for overflow
|
|
jnb short align_exit ; OK, continue
|
|
align_error:
|
|
mov edx,0FF0000h ; Return largest possible size
|
|
jmps align_exit1 ; 255*64k since max # selectors is 255
|
|
align_exit:
|
|
cmp edx, 100000h ; Greater than 1Mb?
|
|
jbe short align_exit1 ; no, done
|
|
add edx, 0FFFh ; yes, page align
|
|
jc align_error
|
|
and dx, not 0FFFh
|
|
cmp edx, 0FF0000h ; Too big?
|
|
ja short align_error ; yep, hard luck
|
|
align_exit1:
|
|
ret
|
|
cEnd nogen
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; galloc ;
|
|
; ;
|
|
; Allocates global memory. ;
|
|
; ;
|
|
; Arguments: ;
|
|
; AX = allocation flags ;
|
|
; BX = #paragraphs ;
|
|
; CX = owner field ;
|
|
; DS:DI = address of global heap info ;
|
|
; ;
|
|
; Returns: ;
|
|
; AX = handle to object or zero ;
|
|
; BX = size of largest free block if AX = 0 ;
|
|
; CX = AX ;
|
|
; ;
|
|
; Error Returns: ;
|
|
; DX = 0 ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ;
|
|
; Calls: ;
|
|
; gsearch ;
|
|
; ghalloc ;
|
|
; glruadd ;
|
|
; gmarkfree ;
|
|
; History: ;
|
|
; ;
|
|
; Wed Jun 24, 1987 03:04:32a -by- David N. Weise [davidw] ;
|
|
; Added support for Global Notify. ;
|
|
; ;
|
|
; Mon Sep 22, 1986 02:38:19p -by- David N. Weise [davidw] ;
|
|
; Added this nifty comment block. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
AccessWord dw DSC_DATA+DSC_PRESENT
|
|
dw (DSC_DISCARDABLE SHL 8) + DSC_DATA+DSC_PRESENT
|
|
dw DSC_CODE+DSC_PRESENT
|
|
dw (DSC_DISCARDABLE SHL 8) + DSC_CODE+DSC_PRESENT
|
|
dw DSC_DATA+DSC_PRESENT
|
|
dw (DSC_DISCARDABLE SHL 8) + DSC_DATA+DSC_PRESENT
|
|
dw DSC_DATA+DSC_PRESENT
|
|
dw (DSC_DISCARDABLE SHL 8) + DSC_DATA+DSC_PRESENT
|
|
|
|
cProc galloc,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
|
|
if KDEBUG
|
|
test al,GA_DISCCODE ; if discardable code, allow alloc
|
|
jnz @F
|
|
call CheckGAllocBreak
|
|
jc gaerr ; time to fail...
|
|
@@:
|
|
endif
|
|
cmp ebx, (16*1024*1020) ; Too big?
|
|
ja gaerr ; yes.
|
|
|
|
or ebx,ebx ; Allocating zero size?
|
|
jz allocate_zero_size
|
|
|
|
call gsearch ; Search for block big enough
|
|
jz ga_exit ; Done, if couldn't get enough
|
|
|
|
mov esi,eax
|
|
push dx
|
|
mov bx,dx
|
|
mov edx, ds:[esi].pga_address
|
|
mov ecx, ds:[esi].pga_size
|
|
|
|
and bx, ((GA_CODE_DATA+GA_DISCARDABLE) shl 8) + GA_DGROUP
|
|
or bl, bh
|
|
xor bh, bh
|
|
shl bx, 1
|
|
mov ax, cs:AccessWord[bx] ; Pick up access rights for selector
|
|
cCall alloc_sel,<edx,ecx>
|
|
pop dx
|
|
or ax, ax ; Did we get the selectors?
|
|
jz short gaerr2 ; no, free block and return
|
|
|
|
add ecx, 0FFFFh ; Calculate # selectors we got
|
|
shr ecx, 16
|
|
mov ds:[esi].pga_selcount, cl
|
|
cCall AssociateSelector32,<ax,esi>
|
|
test dl,GA_MOVEABLE ; Is this a moveable object?
|
|
jnz short moveable
|
|
test dh, GA_DISCARDABLE ; We have a fixed block
|
|
jnz short not_moveable ; Not interested in discardable blocks
|
|
mov bx, ax
|
|
ifdef WOW
|
|
; the following dpmicall is basically a NOP. so just
|
|
; avoid the call altogether.
|
|
; - Nanduri Ramakrishna
|
|
else
|
|
DPMICALL 0004H
|
|
jc short gaerr1
|
|
endif
|
|
inc [esi].pga_pglock ; Mark it locked
|
|
mov ax, bx
|
|
jmps not_moveable
|
|
|
|
moveable:
|
|
mov ds:[esi].pga_count,0 ; Initialize lock count to 0
|
|
StoH ax ; Mark as moveable block
|
|
not_moveable:
|
|
mov ds:[esi].pga_handle,ax ; Set handle in arena
|
|
mov bx, ax ; AX and BX handle
|
|
|
|
call glruadd ; Yes, Add to LRU chain
|
|
mov cx,ax
|
|
ret
|
|
|
|
allocate_zero_size:
|
|
test al,GA_MOVEABLE ; Yes, moveable?
|
|
jz short gaerr ; No, return error (AX = 0)
|
|
|
|
mov bx, ax
|
|
and bx, ((GA_CODE_DATA+GA_DISCARDABLE) shl 8) + GA_DGROUP
|
|
or bl, bh ; Above bits are exclusive
|
|
xor bh, bh
|
|
shl bx, 1
|
|
mov ax, cs:AccessWord[bx] ; Pick up access rights for selector
|
|
and al, NOT DSC_PRESENT ; These are NOT present
|
|
xor edx, edx ; Base of zero for now
|
|
cCall alloc_sel,<edx,dx,cx>
|
|
or ax, ax
|
|
jz short gaerr
|
|
|
|
cCall AssociateSelector32,<ax,0,cx> ; Save owner in selector table
|
|
|
|
StoH al ; Handles are RING 2
|
|
mov bx,ax
|
|
ga_exit:
|
|
mov cx,ax
|
|
ret
|
|
|
|
gaerr1: ; Failed to page lock, free up everthing
|
|
cCall FreeSelArray,<bx>
|
|
gaerr2: ; Failed to get selectors
|
|
xor edx,edx
|
|
call gmarkfree
|
|
gaerr:
|
|
KernelLogError DBF_WARNING,ERR_GALLOC,"GlobalAlloc failed"
|
|
xor dx,dx ; DX = 0 means NOT out of memory
|
|
xor ax,ax ; Return AX = 0 to indicate error
|
|
jmps ga_exit
|
|
cEnd nogen
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; grealloc ;
|
|
; ;
|
|
; Reallocates the given global memory object. ;
|
|
; ;
|
|
; Arguments: ;
|
|
; AX = allocation flags ;
|
|
; EBX = #bytes for new size ;
|
|
; CX = new owner field value ;
|
|
; DX = existing handle ;
|
|
; DS:DI = address of global heap info ;
|
|
; ;
|
|
; Returns: ;
|
|
; AX = handle to object or zero ;
|
|
; DX = size of largest free block if AX = 0 ;
|
|
; CX = AX ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; SI ;
|
|
; ;
|
|
; Calls: ;
|
|
; ;
|
|
; History: ;
|
|
; ;
|
|
; Mon Sep 22, 1986 10:11:48a -by- David N. Weise [davidw] ;
|
|
; Added this nifty comment block. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
cProc grealloc,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
|
|
push bp
|
|
mov bp,sp
|
|
push ax
|
|
rflags EQU word ptr [bp-2]
|
|
push dx
|
|
h EQU word ptr [bp-4]
|
|
push cx
|
|
owner EQU word ptr [bp-6]
|
|
push ebx
|
|
rsize EQU dword ptr [bp-10]
|
|
sub sp, 6
|
|
canmove EQU byte ptr [bp-12]
|
|
locked EQU byte ptr [bp-13]
|
|
mflags EQU byte ptr [bp-14]
|
|
pgLockSel EQU word ptr [bp-16]
|
|
push dx
|
|
oldh EQU word ptr [bp-18]
|
|
|
|
mov pgLockSel, 0 ; No selector to free yet
|
|
|
|
call pdref
|
|
mov dx, bx ; save owner if discarded
|
|
mov word ptr (mflags), cx
|
|
mov ebx,rsize
|
|
jz racreate ; Do nothing with 0, free or discarded handles
|
|
handle_ok:
|
|
test byte ptr rflags,GA_MODIFY ; Want to modify table flags?
|
|
jnz short ramodify ; Yes go do it
|
|
or ebx,ebx ; Are we reallocing to zero length?
|
|
jz short to_0
|
|
jmp raokay ; No, continue
|
|
to_0: or ch,ch ; Is handle locked?
|
|
jz short radiscard
|
|
rafail:
|
|
KernelLogError DBF_WARNING,ERR_GREALLOC,"GlobalReAlloc failed"
|
|
xor ax,ax ; Yes, return failure
|
|
xor dx,dx
|
|
jmp raexit
|
|
|
|
radiscard: ; No, then try to discard the object
|
|
|
|
; Here to discard object, when reallocating to zero size. This
|
|
; feature is only enabled if the caller passes the moveable flag
|
|
|
|
test byte ptr rflags,GA_MOVEABLE ; Did they want to discard?
|
|
jz short rafail ; No, then return failure.
|
|
|
|
mov al,GN_DISCARD ; AL = discard message code
|
|
xor cx,cx ; CX = means realloc
|
|
mov bx, ds:[esi].pga_handle ; BX = handle
|
|
push es
|
|
call gnotify ; See if okay to discard
|
|
pop es
|
|
jz short rafail ; No, do nothing
|
|
call glrudel ; Yes, Delete handle from LRU chain
|
|
|
|
cCall mark_sel_NP,<ds:[esi].pga_handle,ds:[esi].pga_owner>
|
|
xor edx,edx
|
|
call gmarkfree ; Free client data
|
|
jz short rafixed ; Return NULL if freed a fixed block
|
|
jmp rasame ; Return original handle, except
|
|
; GlobalLock will now return null.
|
|
rafixed:
|
|
xor ax,ax
|
|
jmp raexit
|
|
|
|
ramodify:
|
|
if ROM
|
|
cCall IsROMObject, <h>
|
|
or ax, ax
|
|
jnz rasame1
|
|
endif
|
|
mov ax,rflags ; Get new flags
|
|
mov dx,owner ; Get new owner field value
|
|
mov bx, ds:[esi].pga_handle
|
|
test bl, GA_FIXED ; Moveable object?
|
|
jz short is_moveable
|
|
test al,GA_MOVEABLE ; Make fixed into moveable?
|
|
jz short ramod2 ; No, change owner only
|
|
|
|
StoH bx ; Turn selector into handle
|
|
mov ds:[esi].pga_handle, bx
|
|
mov ds:[esi].pga_count, 0 ; 0 lock count for new movable obj
|
|
|
|
is_moveable:
|
|
call glrudel ; Yes, remove from lru chain
|
|
push ax
|
|
push ecx
|
|
lar ecx, ebx ; Get existing access rights
|
|
shr ecx, 8
|
|
test ah, GA_DISCARDABLE ; Do we want it to be discardable?
|
|
jnz short ra_want_discardable
|
|
.errnz DSC_DISCARDABLE-10h
|
|
btr cx, 12 ; Ensure DSC_DISCARDABLE is off
|
|
jnc short ra_ok_disc_bit ; it was
|
|
jmps ra_set_access ; nope, must reset it
|
|
ra_want_discardable:
|
|
bts cx, 12 ; Ensure DSC_DISCARDABLE is on
|
|
jc short ra_ok_disc_bit
|
|
ra_set_access:
|
|
DPMICALL 0009h
|
|
ra_ok_disc_bit:
|
|
pop ecx
|
|
pop ax
|
|
ra_notdiscardable:
|
|
test cl,HE_DISCARDED ; Is this a discarded handle?
|
|
jz short ramod1 ; No, continue
|
|
test ah,GA_SHAREABLE ; Only change owner if making shared
|
|
jz short rasame1
|
|
int 3
|
|
push ax
|
|
push ecx
|
|
lsl ecx, ebx ; Use existing high limit bits
|
|
shr ecx, 16
|
|
DPMICALL 0008h ; Set segment limit (to CX:DX)
|
|
pop ecx
|
|
pop ax
|
|
jmps rasame1
|
|
ramod1:
|
|
call glruadd ; Add to lru chain if now discardable
|
|
ramod2:
|
|
test ah,GA_SHAREABLE ; Only change owner if making shared
|
|
jz short rasame1
|
|
mov ds:[esi].pga_owner,dx ; Set new owner value
|
|
rasame1:
|
|
jmp rasame
|
|
|
|
rafail0:
|
|
jmp rafail
|
|
racreate:
|
|
test cl,HE_DISCARDED ; Is this a discarded handle?
|
|
jz short rafail0 ; No, return error
|
|
or ebx,ebx ; Are we reallocing to zero length?
|
|
jz short rasame1 ; Yes, return handle as is.
|
|
|
|
if KDEBUG
|
|
test cl,GA_DISCCODE ; if discardable code, allow realloc
|
|
jnz @F
|
|
call CheckGAllocBreak
|
|
jc rafail0
|
|
@@:
|
|
endif
|
|
mov ax,GA_MOVEABLE ; Reallocating a moveable object
|
|
or ax,rflags ; ...plus any flags from the caller
|
|
; DO NOT CHANGE: flag conflict
|
|
; GA_DISCARDABLE == GA_ALLOCHIGH.
|
|
and cl,not (HE_DISCARDED + GA_ALLOCHIGH)
|
|
or al,cl
|
|
mov cx,dx ; get owner
|
|
|
|
test al,GA_DISCCODE ; Discardable code segment?
|
|
jz short ranotcode
|
|
or al,GA_ALLOCHIGH ; Yes, allocate high
|
|
ranotcode:
|
|
or al, COMPACT_ALLOC ; Allow discarding
|
|
mov [di].gi_cmpflags,al ; Save flags for gcompact
|
|
and [di].gi_cmpflags,CMP_FLAGS or GA_ALLOCHIGH
|
|
push si ; save handle
|
|
call gsearch ; Find block big enough
|
|
pop si ; restore existing handle
|
|
jz rafailmem1
|
|
|
|
cCall mark_sel_PRESENT,<eax,si>
|
|
or si,si ; Might have failed to grow selector array
|
|
jz short racre_worst_case
|
|
|
|
xchg eax,esi ; Return original handle
|
|
; Set back link to handle in new block
|
|
cCall AssociateSelector32,<ax,esi>
|
|
mov ds:[esi].pga_handle,ax
|
|
mov ds:[esi].pga_count,0
|
|
; and ch,GA_SEGTYPE ; OPTIMIZE superfluous??
|
|
; and es:[di].ga_flags,GAH_NOTIFY
|
|
; or es:[di].ga_flags,ch ; Copy segment type flags to ga_flags
|
|
call glruadd ; Add to LRU chain
|
|
jmp raexit
|
|
|
|
racre_worst_case:
|
|
mov esi, eax ; Free block if selectors not available
|
|
xor edx, edx
|
|
call gmarkfree
|
|
|
|
KernelLogError DBF_WARNING,ERR_GREALLOC,"GlobalReAlloc failed"
|
|
|
|
xor dx, dx
|
|
xor ax, ax
|
|
jmp raexit
|
|
|
|
raokay:
|
|
|
|
if KDEBUG
|
|
test ds:[esi].pga_flags,GA_DISCCODE
|
|
jz short ok
|
|
Debug_Out "GlobalReAlloc of Discardable Code"
|
|
ok:
|
|
endif
|
|
cmp ebx,ds:[esi].pga_size
|
|
jz short rasame
|
|
|
|
clc
|
|
call galign ; assuming there is room.
|
|
|
|
; Here if not trying to realloc this block to zero
|
|
; FS:ESI = arena header of current block
|
|
; AX:0 = client address of current block
|
|
; CH = lock count of current block
|
|
; EDX = new requested size
|
|
|
|
cmp ds:[esi].pga_pglock, 0 ; Are we page locked?
|
|
je short ranolock
|
|
push ax
|
|
push dx
|
|
push es
|
|
cCall IAllocCStoDSAlias,<h> ; Get an alias selector (type doesn't
|
|
pop es
|
|
pop dx
|
|
mov pgLockSel, ax ; matter)
|
|
or ax, ax ; Got selector?
|
|
pop ax
|
|
jz rafail ; no, goodbye
|
|
ranolock:
|
|
mov ebx,ds:[esi].pga_next ; Get address of current next header
|
|
cmp edx,ds:[esi].pga_size ; Are we growing or shrinking?
|
|
ja short raextend ; We are growing
|
|
|
|
call rashrink
|
|
|
|
ifdef WOW
|
|
; the following dpmicall is basically a NOP. so just
|
|
; avoid the call altogether.
|
|
; - Nanduri Ramakrishna
|
|
else
|
|
mov bx, h
|
|
mov ax, pgLockSel ; Were we page locked?
|
|
or ax, ax
|
|
jz short rasame ; no, nothing to do
|
|
Handle_To_Sel bl
|
|
DPMICALL 0004h
|
|
endif
|
|
|
|
rasame_pglock:
|
|
ifdef WOW
|
|
; avoid the call altogether.
|
|
else
|
|
mov bx, pgLockSel ; Were we page locked?
|
|
or bx, bx
|
|
jz short rasame
|
|
DPMICALL 0005h
|
|
endif
|
|
|
|
rasame:
|
|
mov ax,h ; Return the same handle
|
|
jmp raexit ; All done
|
|
|
|
raextend:
|
|
test rflags,GA_DISCCODE ; Not allowed to grow a disccode seg
|
|
jnz short rafail1
|
|
if KDEBUG
|
|
call CheckGAllocBreak
|
|
jc rafail1
|
|
endif
|
|
push ax
|
|
call GrowSelArray
|
|
mov cx, ax
|
|
pop ax ; Did we get the selectors?
|
|
jcxz rafail1 ; no, fail
|
|
mov h, cx ; Update handle
|
|
call ragrow
|
|
jnc short rasame_pglock ; Success
|
|
test mflags,GA_DISCARDABLE ; if discardable, just stop now
|
|
jz short ramove ; since it might get discarded!
|
|
rafail1:
|
|
jmp rafail
|
|
|
|
; Here to try to move the current block
|
|
; AX = client address of current block
|
|
; ES:0 = arena header of current block
|
|
; CH = lock count of current block
|
|
; EDX = new requested size of block
|
|
|
|
ramove:
|
|
mov ebx, edx ; Size now in EBX
|
|
mov canmove, 1
|
|
mov dx,rflags ; get the passed in flags
|
|
test dx,GA_MOVEABLE ; Did they say OK to move
|
|
jnz short ramove1 ; Yes, try to move even iflocked or fixed
|
|
cmp locked, 0 ; Locked?
|
|
; Continue if this handle not locked
|
|
jnz short racompact ; yes, try to find space to grow in place
|
|
or dx,GA_MOVEABLE ; If moveable, make sure bit set.
|
|
test h,GA_FIXED ; Is this a moveable handle?
|
|
jz short ramove2 ; Yes, okay to move
|
|
|
|
racompact:
|
|
xor dx,dx ; No, get size of largest free block
|
|
call gcompact
|
|
jmp racantmove
|
|
|
|
ramove1:
|
|
test h, GA_FIXED
|
|
jz short ramove2
|
|
and dx, NOT GA_MOVEABLE
|
|
ramove2:
|
|
mov ax,dx ; AX = allocation flags
|
|
;;; mov bx,si ; EBX = size of new block
|
|
mov cx,1 ; CX = owner (use size for now)
|
|
call gsearch ; Find block big enough
|
|
jz short racantmove ; Cant find one, grow in place now?
|
|
mov esi, eax ; ESI = destination arena
|
|
call PreAllocArena ; Required for gmovebusy
|
|
jz short ramove2a
|
|
mov cx, pgLockSel ; Do we have to page lock it?
|
|
jcxz ramove3
|
|
cCall PageLockLinear,<ds:[esi].pga_address,ds:[esi].pga_size>
|
|
jnc short ramove3 ; Locked it?
|
|
ramove2a:
|
|
xor edx, edx ; no, free memory block
|
|
call gmarkfree
|
|
jmps racantmove
|
|
ramove3:
|
|
mov cx,h
|
|
|
|
cCall get_arena_pointer32,<cx>
|
|
mov edx,eax
|
|
|
|
call gmovebusy ; Call common code to move busy block
|
|
; (AX destroyed)
|
|
|
|
push ebx
|
|
push esi
|
|
mov esi, edx ; free block just emptied
|
|
mov ebx, ds:[esi].pga_prev ; See if block can be
|
|
cmp ds:[ebx].pga_owner, GA_NOT_THERE; returned to win386
|
|
jne short ra_no_unlink
|
|
push ecx
|
|
mov ecx, ds:[esi].pga_next
|
|
cmp ds:[ecx].pga_owner, GA_NOT_THERE
|
|
jne short ra_no_unlink_ecx
|
|
mov eax, ds:[ecx].pga_next
|
|
cmp eax, ds:[eax].pga_next ; Sentinel?
|
|
je short ra_no_unlink_ecx ; yes, keep this block
|
|
|
|
push edx
|
|
push edi
|
|
cCall UnlinkWin386Block
|
|
pop edi
|
|
pop edx
|
|
|
|
ra_no_unlink_ecx:
|
|
pop ecx
|
|
ra_no_unlink:
|
|
pop esi
|
|
pop ebx
|
|
|
|
cCall set_selector_limit32,<ds:[esi].pga_handle,ds:[esi].pga_size>
|
|
jmp rasame_pglock
|
|
|
|
racantmove:
|
|
mov dx, h
|
|
call pdref
|
|
|
|
mov ebx,rsize
|
|
clc
|
|
call galign ; assuming there is room.
|
|
|
|
mov ebx,ds:[esi].pga_next ; Get address of current next header
|
|
call ragrow
|
|
jc short racmove3
|
|
jmp rasame_pglock
|
|
|
|
racmove3:
|
|
xor dx,dx ; No, get size of largest free block
|
|
call gcompact
|
|
mov dx,ax ; DX = size of largest free block
|
|
|
|
rafailmem:
|
|
|
|
mov eax,ds:[esi].pga_size ; AX = size of current block
|
|
mov esi,ds:[esi].pga_next ; Check following block
|
|
cmp ds:[esi].pga_owner,di ; Is it free?
|
|
jne short rafailmem0 ; No, continue
|
|
add eax,ds:[esi].pga_size ; Yes, then include it as well
|
|
;;; inc ax
|
|
rafailmem0:
|
|
cmp ax,dx ; Choose the larger of the two
|
|
jbe short rafailmem1
|
|
mov dx,ax
|
|
rafailmem1:
|
|
push dx ; Save DX
|
|
KernelLogError DBF_WARNING,ERR_GREALLOC,"GlobalReAlloc failed"
|
|
pop dx ; Restore DX
|
|
|
|
xor ax,ax
|
|
|
|
raexit:
|
|
push ax
|
|
push bx
|
|
push dx
|
|
|
|
mov bx, pgLockSel
|
|
or bx, bx ; Have alias selector?
|
|
jz short noSel ; nope, all ok
|
|
cCall free_sel,<bx>
|
|
noSel:
|
|
mov bx, h
|
|
;;; inc bl
|
|
and bl, NOT 1
|
|
mov cx, oldh
|
|
;;; inc cl
|
|
and cl, NOT 1
|
|
cmp bx, cx ; Did we get new selector array?
|
|
je short no_new_handle ; nope.
|
|
or ax, ax ; Did we succeed?
|
|
jz short free_new
|
|
HtoS cl
|
|
cCall FreeSelArray,<cx> ; Free old selector array
|
|
jmps no_new_handle
|
|
; Update old selector array
|
|
free_new:
|
|
HtoS bl
|
|
cCall get_arena_pointer32,<bx> ; Get new arena (may have moved)
|
|
mov esi,eax
|
|
HtoS cl
|
|
cCall AssociateSelector32,<cx,esi> ; Set up old sel array
|
|
cCall set_selector_address32,<cx,ds:[esi].pga_address>
|
|
lsl ecx, ecx ; Get old length
|
|
if KDEBUG
|
|
jz short @F
|
|
int 3
|
|
@@:
|
|
endif
|
|
add ecx, 10000h
|
|
shr ecx, 16 ; CL has old # selectors
|
|
xchg ds:[esi].pga_selcount, cl
|
|
mov ax, oldh
|
|
xchg ds:[esi].pga_handle, ax ; Reset handle
|
|
cCall AssociateSelector32,<ax,0,0> ; Disassociate new array
|
|
fsloop:
|
|
cCall free_sel,<ax> ; Free new selector array
|
|
add ax, 8
|
|
loop fsloop
|
|
|
|
no_new_handle:
|
|
pop dx
|
|
pop bx
|
|
pop ax
|
|
mov cx, ax
|
|
|
|
mov sp,bp
|
|
pop bp
|
|
ret
|
|
|
|
cEnd nogen
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; rashrink ;
|
|
; ;
|
|
; Shrinks the given block ;
|
|
; ;
|
|
; Arguments: ;
|
|
; Here to shrink a block ;
|
|
; DS:ESI = arena header of current block ;
|
|
; DS:EBX = arena header of next block ;
|
|
; EDX = new requested size ;
|
|
; ;
|
|
; Returns: ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ALL but DS, DI ;
|
|
; ;
|
|
; Calls: ;
|
|
; gsplice ;
|
|
; gmarkfree ;
|
|
; ;
|
|
; History: ;
|
|
; ;
|
|
;-----------------------------------------------------------------------;
|
|
cProc rashrink,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
|
|
call PreAllocArena ; Make sure we can do it
|
|
jz short rashrunk
|
|
|
|
mov ax,ds:[esi].pga_handle
|
|
or ax,ax
|
|
jz short ra_free
|
|
Handle_To_Sel al
|
|
push ecx
|
|
push edx
|
|
lsl ecx, eax
|
|
Limit_To_Selectors ecx ; Old # selectors
|
|
dec edx
|
|
Limit_To_Selectors edx ; New # selectors
|
|
sub cx, dx
|
|
jbe short none_to_free
|
|
|
|
mov ds:[esi].pga_selcount, dl
|
|
push ax
|
|
.errnz SIZE DscPtr-8
|
|
shl dx, 3
|
|
add ax, dx ; First selector to free
|
|
ras_loop:
|
|
cCall free_sel,<ax>
|
|
add ax, SIZE DscPtr
|
|
loop ras_loop
|
|
pop ax
|
|
|
|
none_to_free:
|
|
pop edx
|
|
pop ecx
|
|
|
|
cCall set_selector_limit32,<ax,edx>
|
|
ra_free:
|
|
cmp edx,ds:[esi].pga_size ; Enough room from for free block?
|
|
jae short rashrunk ; No, then no change to make
|
|
|
|
call gsplice ; splice new block into the arena
|
|
mov esi, edx
|
|
xor edx, edx
|
|
call gmarkfree ; Mark it as free
|
|
rashrunk:
|
|
ret
|
|
cEnd nogen
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; ragrow ;
|
|
; ;
|
|
; Tries to grow the given global memory object in place ;
|
|
; ;
|
|
; Arguments: ;
|
|
; AX:0 = client address of current block ;
|
|
; FS:ESI = arena header of current block ;
|
|
; FS:EBX = arena header of next block ;
|
|
; EDX = new requested size of block ;
|
|
; ;
|
|
; Returns: ;
|
|
; CY = 0 Success ;
|
|
; ;
|
|
; CY = 1 Failed ;
|
|
; ESI preserved ;
|
|
; EDX contains free memory required ;
|
|
; ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ALL but DS, DI ;
|
|
; ;
|
|
; Calls: ;
|
|
; is_there_theoretically_enough_space ;
|
|
; can_we_clear_this_space ;
|
|
; gjoin ;
|
|
; gzero ;
|
|
; rashrink ;
|
|
; ;
|
|
; History: ;
|
|
; ;
|
|
; Mon 05-Sep-1988 20:10:15 -by- David N. Weise [davidw] ;
|
|
; Made ragrow be more intelligent by trying to extend into moveable ;
|
|
; blocks. ;
|
|
;-----------------------------------------------------------------------;
|
|
cProc ragrow,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
|
|
push ds:[esi].pga_size ; Save in case we have to back out
|
|
push edx
|
|
push esi ; Save current block address
|
|
sub edx, ds:[esi].pga_size ; compute amount of free space wanted
|
|
xchg esi,ebx ; ESI = next block address
|
|
mov cx,[di].hi_count
|
|
push ax
|
|
push cx
|
|
push esi
|
|
call is_there_theoretically_enough_space
|
|
pop esi
|
|
pop cx
|
|
cmp eax,edx
|
|
jb short ragx
|
|
call can_we_clear_this_space
|
|
jz short ragx
|
|
cCall alloc_data_sel,<ds:[esi].pga_address, edx>
|
|
|
|
or ax, ax ; Did we get a selector?
|
|
jnz short okk ; yes, continue
|
|
jmps ragx
|
|
okk:
|
|
mov cx, ax
|
|
pop ax
|
|
push cx ; Parameter to free_sel (below)
|
|
push edx
|
|
call gjoin ; and attach to end of current block
|
|
pop edx
|
|
test byte ptr rflags,GA_ZEROINIT ; Zero fill extension?
|
|
jz short ranz ; No, continue
|
|
mov bx,cx ; Yes, BX = first paragraph to fill
|
|
mov ecx,edx ; compute last paragraph to fill
|
|
call gzero ; zero fill extension
|
|
ranz:
|
|
call FreeSelArray
|
|
pop edx ; clear the stack
|
|
pop edx ; New length of block
|
|
mov bx, ds:[esi].pga_handle
|
|
Handle_To_Sel bl
|
|
cCall set_selector_limit32,<bx,edx>
|
|
|
|
ifndef WOW ; WOW doesn't lock pages
|
|
cmp ds:[esi].pga_pglock, 0
|
|
je short rag1
|
|
|
|
mov ax, 4 ; Page lock the whole thing
|
|
int 31h
|
|
mov ax, bx
|
|
jc short rag2
|
|
endif; WOW
|
|
|
|
rag1:
|
|
mov ebx,ds:[esi].pga_next ; Pick up new next block address
|
|
call rashrink ; Now shrink block to correct size
|
|
add sp, 4
|
|
clc
|
|
ret
|
|
ragx:
|
|
pop ax
|
|
pop esi ; Recover current block address
|
|
pop edx
|
|
add sp, 4 ; toss original size
|
|
stc
|
|
ret
|
|
rag2:
|
|
if KDEBUG
|
|
int 3
|
|
endif
|
|
pop edx ; Shrink back to orignal size
|
|
mov ebx, ds:[esi].pga_next
|
|
call rashrink
|
|
stc ; and fail
|
|
ret
|
|
|
|
cEnd nogen
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; gfree ;
|
|
; ;
|
|
; Frees a global object. ;
|
|
; ;
|
|
; Arguments: ;
|
|
; DX = global memory object handle ;
|
|
; CX = owner field value to match or zero if dont care ;
|
|
; DS:DI = address of global heap info ;
|
|
; ;
|
|
; Returns: ;
|
|
; AX = 0 ;
|
|
; CX = 0 ;
|
|
; ;
|
|
; Error Returns: ;
|
|
; AX = -1 ;
|
|
; CX = -1 ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ? ;
|
|
; Calls: ;
|
|
; gdref ;
|
|
; free_object ;
|
|
; hfree ;
|
|
; ;
|
|
; History: ;
|
|
; ;
|
|
; Sat Sep 20, 1986 11:48:38a -by- David N. Weise [davidw] ;
|
|
; Added this nifty comment block and restructured. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
cProc gfree,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
|
|
push cx
|
|
call pdref
|
|
pop dx
|
|
jz short object_discarded
|
|
call free_object
|
|
jmps gfree_exit
|
|
|
|
;** When the object is discarded, we have to remove the sel table
|
|
;* pointer to the object (this points to the >owner< of the
|
|
;** block for discardable objects)
|
|
object_discarded:
|
|
PUBLIC object_discarded
|
|
xor eax,eax
|
|
cCall AssociateSelector32, <si,eax> ;Remove in the sel table
|
|
cCall FreeSelArray,<si>
|
|
xor ax,ax ;Force success
|
|
|
|
gfree_exit:
|
|
mov cx,ax
|
|
ret
|
|
cEnd nogen
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; free_object ;
|
|
; ;
|
|
; Arguments: ;
|
|
; DX = owner field value to match or zero if dont care ;
|
|
; DS:DI = address of global heap info ;
|
|
; ES:ESI = address of arena header ;
|
|
; ;
|
|
; Returns: ;
|
|
; AX = 0 ;
|
|
; ;
|
|
; Error Returns: ;
|
|
; AX = -1 ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ;
|
|
; Calls: ;
|
|
; glrudel ;
|
|
; gmarkfree ;
|
|
; hfree ;
|
|
; History: ;
|
|
; ;
|
|
; Sat Sep 20, 1986 02:59:06p -by- David N. Weise [davidw] ;
|
|
; Moved it from gfree. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
cProc free_object,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
or dx,dx
|
|
jz short free_it
|
|
cmp ds:[esi].pga_owner,dx
|
|
je short free_it
|
|
mov ax,-1
|
|
jmps free_object_exit
|
|
free_it:
|
|
call glrudel ; delete object from LRU chain
|
|
ifdef WOW
|
|
; No need to call DPMI to unpagelock
|
|
else
|
|
movzx cx, ds:[esi].pga_pglock
|
|
jcxz fo_notpglocked
|
|
mov bx, ds:[esi].pga_handle
|
|
unpagelock:
|
|
DPMICALL 0005h
|
|
loop unpagelock
|
|
endif
|
|
mov ds:[esi].pga_pglock, 0
|
|
fo_notpglocked:
|
|
push dx
|
|
xor edx,edx
|
|
call gmarkfree ; free the object
|
|
|
|
mov ebx, ds:[esi].pga_prev ; See if block can be
|
|
cmp ds:[ebx].pga_owner, GA_NOT_THERE; returned to win386
|
|
jne short fo_no_return
|
|
mov ecx, ds:[esi].pga_next
|
|
cmp ds:[ecx].pga_owner, GA_NOT_THERE
|
|
jne short fo_no_return
|
|
push ecx
|
|
mov ecx, ds:[ecx].pga_next
|
|
cmp ecx, ds:[ecx].pga_next ; Sentinel?
|
|
pop ecx
|
|
je short fo_no_return ; yes, keep this block
|
|
|
|
cCall UnlinkWin386Block
|
|
jmps fo_no_discard
|
|
|
|
fo_no_return:
|
|
call gwin386discard
|
|
fo_no_discard:
|
|
Handle_To_Sel dl
|
|
cCall AssociateSelector32,<dx,edi> ; Trash sel table entry
|
|
cCall FreeSelArray,<dx>
|
|
pop dx
|
|
xor ax,ax ;!!! just for now force success
|
|
free_object_exit:
|
|
ret
|
|
cEnd nogen
|
|
|
|
|
|
cProc free_object2,<PUBLIC,FAR>
|
|
cBegin nogen
|
|
call glrudel ; delete object from LRU chain
|
|
ifdef WOW
|
|
; No need to call DPMI to unpagelock
|
|
else
|
|
movzx cx, ds:[esi].pga_pglock
|
|
jcxz fo2_notpglocked
|
|
mov bx, ds:[esi].pga_handle
|
|
unpagelock2:
|
|
DPMICALL 0005h
|
|
loop unpagelock2
|
|
endif
|
|
mov ds:[esi].pga_pglock, 0
|
|
fo2_notpglocked:
|
|
xor edx,edx
|
|
call gmarkfree ; free the object
|
|
|
|
mov ebx, ds:[esi].pga_prev ; See if block can be
|
|
cmp ds:[ebx].pga_owner, GA_NOT_THERE; returned to win386
|
|
jne short fo2_no_return
|
|
mov ecx, ds:[esi].pga_next
|
|
cmp ds:[ecx].pga_owner, GA_NOT_THERE
|
|
jne short fo2_no_return
|
|
push ecx
|
|
mov ecx, ds:[ecx].pga_next
|
|
cmp ecx, ds:[ecx].pga_next ; Sentinel?
|
|
pop ecx
|
|
je short fo2_no_return ; yes, keep this block
|
|
|
|
cCall UnlinkWin386Block
|
|
jmps fo2_no_discard
|
|
|
|
fo2_no_return:
|
|
call gwin386discard
|
|
fo2_no_discard:
|
|
xor ax,ax ;!!! just for now force success
|
|
ret
|
|
cEnd nogen
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; free_handle ;
|
|
; ;
|
|
; Frees the given handle. ;
|
|
; ;
|
|
; Arguments: ;
|
|
; DS:SI = handle table entry address ;
|
|
; ;
|
|
; Returns: ;
|
|
; AX = 0 ;
|
|
; CX = AX ;
|
|
; ;
|
|
; Error Returns: ;
|
|
; AX = -1 ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; BX ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ? ;
|
|
; Calls: ;
|
|
; hfree ;
|
|
; History: ;
|
|
; ;
|
|
; Sat Sep 20, 1986 02:30:32p -by- David N. Weise [davidw] ;
|
|
; Moved it from gfree. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
;cProc free_handle,<PUBLIC,NEAR>
|
|
;cBegin nogen
|
|
; xor ax,ax
|
|
; or si,si
|
|
; jz short free_handle_exit
|
|
; push bx
|
|
; mov bx,si
|
|
; call hfree
|
|
; pop bx
|
|
;free_handle_exit:
|
|
; ret
|
|
;cEnd nogen
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; gfreeall ;
|
|
; ;
|
|
; Frees all global objects that belong to the given owner. It first ;
|
|
; loops through the global heap freeing objects and then loops through ;
|
|
; the handle table freeing handles of discarded objects. ;
|
|
; ;
|
|
; Arguments: ;
|
|
; DX = owner field value to match ;
|
|
; DS:DI = address of global heap info ;
|
|
; ;
|
|
; Returns: ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; CX,ES,SI ;
|
|
; ;
|
|
; Calls: ;
|
|
; free_object ;
|
|
; henum ;
|
|
; hfree ;
|
|
; ;
|
|
; History: ;
|
|
; ;
|
|
; Fri Sep 19, 1986 05:46:52p -by- David N. Weise [davidw] ;
|
|
; Added this nifty comment block. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
cProc gfreeall,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
|
|
mov esi,[di].phi_first ; ES:DI points to first arena entry
|
|
mov cx,[di].hi_count ; CX = #entries in the arena
|
|
free_all_objects:
|
|
push cx
|
|
call free_object ; Free object if matches owner
|
|
pop cx
|
|
mov esi,ds:[esi].pga_next ; Move to next block
|
|
loop free_all_objects
|
|
|
|
; may go extra times, as CX does not track coalescing done by gfree,
|
|
; but no big whoop
|
|
|
|
|
|
push ax
|
|
push ebx
|
|
push edi
|
|
CheckKernelDS FS
|
|
ReSetKernelDS FS
|
|
movzx ecx, SelTableLen
|
|
shr ecx, 2
|
|
mov edi, SelTableStart
|
|
mov esi, edi
|
|
smov es, ds
|
|
UnSetKernelDS FS
|
|
free_all_handles_loop:
|
|
movzx eax, dx
|
|
repne scas dword ptr es:[edi] ; Isn't this easy?
|
|
jne short we_be_done
|
|
lea eax, [edi-4]
|
|
sub eax, esi
|
|
shl ax, 1
|
|
or al, SEG_RING
|
|
|
|
lar ebx, eax
|
|
test bh,DSC_PRESENT ; segment present?
|
|
jnz short free_all_handles_loop ; yes, not a handle
|
|
test ebx,DSC_DISCARDABLE SHL 16 ; discardable?
|
|
jz short free_all_handles_loop ; no, nothing to free
|
|
cCall FreeSelArray,<ax>
|
|
mov dword ptr [edi-4], 0 ; Remove owner from table
|
|
jmps free_all_handles_loop
|
|
we_be_done:
|
|
pop edi
|
|
pop ebx
|
|
pop ax
|
|
|
|
gfreeall_done:
|
|
ret
|
|
cEnd nogen
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; glock ;
|
|
; ;
|
|
; Increment the lock count of an object in handle table entry ;
|
|
; ;
|
|
; Arguments: ;
|
|
; BX = handle to global object ;
|
|
; CH = handle table flags ;
|
|
; CL = lock count for moveable objects ;
|
|
; DX = segment address of object ;
|
|
; DS:DI = address of master object ;
|
|
; ES:DI = arena header ;
|
|
; ;
|
|
; Returns: ;
|
|
; CX = updated lock count ;
|
|
; DX = pointer to client area ;
|
|
; ;
|
|
; Error Returns: ;
|
|
; ZF = 1 if count overflowed. ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; AX ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ;
|
|
; Calls: ;
|
|
; nothing ;
|
|
; History: ;
|
|
; ;
|
|
; Fri Sep 19, 1986 05:38:57p -by- David N. Weise [davidw] ;
|
|
; Added this nifty comment block. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
cProc glock,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
push ax
|
|
inc ch ; Increment lock count
|
|
jz short overflow ; All done if overflow
|
|
mov ds:[esi].pga_count,ch ; Update lock count
|
|
glockerror:
|
|
overflow:
|
|
pop ax
|
|
ret
|
|
cEnd nogen
|
|
|
|
|
|
;-----------------------------------------------------------------------;
|
|
; gunlock ;
|
|
; ;
|
|
; Decrement the lock count of an object. ;
|
|
; ;
|
|
; Arguments: ;
|
|
; BX = handle to global object ;
|
|
; CH = handle table flags ;
|
|
; CL = lock count for moveable objects ;
|
|
; CX = handle table flags and lock count for moveable objects ;
|
|
; DS:DI = address of master object ;
|
|
; ES:DI = arena header ;
|
|
; ;
|
|
; Returns: ;
|
|
; CX = updated lock count, no underflow ;
|
|
; ;
|
|
; Registers Preserved: ;
|
|
; ;
|
|
; Registers Destroyed: ;
|
|
; ;
|
|
; Calls: ;
|
|
; glrutop ;
|
|
; History: ;
|
|
; ;
|
|
; Fri Sep 19, 1986 04:39:01p -by- David N. Weise [davidw] ;
|
|
; Added this nifty comment block. ;
|
|
;-----------------------------------------------------------------------;
|
|
|
|
cProc gunlock,<PUBLIC,NEAR>
|
|
cBegin nogen
|
|
push ax
|
|
mov ax,bx
|
|
dec ch ; Decrement usage count
|
|
cmp ch,0FFh-1 ; ff -> fe, 0 -> ff
|
|
jae short count_zero ; Return if pinned, or was already 0
|
|
dec ds:[esi].pga_count ; Non-zero update lock count
|
|
jnz short count_positive ; All done if still non-zero
|
|
test cl,GA_DISCARDABLE ; Is this a discardable handle?
|
|
jz short count_zero ; No, all done
|
|
call glrutop ; Yes, bring to top of LRU chain
|
|
count_zero:
|
|
xor cx,cx
|
|
count_positive:
|
|
pop ax
|
|
ret
|
|
cEnd nogen
|
|
|
|
sEnd CODE
|
|
|
|
end
|