windows-nt/Source/XPSP1/NT/base/mvdm/wow16/kernel31/linterf.asm
2020-09-26 16:20:57 +08:00

1581 lines
38 KiB
NASM

TITLE LINTERF - Local memory allocator, interface procedures
.xlist
include tdb.inc
include kernel.inc
.list
.errnz la_prev ; This code assumes la_prev = 0
if KDEBUG
CheckHeap MACRO n
local a
extrn CheckLocalHeap:NEAR
cCall CheckLocalHeap
or ax,ax
jz a
or ax,ERR_LMEM
kerror <>,<&n: Invalid local heap>
a:
endm
else
CheckHeap MACRO n
endm
endif
externW pLocalHeap
externFP <GlobalHandle,GlobalReAlloc,GlobalSize,GlobalCompact,GlobalFlags>
externFP <GlobalLock,GlobalUnlock>
externFP <GlobalMasterHandle,GetCurrentTask>
externFP FarValidatePointer
externFP DibRealloc
DataBegin
externW curTDB
;externW MyCSDS
if KDEBUG
externW DebugOptions
endif
DataEnd
sBegin CODE
assumes CS,CODE
assumes DS,NOTHING
assumes ES,NOTHING
externNP <halloc,lhfree,lhdref,hthread> ; LHANDLE.ASM
externNP <ljoin,lrepsetup,lzero> ; LALLOC.ASM
externNP <lalloc,lfree,lfreeadd,lfreedelete> ; LALLOC.ASM
externNP <lcompact,lshrink> ; LCOMPACT.ASM
if KDEBUG
externNP <CheckLAllocBreak>
endif ;KDEBUG
;-----------------------------------------------------------------------;
; lenter ;
; ;
; Enters a critical region for the local heap. ;
; ;
; Arguments: ;
; DS = automatic data segment containing local heap ;
; ;
; Returns: ;
; DS:DI = address of LocalInfo for local heap ;
; (li_lock field has been incremented) ;
; ;
; Error Returns: ;
; CX == 1 if heap is busy ;
; ;
; Registers Preserved: ;
; AX,BX,DX,SI,ES ;
; ;
; Registers Destroyed: ;
; CX ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Sun Oct 13, 2086 09:27:27p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc lenter,<PUBLIC,NEAR>
cBegin nogen
mov di,pLocalHeap
mov cx,1
xchg [di].li_lock,cx
jcxz enter1
; Should really do a WaitEvent
if KDEBUG
xor bx,bx
kerror ERR_LMEMCRIT,<lenter: local heap is busy>,bx,cx
endif
enter1: ret
cEnd nogen
;-----------------------------------------------------------------------;
; lleave ;
; ;
; Leaves a critical region for the local heap. ;
; ;
; Arguments: ;
; DS = automatic data segment containing local heap ;
; ;
; Returns: ;
; DS:DI = address of LocalInfo for local heap ;
; (li_lock field has been cleared) ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; AX,BX,DX,SI,ES ;
; ;
; Registers Destroyed: ;
; CX ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Sun Oct 13, 2086 09:30:01p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc lleave,<PUBLIC,NEAR>
cBegin nogen
mov di,pLocalHeap
xor cx,cx
xchg ds:[di].li_lock,cx
jcxz leave1
ret
leave1:
; Should really do a PostEvent
if KDEBUG
kerror ERR_LMEMCRIT,<lleave: local heap was not busy>
endif
cEnd nogen
;-----------------------------------------------------------------------;
; lhexpand ;
; ;
; Expands a local handle table. ;
; ;
; Arguments: ;
; CX = #handle entries to allocate ;
; DS:DI = local info structure ;
; ;
; Returns: ;
; AX = address of handle table block of requested size ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; ;
; Registers Destroyed: ;
; BX,CX,DX,ES ;
; ;
; Calls: ;
; lalloc ;
; hthread ;
; ;
; History: ;
; ;
; Tue Oct 14, 1986 01:48:21p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc lhexpand,<PUBLIC,NEAR>
cBegin nogen
xor ax,ax ; Allocate fixed local block
mov bx,cx
inc bx ; plus 1 for word at beginning & end
shl bx,1
shl bx,1
errnz <4-SIZE LocalHandleEntry>
push cx
call lalloc
pop cx
or ax,ax
jz lhfail
mov bx,ax
xchg [di].hi_htable,bx
push di ; Save DI
mov di,ax
mov ds:[di],cx
inc di
inc di
call hthread
mov ds:[di],bx ; Store pointer to next block
pop di ; Restore DI
lhfail: mov cx,ax
ret
cEnd nogen
;-----------------------------------------------------------------------;
; lalign ;
; ;
; Aligns the size request for a local item to a multiple of 4 bytes. ;
; ;
; Arguments: ;
; CF = 0 ;
; BX = #bytes ;
; CF = 1 get max amount ;
; ;
; Returns: ;
; DX = #bytes aligned to next higher multiple of 4 ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Tue March 10, 1987 -by- Bob Gunderson [bobgu] ;
; To accomidate free list, must impose a minimum block size to prevent ;
; allocating a block on top of the extended header of previous block. ;
; ;
; Tue Oct 14, 1986 01:56:42p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc lalign,<PUBLIC,NEAR>
cBegin nogen
jnc align0
mov bx,LA_MASK
align0: cmp bx,SIZE LocalArenaFree
jae align2
mov bx,SIZE LocalArenaFree ; Must impose a minimum size
align2: lea dx,[bx+LA_ALIGN]
and dl,LA_MASK
cmp dx,bx ; Test for overflow
jae align1 ; No, continue
mov dx,LA_MASK ; Yes, return largest possible size
align1: ret
cEnd nogen
;-----------------------------------------------------------------------;
; ldref ;
; ;
; Dereferences a local handle. ;
; ;
; Arguments: ;
; SI = handle ;
; ;
; Returns: ;
; AX = address of client data ;
; BX = address of arena header ;
; CH = lock count or zero for fixed objects ;
; SI = handle table entry address or zero for fixed objects ;
; ZF = 1 if NULL handle passed in ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; ;
; Registers Destroyed: ;
; CL ;
; ;
; Calls: ;
; lhdref ;
; ;
; History: ;
; ;
; Tue Oct 14, 1986 01:58:58p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc ldref,<PUBLIC,NEAR>
cBegin nogen
xor cx,cx ; Zero lock count
mov ax,si ; Return handle if fixed object
test al,LA_MOVEABLE ; Is it moveable?
jnz dref3 ; Yes, go dereference handle
xor si,si ; Set SI to zero for fixed objects
or ax,ax ; No, were we passed NULL?
jz dref2 ; Yes, then all done
dref1: mov bx,ax ; No, return BX pointing
and bl,LA_MASK ; ...to arena header
sub bx,la_fixedsize ; Leaving ZF = 0
dref2: ret
dref3: call lhdref ; Get true address in AX
; and lock count in CH
ife KDEBUG
jz done ; Compute arena header if valid true address
mov bx,ax ; See if arena header points to
sub bx,SIZE LocalArena
done: ret ; No, return with ZF = 1
else
test cl,LHE_DISCARDED ; Discarded?
jnz dref5 ; Yes, all done
or ax,ax ; Is there a true address?
jz dref4 ; No, then must be error
mov bx,ax ; See if arena header points to
sub bx,SIZE LocalArena
cmp [bx].la_handle,si ; handle table entry?
je dref5 ; Yes, continue
dref4: xor bx,bx
kerror ERR_LMEMHANDLE,<LDREF: Invalid local handle>,bx,si
xor ax,ax
dref5: or ax,ax
ret
endif
cEnd nogen
;-----------------------------------------------------------------------;
; lnotify ;
; ;
; Calls the local heap's notify proc (if any). ;
; ;
; Arguments: ;
; AL = message code ;
; BX = handle or largest free block ;
; CX = optional argument ;
; ;
; Returns: ;
; AX = return value from notify proc or AL ;
; ZF = 1 if AX = 0 ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; ;
; Registers Destroyed: ;
; BX,CX,DX,ES ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Tue Oct 14, 1986 02:03:14p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc lnotify,<PUBLIC,NEAR>
cBegin nogen
cmp word ptr [di+2].li_notify,0
je notify1
xor ah,ah
cCall [di].li_notify,<ax,bx,cx>
notify1:
or ax,ax
ret
cEnd nogen
; The remainder of this file implements the exported interface to the
; local memory manager. A summary follows:
;
; HANDLE far PASCAL LocalAlloc( WORD, WORD );
; HANDLE far PASCAL LocalReAlloc( HANDLE, WORD, WORD );
; HANDLE far PASCAL LocalFree( HANDLE );
; WORD far PASCAL LocalSize( HANDLE );
; char * far PASCAL LocalLock( HANDLE );
; BOOL far PASCAL LocalUnlock( HANDLE );
; WORD far PASCAL LocalCompact( WORD );
; WORD far PASCAL LocalShrink( HANDLE , WORD );
; FARPROC far PASCAL LocalNotify( FARPROC );
; #define LocalDiscard( h ) LocalReAlloc( h, 0, LMEM_MOVEABLE )
; BOOL far PASCAL LocalInit( char *, char * );
;
; extern WORD * PASCAL pLocalHeap;
;
; #define dummy 0
; #define LocalFreeze( dummy ) ( *(pLocalHeap+1) += 1 )
; #define LocalThaw( dummy ) ( *(pLocalHeap+1) -= 1 )
;
cProc ILocalAlloc,<PUBLIC,FAR>,<si,di>
parmW flags
parmW nbytes
cBegin
WOWTrace "LocalAlloc(#AX,#BX)",<<ax,flags>,<bx,nbytes>>
CheckHeap LocalAlloc
call lenter ; About to modify memory arena
jcxz la_ok
xor ax, ax
jmp la_crit
la_ok:
mov ax,flags ; Allocate space for object
test al,LA_NOCOMPACT
jz all0
inc [di].hi_freeze
all0: mov bx,nbytes
or bx,bx ; Zero length?
jnz alloc0 ; No, continue
and ax,LA_MOVEABLE ; Yes, moveable?
jz alloc1 ; No, return error
call halloc ; Yes, allocate handle
or ax,ax ; failure??
jz alloc1 ; yep... return a NULL
xor byte ptr [bx].lhe_address,LA_MOVEABLE ; and zero address field
or byte ptr [bx].lhe_flags,LHE_DISCARDED ; and mark as discarded
jmps alloc1 ; all done
alloc0: test al,LA_MOVEABLE ; Is this a moveable object?
jz dont_need_handle
push ax
push bx
call halloc ; Allocate handle first.
or ax,ax
jnz all2 ; error?
pop bx ; yes, DON'T destroy the NULL in AX
pop bx
jmps alloc1
all2:
pop bx
pop ax
; pop bx
; pop ax
; jcxz alloc1
push cx ; this is handle
call lalloc
pop si ; get handle in index register
jnz got_space
call lhfree ; free the allocated handle
jmps alloc1
got_space:
mov [si].lhe_address,ax ; Store address away.
mov bx,ax ; Store back link to handle in header
mov [bx-SIZE LocalArena].la_handle,si ; Mark as moveable block
or byte ptr [bx-SIZE LocalArena].la_prev,LA_MOVEABLE
mov ax,si ; return the handle
and dh,LHE_DISCARDABLE ; Discardable object?
jz alloc1 ; No, continue
mov [si].lhe_flags,dh ; Yes, save discard level in handle
jmps alloc1 ; table entry
dont_need_handle:
call lalloc
alloc1: test byte ptr flags,LA_NOCOMPACT
jz all1
dec [di].hi_freeze
all1: call lleave ; Arena is consistent now
la_crit:
ifdef WOW
; We don't want this debug spew
else
or ax,ax
jnz @F
KernelLogError DBF_WARNING,ERR_LALLOC,"LocalAlloc failed" ; LocalAlloc failure
xor ax, ax ; preserve the return value
@@:
endif
mov cx,ax ; Let caller do jcxz to test failure
WOWTrace "LocalAlloc: #AX"
cEnd
cProc ILocalReAlloc,<PUBLIC,FAR>,<si,di>
parmW h
parmW nbytes
parmW rflags
cBegin
WOWTrace "LocalReAlloc(#AX,#BX,#CX)",<<ax,h>,<bx,nbytes>,<cx,rflags>>
CheckHeap LocalReAlloc
call lenter ; About to modify memory arena
jcxz lr_ok
xor ax, ax
jmp lr_crit
lr_ok:
test byte ptr rflags,LA_NOCOMPACT
jz rel0
inc [di].hi_freeze
rel0:
mov si,h ; Dereference handle
call ldref
jz racreate ; If zero handle, check for re-creation.
test byte ptr rflags,LA_MODIFY ; Want to modify handle table flags
jnz ramodify ; Yes, go do it
mov si,bx ; SI = arena header
mov bx,ax ; Compute address of new next header
mov dx,nbytes ; assuming there is room.
cmp dx,SIZE LocalArenaFree ; Minimum size must be large enough
jae @F ; to a hold free header.
mov dx,SIZE LocalArenaFree
@@: add bx,dx
call lalign
mov bx,[si].la_next ; Get address of current next header
cmp nbytes,0 ; Are we reallocing to zero length?
jne raokay ; No, continue
jcxz radiscard ; Yes, discard if not locked
rafail:
ifdef WOW
; We don't want this debug spew
else
KernelLogError DBF_WARNING,ERR_LREALLOC,"LocalReAlloc failed"
endif
xor ax,ax
jmp raexit
radiscard:
; 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,LA_MOVEABLE ; Did they want to discard?
jz rafail ; No, then return failure.
mov al,LN_DISCARD ; AL = discard message code
xor cx,cx ; CX = disc level of 0 (means realloc)
mov bx,h ; BX = handle
call lnotify ; See if okay to discard
jz rafail ; No, do nothing
xor ax,ax ; Yes, free client data
mov bx,si
call lfree
jz rax ; Return NULL if freed a fixed block
mov [si].lhe_address,ax ; No, zero addr in handle table entry
or [si].lhe_flags,LHE_DISCARDED ; and mark as discarded
jmps rasame ; Return original handle, except
; LocalLock will now return null.
ramodify:
mov ax,rflags ; Get new flags
or si,si ; Moveable object?
jz rasame ; No, all done
and [si].lhe_flags,not LHE_USERFLAGS ; Clear old flags
and ah,LHE_USERFLAGS; Get new flags
or [si].lhe_flags,ah ; Store new flags in handle table entry
jmps rasame
racreate:
test cl,LHE_DISCARDED ; Is this a discarded handle?
jz rasame ; No, return original value
mov bx,nbytes ; BX = new requested size
push si ; save handle
mov ax,LA_MOVEABLE ; Reallocating a moveable object
or ax,rflags ; ...plus any flags from the caller
call lalloc ; Allocate a new block
pop si ; restore existing handle
jz rafail
xor [si].lhe_flags,LHE_DISCARDED ; and mark as not discarded
jmp ram2
raokay:
; Here if not trying to realloc this block to zero
; SI = arena header of current block
; AX = client address of current block
; BX = arena header of next block
; CH = lock count of current block
; DX = new next block, based on new requested size
cmp dx,bx ; Are we growing or shrinking?
ja ragrow ; We are growing
rashrink: ; We are shrinking
; Here to shrink a block
; SI = arena header of current block
; BX = arena header of next block
; DX = new next block, based on new requested size
push si
mov si,dx ; SI = new next block
add dx,LA_MINBLOCKSIZE ; Test for small shrinkage
jnc @F ; No overflow
pop bx ; Overflowed, obviously no room!
jmp rasame ; Clear stack and return same handle
@@: cmp dx,bx ; Is there room from for free block?
pop bx ; BX = current block
jae rasame ; No, then no change to make
; Splice extra free space into arena
mov cx,si ; [si].la_next = [bx].la_next
xchg [bx].la_next,cx ; [bx].la_next = si
mov [si].la_prev,bx ; [si].la_prev = bx
xchg si,cx
and [si].la_prev,LA_ALIGN
jz splice1 ; If free then coelesce
inc [di].hi_count ; No, adding new arena entry
jmps splice2
splice1:
; next block is free, must make the new block a larger free block
; first remove the current free block [SI] from the free list
xchg bx,si
call lfreedelete
xchg bx,si
mov si,[si].la_next
and [si].la_prev,LA_ALIGN
splice2:
or [si].la_prev,cx ; [[si].la_next].la_prev = si
xchg si,cx
mov [si].la_next,cx
mov bx,si ; BX = new block
xor si,si ; don't know where to insert
call lfreeadd ; add to free list
rasame:
mov ax,h ; Return the same handle
rax: jmps raexit ; All done
; Here to try to grow the current block
; AX = client address of current block
; SI = arena header of current block
; BX = arena header of next block
; DX = new next block, based on new requested size
ragrow:
if KDEBUG
call CheckLAllocBreak
jc rafail1
endif
test byte ptr [bx].la_prev,LA_BUSY ; Is next block free?
jnz ramove ; No, try to move the current block
cmp dx,[bx].la_next ; Yes, is free block big enough?
ja ramove ; No, try to move the current block
mov cx,bx ; Yes, save free block address in CX
call ljoin ; and attach to end of current block
test rflags,LA_ZEROINIT ; Zero fill extension?
jz ranz ; No, continue
call lzero ; Yes, zero fill
ranz:
jmp rashrink ; Now shrink block to correct size
; Here to try to move the current block
; AX = client address of current block
; SI = arena header of current block
; CH = lock count of current block
ramove:
mov dx,rflags ; get the passed in flags
mov bx,LA_MOVEABLE ; Determine if okay to move this guy
jcxz ramove1 ; Continue if this handle not locked
test dx,bx ; Locked. Did they say move anyway?
jnz ramove1 ; Yes, go do it
rafail1:jmp rafail
ramove1:
or dx,bx ; make sure moveable bit set
test h,bx ; Is it a moveable handle?
jnz ram1 ; Yes, okay to move
test rflags,bx ; No, did they say it's okay to move?
jz rafail1 ; No, then fail
xor dx,bx ; turn off moveable bit
ram1:
; We do this because the lalloc can move the old block to a
; block that is larger than the requested new block. This can
; happen if we LocalCompact durring the lalloc call. (bobgu 8/27/87)
mov bx,[si].la_next
sub bx,ax ; # client bytes in current block
push bx ; save it for later
mov ax,dx ; AX = allocation flags
mov bx,nbytes ; BX = new requested size
call lalloc ; Allocate a new block
pop cx ; CX = client size of old block
jz rafail1
push cx ; save it away again
push ax ; Call notify proc
mov cx,ax ; with new location
mov bx,h ; handle
mov al,LN_MOVE
call lnotify ; Notify client of new location
mov si,h
call ldref ; BX = old arena header address
mov si,ax ; SI = old client address
pop ax ; AX = new client address
pop cx ; get back size of old client
; mov cx,[bx].la_next ; Compute length of old client data
; sub cx,si
call lrepsetup ; Setup for copy of words
push di
mov di,ax ; DI = new client data address
rep movsw ; Copy old client data to new area
pop di ; Restore DI
call lfree ; Free old block
jz raexit
ram2:
mov [si].lhe_address,ax ; Set new client data address
xchg ax,si ; Return original handle
; Set back link to handle in new block
or byte ptr [si-SIZE LocalArena].la_prev,LA_MOVEABLE
mov [si-SIZE LocalArena].la_handle,ax
raexit:
test byte ptr rflags,LA_NOCOMPACT
jz rel1
dec [di].hi_freeze
rel1:
call lleave ; Arena is consistent now
lr_crit:
mov cx,ax ; Let caller do jcxz to test failure
WOWTrace "LocalReAlloc: #AX"
cEnd
cProc ILocalFree,<PUBLIC,FAR>,<si,di>
parmW h
cBegin
WOWTrace "LocalFree(#AX)",<<ax,h>>
call lenter ; About to modify memory arena
jcxz lf_ok
xor ax, ax
jmp lf_crit
lf_ok:
CheckHeap LocalFree
mov si,h ; Dereference handle
call ldref
jz free1 ; Free handle if object discarded
if KDEBUG
push ds
SetKernelDS
mov ds,curTDB
assumes ds, nothing
cmp ds:[TDB_ExpWinVer],201h
pop ds
jb dont_do_error_checking
or ch,ch ; No, is the handle locked?
jz freeo ; Yes, then don't free
xor bx,bx
kerror ERR_LMEMUNLOCK,<LocalFree: freeing locked object>,bx,h
mov si,h ; Dereference handle again
call ldref
freeo:
dont_do_error_checking:
endif
call lfree ; No, free the object
free1: call lhfree ; and any handle
freex: call lleave ; Arena is consistent now
lf_crit:
WOWTrace "LocalFree: #AX"
cEnd
cProc ILocalSize,<PUBLIC,FAR>
; parmW h
cBegin nogen
; CheckHeap LocalSize
push si
mov si,sp
mov si,ss:[si+6] ; Dereference handle
call ldref ; into BX
jz size1 ; Done if AX = zero
sub ax,[bx].la_next ; Otherwise size =
neg ax ; - (client address - next block address)
size1: mov cx,ax ; Let caller do jcxz to test failure
pop si
ret 2
cEnd nogen
cProc ILocalFlags,<PUBLIC,FAR>
; parmW h
cBegin nogen
; CheckHeap LocalFlags
push si
mov si,sp
mov si,ss:[si+6] ; Dereference handle
call ldref ; into BX
mov cx,si
jcxz flags1 ; Done if not moveable
mov cx,word ptr [si].lhe_flags ; Return flags and lock count
flags1:
xchg cl,ch ; Return lock count in low half
mov ax,cx ; Let caller do jcxz to test failure
pop si
ret 2
cEnd nogen
if KDEBUG
cProc ILocalLock,<PUBLIC,FAR>,<si>
parmW h
cBegin
WOWTrace "LocalLock(#AX)",<<ax,h>>
; CheckHeap LocalLock
mov si,h
call ldref ; SI = handle table entry
jz lock2 ; Done if invalid handle or discarded
or si,si
jz lock2 ; or if not moveable
inc [si].lhe_count ; Increment usage count
jnz lock1
xor cx,cx
kerror ERR_LMEMLOCK,<LocalLock: Object usage count overflow>,cx,h
dec [si].lhe_count ; Keep pinned at max value
lock1: mov ax,[si].lhe_address ; Return true address in AX
lock2:
or ax, ax
jnz @F
KernelLogError DBF_WARNING,ERR_LLOCK,"LocalLock failed"
xor ax,ax ; Get back the NULL value in ax
@@: mov cx,ax ; Let caller do jcxz to test failure
WOWTrace "LocalLock: #AX"
cEnd
else
cProc ILocalLock,<PUBLIC,FAR>
; parmW h
cBegin nogen
mov bx,sp ; Get handle parameter from stack
mov bx,SS:[bx+4]
mov ax,bx ; Return in AX
test bl,LA_MOVEABLE ; Test for moveable (also null)
jz lock2 ; Return if not moveable or null
test [bx].lhe_flags,LHE_DISCARDED ; Return zero if discarded
jnz lock1
inc [bx].lhe_count ; Increment usage count
jz lock3 ; Special case if overflow
lock1:
mov ax,[bx].lhe_address ; Return true address in AX or zero
lock2: mov cx,ax ; Let caller do jcxz to test failure
ret 2
lock3:
dec [bx].lhe_count ; Ooops, keep pinned at max value
jmp lock1
cEnd nogen
endif
if KDEBUG
cProc ILocalUnlock,<PUBLIC,FAR>,<si>
parmW h
cBegin
WOWTrace "LocalUnlock(#AX)",<<ax,h>>
; CheckHeap LocalUnlock
mov si,h
call ldref ; SI = handle table entry
jz unlock1 ; Done if invalid handle or discarded
xor ax,ax
or si,si
jz unlock1 ; or if not moveable
or ch, ch
jz unlockerr
dec ch ; Decrement usage count
cmp ch,0FFh-1 ; 0 -> ff, ff -> fe
jae unlock1 ; Return if pinned or already unlocked
mov [si].lhe_count,ch
; mov ax,bx
mov al,ch
cbw
jmps unlock1
unlockerr:
xor cx,cx
kerror ERR_LMEMUNLOCK,<LocalUnlock: Object usage count underflow>,cx,h
xor ax,ax
unlock1:
mov cx,ax ; Let caller do jcxz to test failure
WOWTrace "LocalUnlock: #AX"
cEnd
else
cProc ILocalUnlock,<PUBLIC,FAR>
; parmW h
cBegin nogen
mov bx,sp ; Get handle parameter from stack
mov bx,SS:[bx+4]
xor ax,ax
test bl,LA_MOVEABLE ; Test for moveable (also null)
jz unlock1 ; Return if not moveable or null
mov cx,word ptr [bx].lhe_flags
errnz <2-lhe_flags>
errnz <3-lhe_count>
and cl,LHE_DISCARDED
jnz unlock1 ; Return if discarded
dec ch ; Decrement usage count
cmp ch,0FFh-1 ; 0 -> ff, ff -> fe
jae unlock1 ; Return if pinned or already unlocked
mov [bx].lhe_count,ch
; mov ax,bx
mov al,ch
cbw
unlock1:
mov cx,ax ; Let caller do jcxz to test failure
ret 2
cEnd nogen
endif
cProc LocalHandle,<PUBLIC,FAR>
; parmW h
cBegin nogen
mov bx,sp
mov bx,SS:[bx+4]
test bl,LA_MOVEABLE
jz lh1
mov ax,bx
mov bx,[bx-SIZE LocalArena].la_handle
cmp [bx].lhe_address,ax
je lh1
xor bx,bx
lh1:
mov ax,bx
ret 2
cEnd nogen
cProc LocalCompact,<PUBLIC,FAR>,<si,di>
parmW minBytes
cBegin
CheckHeap LocalCompact
call lenter ; About to modify memory arena
jcxz lc_ok
xor ax, ax
jmp lc_crit
lc_ok:
mov bx,minBytes
clc
call lalign
call lcompact
or ax,ax
jz compact1
sub ax,SIZE LocalArena ; Reduce available size by header size
compact1:
call lleave ; Arena is consistent now
lc_crit:
cEnd
cProc LocalShrink,<PUBLIC,FAR>,<si,di>
parmW hseg
parmW wsize
cBegin
mov ax,hseg
or ax,ax ; use heap in current DS ?
jz ls_useds ; yes
; Use the segment handle passed
push ax
call GlobalHandle
or ax,ax ; valid handle ?
jz ls_errexit ; no....
mov ds,dx ; set the proper DS
ls_useds:
; check the heap and lock it
CheckHeap LocalShrink
call lenter ; About to modify memory arena
jcxz ls_ok
xor ax, ax
jmp short ls_crit
ls_ok:
mov bx,wsize ; get requested min size
call lshrink ; Let's get small
; AX = new local heap size
call lleave ; Arena is consistent now
ls_crit:
ls_errexit:
cEnd
cProc LocalNotifyDefault,<PUBLIC,FAR>,<si,di>
parmW msg
parmW handle ; or largest free block
parmW arg1
cBegin
mov ax,msg
or ax,ax
jnz dlnexit1
cCall GlobalHandle,<ds>
or ax,ax
jz dlnexit1
; Fix for FORMBASE who uses a fixed
; segment for a local heap. This blows
; up if we cause the calling segment
; to be discarded since the DS saved by
; Local???? is a fixed segment which
; SearchStack can't handle. Confused?
; This was not a problem in 2.x because
; 2.x failed to grow a fixed object.
; Using a fixed segment for a local heap
; is not valid and this is really a problem
; with FORMBASE.
mov si,ax
cCall GlobalFlags,<si> ; Get flags
xchg ah, al
push ax
cCall GlobalSize,<si>
sub ax, handle ; Temorarily subtract out largest free
pop bx ; Get flags in BX
xor cx,cx
add ax,arg1
adc dx,cx ; Fail if DS attempts to grow
jnz dln_too_large ; beyond 64k.
add ax,18h ; since li_extra isn't guaranteed
adc dx,cx
jnz dln_too_large
add ax,[di].li_extra
adc dx,cx ; Fail if DS attempts to grow
jnz @F ; beyond 64k.
add ax, handle ; add back largest free
adc dx, cx
jnz @F
cmp ax,0FFF0h
jbe dln0
@@: mov ax,0FFF0h
xor dx,dx
jmps dln0
dln_too_large:
xor ax,ax
dlnexit1:
jmp dlnexit
dln0:
test si,GA_FIXED ; Is DS fixed?
jnz dln1 ; Yes, must grow in place
cmp bh,1 ; No, is lock count 1?
jne dln1 ; No must grow in place if locked
or cl,GA_MOVEABLE ; Yes, okay to move even though locked
dln1:
push bx
grow_DS:
cCall GlobalReAlloc,<si,dxax,cx>
pop bx
jcxz dlnexit
push bx
cCall GlobalSize,<ax>
or dx,dx ; Did we get rounded up >= 64K?
jz @F ; No, OK
mov ax,0FFFFh ; This only happens under 386pmode
@@:
mov bx,ax
sub bx,la_freefixedsize
and bl,LA_MASK
mov di,ds:[pLocalHeap]
mov si,[di].hi_last
mov [bx].la_next,bx
mov [bx].la_prev,si
or byte ptr [bx].la_prev,LA_BUSY
mov [si].la_next,bx
; Maintain the free list.
mov ax,[si].la_free_prev
mov [bx].la_free_prev,ax
mov [bx].la_free_next,bx
mov [bx].la_size,WORD PTR la_freefixedsize
push si
mov si,ax
mov [si].la_free_next,bx
pop si
mov [di].hi_last,bx
inc [di].hi_count
mov bx,si
call lfree
; Don't do this... (bobgu 8/4/87)
; stc
; call lalign
; call lcompact
mov ax,1
pop bx
mov ax,1
dlnexit:
cEnd
cProc LocalNotifyDib,<PUBLIC,FAR>,<si,di>
parmW msg
parmW handle ; or largest free block
parmW arg1
cBegin
mov ax,msg
or ax,ax
jnz dlnexit1dib
cCall GlobalHandle,<ds>
or ax,ax
jz dlnexit1dib
; Fix for FORMBASE who uses a fixed
; segment for a local heap. This blows
; up if we cause the calling segment
; to be discarded since the DS saved by
; Local???? is a fixed segment which
; SearchStack can't handle. Confused?
; This was not a problem in 2.x because
; 2.x failed to grow a fixed object.
; Using a fixed segment for a local heap
; is not valid and this is really a problem
; with FORMBASE.
mov si,ax
cCall GlobalFlags,<si> ; Get flags
xchg ah, al
push ax
cCall GlobalSize,<si>
sub ax, handle ; Temorarily subtract out largest free
pop bx ; Get flags in BX
xor cx,cx
add ax,arg1
adc dx,cx ; Fail if DS attempts to grow
jnz dib_too_large ; beyond 64k.
add ax,18h ; since li_extra isn't guaranteed
adc dx,cx
jnz dib_too_large
add ax,[di].li_extra
adc dx,cx ; Fail if DS attempts to grow
jnz @F ; beyond 64k.
add ax, handle ; add back largest free
adc dx, cx
jnz @F
cmp ax,0FFF0h
jbe dln0dib
@@: mov ax,0FFF0h
xor dx,dx
jmps dln0dib
dib_too_large:
xor ax,ax
dlnexit1Dib:
jmp dlnexitdib
dln0dib:
test si,GA_FIXED ; Is DS fixed?
jnz dln1dib ; Yes, must grow in place
cmp bh,1 ; No, is lock count 1?
jne dln1dib ; No must grow in place if locked
or cl,GA_MOVEABLE ; Yes, okay to move even though locked
dln1dib:
push bx
cCall DibReAlloc,<ds,ax>
or ax,ax
jz dlnexitdib0
cCall GlobalSize,<si>
or dx,dx ; Did we get rounded up >= 64K?
jz @F ; No, OK
mov ax,0FFFFh ; This only happens under 386pmode
@@:
mov bx,ax
sub bx,la_freefixedsize
and bl,LA_MASK
mov di,ds:[pLocalHeap]
mov si,[di].hi_last
mov [bx].la_next,bx
mov [bx].la_prev,si
or byte ptr [bx].la_prev,LA_BUSY
mov [si].la_next,bx
; Maintain the free list.
mov ax,[si].la_free_prev
mov [bx].la_free_prev,ax
mov [bx].la_free_next,bx
mov [bx].la_size,WORD PTR la_freefixedsize
push si
mov si,ax
mov [si].la_free_next,bx
pop si
mov [di].hi_last,bx
inc [di].hi_count
mov bx,si
call lfree
; Don't do this... (bobgu 8/4/87)
; stc
; call lalign
; call lcompact
mov ax,1
dlnexitdib0:
pop bx
dlnexitdib:
cEnd
sEnd CODE
externFP Far_lalign
externFP Far_lrepsetup
if KDEBUG
externFP Far_lfillCC
endif
sBegin NRESCODE
assumes CS,NRESCODE
assumes DS,NOTHING
assumes ES,NOTHING
cProc ILocalNotify,<PUBLIC,FAR>
; parmD lpProc
cBegin nogen
mov bx,sp
mov ax,SS:[bx+4]
mov dx,SS:[bx+6]
mov bx,ds:[pLocalHeap]
xchg word ptr [bx].li_notify,ax
xchg word ptr [bx].li_notify+2,dx
ret 4
cEnd nogen
cProc LocalInit,<PUBLIC,FAR>,<ds,si,di>
parmW pseg
parmW pstart
parmW pend
cBegin
; Init current DS if none passed.
mov cx,pseg
jcxz li1
mov ds,cx
li1:
; Place local arena info at the beginning
mov bx,pstart
or bx,bx
jnz li2
cCall GlobalSize,<ds>
; Here we must do a little checking... The global memory manager may have
; rounded up the size on us so that the DS is >=64K! If this has happened,
; we can simply ignore the extra (since app can't look at it anyway) and
; pretend the DS is actually 0000FFFFH bytes big.
or dx,dx ; Did we get rounded up >= 64K?
jz li1a ; No, OK
mov ax,0FFFFH ; Pretend heap is 64K-1
li1a:
mov bx,ax
dec bx
xchg pend,bx
sub bx,pend
neg bx ; BX = first byte in arena
li2:
clc
call Far_lalign
mov bx,dx ; DX = addr of first block to use
; OK, so here's how it works... In order to keep a free block list, there
; are 4 blocks allocated initially. First is a dummy marker block that is
; free but marked busy. Second is the local arena information block which
; is a standard busy block. Third is the really big free block. And lastly
; is another busy type free block. All free blocks have an extended header
; in order to keep a free block list.
; Reserve room for the first free busy block.
lea bx,[bx].la_freefixedsize ; move over first free block
push dx ; preserve first free block address
clc
call Far_lalign
mov bx,dx ; BX = arena info block address
pop dx ; DX = first block address
push dx ; * Save the address of the first
push bx ; * two block on the stack for later
; DI = client address of info block.
lea di,[bx].la_fixedsize
xor ax,ax ; Zero local arena info
mov cx,pend
cmp bx,cx ; start > end?
;;;;;;; jae lix
jb li21
pop dx ; clean up the stack first
pop dx
jmp lix
li21:
sub cx,di
call Far_lrepsetup
push di
rep stosw
pop di
lea bx,[di].SIZE LocalInfo
if KDEBUG
mov [di].hi_pstats,bx
add bx,SIZE LocalStats
ifdef DISABLE
; Set the heap checking flag.
push es
push dx
cCall GlobalMasterHandle
mov es,dx
mov ax,es:[hi_check]
pop dx
pop es
else
push es
push _DATA
pop es
assumes es,DATA
;
; hi_check = 0;
; if (DebugOptions & DBO_CHECKHEAP)
; {
; hi_check = 1
; if (DebugOptions & DBO_CHECKFREE)
; hi_check = 2;
; }
;
xor ax,ax
test es:DebugOptions,DBO_CHECKHEAP
jz @F
inc ax
test es:DebugOptions,DBO_CHECKFREE
jz @F
inc ax
@@:
assumes es,NOTHING
pop es
endif
mov [di].hi_check,ax
endif
; set the rest of the heap info
mov byte ptr [di].hi_hdelta,32
mov byte ptr [di].hi_count,4
mov [di].hi_first,dx
mov word ptr [di].li_notify,codeOFFSET LocalNotifyDefault
mov word ptr [di].li_notify+2,codeBASE
mov word ptr [di].hi_hexpand,codeOFFSET lhexpand
mov [di].li_extra,512
mov [di].li_sig,LOCALHEAP_SIG
; Move SI to first aligned block after info record
clc
call Far_lalign
mov si,dx
; Move BX to last aligned block at end of local heap
mov bx,pend ; BX = end address
sub bx,la_freefixedsize ; Make room for an arena header
and bl,LA_MASK ; Align downwards to 4 byte boundary
cmp bx, si ; If heap is too small, the
ja @f ; supposed free block could be
xor ax, ax ; beyond the end block
jmp lix
@@:
; Setup reserved pointer in DS to point to LocalInfo
mov [di].hi_last,bx
mov ds:[pLocalHeap],di
; Finish linking entries in the local heap.
;
; DX = address of the first arena block. Free busy marker.
; DI = address of the element which contains the local heap
; information struc.
; SI = address of large free block that is the initial heap.
; BX = address of a zero length arena element that is used to
; mark the end of the local heap.
;
;
; This last arena element is always busy, with a length of
; zero. This allows us to always calculate the length of an
; arena entry by subtracting the address of the arena element
; from the hi_next field of the arena element (see lsize subr)
pop di ; Get the address of the first two
pop dx ; ... blocks off the stack
; Setup first block in arena, busy free block.
xchg bx,dx ;bx = first block (temporarily)
lea ax,[bx+LA_BUSY] ; ...as a busy block
mov [bx].la_prev,ax ; point to self
mov [bx].la_next,di ; point to next
mov [bx].la_free_prev,bx ; previous free block is self
mov [bx].la_free_next,si ; next free is large block
mov [bx].la_size,WORD PTR la_freefixedsize ; set the block size
xchg bx,dx ; back to normal
; Setup block that contains info structure.
xchg dx,bx
lea ax,[bx+LA_BUSY] ; ...as a busy block
xchg dx,bx
mov [di].la_prev,ax ; point to previous block
mov [di].la_next,si ; point to next block
; Setup large free block with extended free block header.
mov [si].la_prev,di ; Point middle block to first and
mov [si].la_next,bx ; last blocks
mov [si].la_free_prev,dx ; previous free block
mov [si].la_free_next,bx ; next free block
mov ax,bx
sub ax,si
mov [si].la_size,ax ; length of free block
if KDEBUG
xchg si,bx ; BX = large free block
call Far_lfillCC ; Fill with 0CCh
xchg si,bx
endif
; Setup last free block with extended header.
mov [bx].la_next,bx ; Point last block to middle and itself
lea ax,[si+LA_BUSY] ; ...as a busy block
mov [bx].la_prev,ax
mov [bx].la_free_prev,si ; previous free block
mov [bx].la_free_next,bx ; next free block is self
mov [bx].la_size,WORD PTR la_freefixedsize ; set the block size
; Set the minimum size in arena header.
mov bx,ds:[pLocalHeap]
mov ax,[bx].hi_last
add ax,SIZE LocalArenaFree
sub ax,[bx].hi_first
mov [bx].li_minsize,ax
cCall GlobalLock,<ds> ; Make moveable DS initially locked.
; (see LocalNotifyDefault)
mov al,1
lix:
mov cx,ax
cEnd
;-----------------------------------------------------------------------;
; LocalCountFree ;
; ;
; Return the count of free bytes in the local heap. This was motivated ;
; by the InitApp routines that want at least 4K available to continue ;
; the app running. ;
; ;
; Arguments: ;
; DS = heap segment ;
; ;
; Returns: ;
; AX = free bytes in local heap ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; all ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Sat Aug 15, 1987 04:35:55p -by- Bob Gunderson [bobgu] ;
; Wrote it. ;
;-----------------------------------------------------------------------;
cProc LocalCountFree,<PUBLIC,FAR>
cBegin nogen
push di
push si
mov di,pLocalHeap
lea si, [di].li_sig
cCall FarValidatePointer,<ds,si>
or ax, ax ; OK pointer?
jz countexit ; no, just exit
xor ax,ax ; start with 0 bytes free
cmp [di].li_sig, LOCALHEAP_SIG
jne countexit ; No local heap!!
mov si,[di].hi_last ; sentenal block
mov di,[di].hi_first ; arena header
mov di,[di].la_free_next ; first free block
countloop:
cmp di,si
jz countexit
add ax,[di].la_size ; count size of this block
sub ax,SIZE LocalArenaFree ; less block overhead
mov di,[di].la_free_next ; next free block
jmp countloop
countexit:
pop si
pop di
ret
cEnd nogen
;-----------------------------------------------------------------------;
; LocalHeapSize ;
; ;
; Return the # bytes allocated to the local heap. ;
; ;
; Arguments: ;
; DS = heap segment ;
; ;
; Returns: ;
; AX = size of local heap ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; CX,DX,SI,SI,DS,ES ;
; ;
; Registers Destroyed: ;
; BX ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Sat Aug 15, 1987 04:35:55p -by- Bob Gunderson [bobgu] ;
; Wrote it. ;
;-----------------------------------------------------------------------;
cProc LocalHeapSize,<PUBLIC,FAR>
cBegin nogen
mov bx,pLocalHeap
mov ax,[bx].hi_last
sub ax,[bx].hi_first
ret
cEnd nogen
;-----------------------------------------------------------------------;
; LocalHandleDelta ;
; ;
; Change the number of handles to allocate each time ;
; ;
; Arguments: ;
; delta = new # of handles or 0 ;
; DS = heap segment ;
; ;
; Returns: ;
; AX = new number of handles ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; CX,DX,SI,SI,DS,ES ;
; ;
; Registers Destroyed: ;
; BX ;
; ;
; Calls: ;
; ;
; History: ;
; ;
;-----------------------------------------------------------------------;
cProc LocalHandleDelta,<PUBLIC,FAR>
parmW delta
cBegin
mov bx,pLocalHeap
mov ax, delta
or ax, ax ; Zero means return present value
jz return_present_value
mov ax, delta
mov [bx].hi_hdelta, ax ; Set new value
return_present_value:
mov ax, [bx].hi_hdelta
cEnd
sEnd NRESCODE
end