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

918 lines
21 KiB
NASM

TITLE GMOREMEM - More Global Memory Manager procedures
.xlist
include kernel.inc
include tdb.inc
include newexe.inc
include protect.inc
.list
.286
DataBegin
externB Kernel_Flags
;externW hGlobalHeap
externW pGlobalHeap
externW curTDB
externW headTDB
externW cur_dos_PDB
externW Win_PDB
externW MaxCodeSwapArea
DataEnd
externFP CalcMaxNRSeg
sBegin CODE
assumes CS,CODE
assumes DS,NOTHING
assumes ES,NOTHING
;externW MyCSDS
if SDEBUG
externNP DebugMovedSegment
endif
;if SWAPPRO
;externNP WriteDiscardRecord
;endif
externNP real_dos
externNP gcompact
externNP get_physical_address
externNP get_arena_pointer
externNP GetAccessWord
externFP CVWBreak
externNP GrowHeap
if LDCHKSUM
externNP ZeroSegmentChksum
endif
;-----------------------------------------------------------------------;
; genter ;
; ;
; Enters a critical region for the global heap. ;
; ;
; Arguments: ;
; none ;
; ;
; Returns: ;
; DS:DI = address of GlobalInfo for global heap ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; All ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Mon Sep 29, 1986 03:05:33p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc genter,<PUBLIC,NEAR>
cBegin nogen
SetKernelDS
mov ds,pGlobalHeap
UnSetKernelDS
xor di,di
inc [di].gi_lrulock
ret
cEnd nogen
;-----------------------------------------------------------------------;
; gleave ;
; ;
; Leaves a critical region for the global heap. ;
; ;
; Arguments: ;
; DS:DI = address of GlobalInfo for global heap ;
; ;
; Returns: ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Mon Sep 29, 1986 03:07:53p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc gleave,<PUBLIC,NEAR>
cBegin nogen
dec ds:[gi_lrulock]
jnz gl_ret
test ds:[gi_flags],GIF_INT2
jz gl_ret
and ds:[gi_flags],NOT GIF_INT2
pushf
call CVWBreak
gl_ret: ret
cEnd nogen
;-----------------------------------------------------------------------;
; gavail ;
; ;
; Gets the available memory. ;
; ;
; Arguments: ;
; DX = number of paragraphs wanted ;
; DS:DI = master object ;
; Returns: ;
; AX = #paragraphs available for the biggest block ;
; DX = 0 ;
; ;
; Error Returns: ;
; none ;
; ;
; Registers Preserved: ;
; DI,DS ;
; Registers Destroyed: ;
; BX,CX,SI,ES ;
; Calls: ;
; gcompact ;
; ;
; History: ;
; Thu Apr 06, 1988 08:00:00a -by- Tim Halvorsen [iris] ;
; Fix bug in computation of space available when GA_NOT_THERE object ;
; resides above discard fence. ;
; ;
; Wed Oct 15, 1986 05:09:27p -by- David N. Weise [davidw] ;
; Moved he_count to ga_count. ;
; ;
; Sat Sep 27, 1986 09:37:27a -by- David N. Weise [davidw] ;
; Reworked it. ;
;-----------------------------------------------------------------------;
cProc gavail,<PUBLIC,NEAR>
cBegin nogen
mov byte ptr [di].gi_cmpflags,0
call gcompact
mov es,[di].hi_first
xor dx,dx
loop_for_beginning:
xor ax,ax
mov es,es:[di].ga_next ; Next block
cmp es:[di].ga_sig,GA_ENDSIG ; End of arena?
jnz blecher
jmp all_done
blecher:
cmp es:[di].ga_owner,di ; Free?
jz how_big_is_it ; Yes
mov si,es:[di].ga_handle
or si,si ; Fixed?
jz loop_for_beginning ; Yes, next block
cmp es:[di].ga_count,0 ; Locked?
jne loop_for_beginning ; Yes, next block
push ax
cCall GetAccessWord,<si>
test ah, DSC_DISCARDABLE
pop ax
jz loop_for_beginning ; No, next block
how_big_is_it:
mov ax,es:[di].ga_size ; Use this size
loop_for_bigness:
mov es,es:[di].ga_next ; Next block
cmp es:[di].ga_owner,di ; Free?
jz include_his_size ; Yes, include size
cmp es:[di].ga_sig,GA_ENDSIG ; End of arena?
jz all_done
mov si,es:[di].ga_handle
or si,si ; Fixed?
jz end_of_bigness ; Yes, stop looking
cmp es:[di].ga_count,0 ; Locked?
jne end_of_bigness ; Yes, stop looking
push ax
cCall GetAccessWord,<si>
test ah, DSC_DISCARDABLE
pop ax
jz dont_include_him ; No, dont include in count then
include_his_size: ; Free or Discardable
add ax,es:[di].ga_size ; Increase availabe space
inc ax ; by size of this block
dont_include_him: ; Moveable
jmp loop_for_bigness
end_of_bigness:
mov si,es
cmp es:[di].ga_owner,GA_NOT_THERE
jnz nothing_not_there
push ax
push dx
cCall get_physical_address,<si>
sub ax,word ptr [di].gi_disfence_lo
sbb dx,word ptr [di].gi_disfence_hi
jae fence_lower
pop dx
pop ax
jmps nothing_not_there
fence_lower:
REPT 4
shr dx,1
rcr ax,1
ENDM
mov si,ax
pop dx
pop ax
sub ax,si
jmps all_done_1
nothing_not_there:
cmp ax,dx ; Did we find a bigger block?
jbe blech_o_rama ; No, then look again
mov dx,ax ; Yes, remember size
blech_o_rama:
jmp loop_for_beginning
all_done:
sub ax,[di].gi_reserve ; In case lower_land has little free.
jb all_done_2
all_done_1:
cmp ax,dx ; Did we find a bigger block?
ja gcsize
all_done_2:
mov ax,dx
gcsize:
inc ax ; Cheap bug fix
or ax,ax ; zero mem available?
jz gcfinal ; yes, return 0
dec ax ; Dont be too exact
jz gcfinal
dec ax
jz gcfinal
dec ax
gcfinal:
and al,GA_MASK ; round down to nearest alignment
xor dx,dx
ret
cEnd nogen
;-----------------------------------------------------------------------;
; greserve ;
; ;
; Sets the size of the discardable code reserve area. ;
; If the new size is larger than the old size, it checks to see ;
; if there is enough room to support the new size. ;
; ;
; Arguments: ;
; AX = new greserve size ;
; ;
; Returns: ;
; CX = the largest greserve we can get ;
; AX != 0 success ;
; AX = 0 failure ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; DI,SI,DS ;
; ;
; Registers Destroyed: ;
; BX,DX,ES ;
; ;
; Calls: ;
; genter ;
; gcompact ;
; will_gi_reserve_fit ;
; gleave ;
; ;
; History: ;
; ;
; Sun 14-Jan-1990 13:42:59 -by- David N. Weise [davidw] ;
; The greserve must be twice as big as the largest non-resident ;
; segment, because restarting a NP fault requires both caller and ;
; callee to be in memory. ;
; ;
; Tue May 19, 1987 00:03:08p -by- David N. Weise [davidw] ;
; Made it far. ;
; ;
; Sat Sep 27, 1986 01:03:08p -by- David N. Weise [davidw] ;
; Made it perform according to spec and added this nifty comment block. ;
;-----------------------------------------------------------------------;
assumes ds,nothing
assumes es,nothing
cProc greserve,<PUBLIC,FAR>,<si,di> ; FAR because of the gcompact below
localW new_size
cBegin
call genter
add ax, ax ; Double required so caller & callee
add ax,GA_ALIGN ; both fit in memory
and al,GA_MASK
mov new_size,ax
mov si, ax
mov cx,[di].gi_reserve ; Get old size.
jcxz first_time ; Old size equal to zero?
call will_gi_reserve_fit
jnc new_okay
call GrowHeap
call will_gi_reserve_fit
jnc new_okay
try_compacting:
mov dx,new_size
call gcompact ; Try compacting to get new reserve
call will_gi_reserve_fit
jnc new_okay
will_not_fit:
xor ax,ax
jmps gr_exit
first_time: ; Things are strange at first
mov bx, ax
xor cx, cx
REPT 4
shl bx, 1
rcl cx, 1
ENDM
cCall get_physical_address,<[di].hi_last>
sub ax, bx
sbb dx, cx ; Address of gi_reserve
new_okay:
mov word ptr [di].gi_disfence_lo,ax
mov word ptr [di].gi_disfence_hi,dx
mov dx,new_size
mov [di].gi_reserve,dx
gr_exit:
call gleave
cEnd
;-----------------------------------------------------------------------;
; will_gi_reserve_fit ;
; ;
; See if new size okay by scanning arena backwards. ;
; ;
; Arguments: ;
; SI = new greserve size ;
; DS:DI = master object ;
; ;
; Returns: ;
; DX:AX = gi_disfence ;
; BX = amount of NOT THERE memory, such as EGA ;
; CX = the largest greserve we can get ;
; CF = 0 new size ok ;
; CF = 1 new size NOT ok ;
; ES:DI => arena below CODE segments ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; SI,DI,DS ;
; ;
; Registers Destroyed: ;
; ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Sun Jul 12, 1987 08:13:23p -by- David N. Weise [davidw] ;
; Added EMS support. ;
; ;
; Sat Sep 27, 1986 01:03:57p -by- David N. Weise [davidw] ;
; Rewrote it. ;
;-----------------------------------------------------------------------;
cProc will_gi_reserve_fit,<PUBLIC,NEAR>
localB got_half
localW second_block
localW largest_block
localW fence_block
localW fence_offset
cBegin
xor ax,ax
xor bx,bx
mov got_half, al
mov fence_block, ax
mov largest_block, ax
mov second_block, ax
mov es,[di].hi_last
mov cx,[di].hi_count
how_much_space_loop:
mov es,es:[di].ga_prev
how_much_space_loop1:
cmp di,es:[di].ga_owner ; If free then include.
jz include_this_one
test es:[di].ga_flags,GA_DISCCODE ; If disccode then include.
jz end_block
include_this_one:
add ax, es:[di].ga_size ; Total the size of this block
inc ax ; include arena header
loop how_much_space_loop
end_block:
cmp ax, largest_block ; Track two largest blocks
jbe not_largest
push largest_block
mov largest_block, ax
pop second_block
jmps not_second
not_largest:
cmp ax, second_block
jbe not_second
mov second_block, ax
not_second:
cmp fence_block, di
jne skip_not_there
cmp si, ax ; This block big enough?
jbe set_fence
cmp got_half, 0
jne skip_not_there ; Already split requirement
shr si, 1
cmp si, ax ; Half fits?
ja no_good
mov got_half, 1 ; yes, will use this block
jmps skip_not_there
set_fence:
sub ax, si
mov fence_offset, ax ; Offset from next arena
mov ax, es:[di].ga_next
mov fence_block, ax
jmps skip_not_there
no_good:
shl si, 1 ; no, keep trying for full amount
skip_not_there:
xor ax, ax ; zero count
cmp es:[di].ga_owner,GA_NOT_THERE
jne thats_all_folks
skip_not_there_loop:
add bx,es:[di].ga_size
dec cx
mov es, es:[di].ga_prev
cmp es:[di].ga_owner, GA_NOT_THERE ; skip all not there blocks
je skip_not_there_loop
jmps how_much_space_loop1
thats_all_folks:
mov cx, second_block ; Return max of twice second
shl cx, 1 ; largest block and the largest
jnc not_monstrously_huge ; block
mov cx, 0FFFDh
jmps time_to_go
not_monstrously_huge:
cmp cx, largest_block
jae time_to_go
mov cx, largest_block
time_to_go:
cmp got_half, 1
jne si_ok
shl si, 1
si_ok:
cmp cx, si ; Set CARRY for failure
jc failed
push bx
push cx
cCall get_physical_address,<fence_block>
mov bx, fence_offset
xor cx, cx
REPT 4
shl bx, 1
rcl cx, 1
ENDM
add ax, bx
adc dx, cx ; DX:AX is new fence
pop cx
pop bx
clc ; Success
failed:
cEnd
;-----------------------------------------------------------------------;
; gnotify ;
; ;
; This is where the hard job of updating the stacks and thunks happens. ;
; We only walk stacks and thunks for code and data (defined by being ;
; LocalInit'ed), not for resources or task allocated segments. ;
; ;
; Arguments: ;
; AL = message code ;
; BX = handle ;
; CX = optional argument ;
; ES = address of arena header ;
; ;
; Returns: ;
; AX = return value from notify proc or AL ;
; DS = current DS (i.e. if DATA SEG was moved then DS is updated. ;
; ZF = 1 if AX = 0 ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; DI,SI ;
; ;
; Registers Destroyed: ;
; BX,CX,DX,ES ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Wed Jun 24, 1987 03:08:39a -by- David N. Weise [davidw] ;
; Adding support for Global Notify. ;
; ;
; Wed Dec 03, 1986 01:59:27p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc gnotify,<PUBLIC,NEAR>
cBegin nogen
push si
push di
xor ah,ah
mov di,cx
mov cx,ax
mov si,bx
loop notify_discard
errnz <GN_MOVE - 1>
;-----------------------------------------------------------------------;
; Here for a segment that moved. ;
;-----------------------------------------------------------------------;
why_bother:
mov cx,ds
cmp cx,si ; Did we move DS?
jne notify_exit_0 ; No, continue
notify_exit_0:
jmp notify_exit
notify_discard:
;;;;;; loop notify_exit
loop notify_exit_0
errnz <GN_DISCARD - 2>
;-----------------------------------------------------------------------;
; Here for a segment discarded. ;
;-----------------------------------------------------------------------;
xor ax,ax
test bl,1
jnz notify_exit_0 ; SDK is wrong, can't free fixed.
push ax
cCall GetAccessWord,<bx>
test ah, DSC_DISCARDABLE
pop ax
jz notify_exit_0
test es:[ga_flags],GAH_NOTIFY
jnz @F
jmp dont_bother_asking
@@:
push bx
push cx
push dx
mov ax,1
mov es,es:[ga_owner]
cmp es:[ne_magic],NEMAGIC
jz dont_ask ; doesn't belong to a particular task
mov ax,es
SetKernelDS es
push HeadTDB ; Look for TDB that owns this block.
find_TDB:
pop cx
jcxz dont_ask
mov es,cx
UnSetKernelDS es
push es:[TDB_next]
cmp ax,es:[TDB_PDB]
jnz find_TDB
pop cx ; clear stack in 1 byte
mov cx,word ptr es:[TDB_GNotifyProc][0] ; paranoia
cmp cx,word ptr es:[TDB_GNotifyProc][2]
jz dont_ask
push ds
SetKernelDS
;;; xor cx, cx ; bad Hack for Legend
;;; cmp ax, cur_dos_PDB ; Are we on task's PDB
;;; je nothing_to_hack ; Yes, fine
;;; mov cx, cur_dos_PDB ; No, save the PDB to restore
;;; push ax
;;; push bx
;;; mov bx, ax
;;; mov cur_dos_PDB, bx ; And point us to the task's
;;; mov Win_PDB, bx
;;; mov ah, 50h
;;; call real_dos
;;; pop bx
;;; pop ax
;;;nothing_to_hack:
;;; push cx ; Save old PDB (if any)
push Win_PDB ; Save current PDB
mov Win_PDB, ax ; Ensure it is the task's
or Kernel_Flags[1],kf1_GLOBALNOTIFY
push es:[TDB_GNotifyProc]+2 ; push addr of func to call onto stack
push es:[TDB_GNotifyProc]
push bx ; push arg for notify proc
mov ax,ss ; Zap segment regs so DS
mov ds,ax ; doesn't get diddled by callee
mov es,ax
mov bx,sp
call dword ptr ss:[bx]+2
add sp,4 ; clean up stack.
SetKernelDS
and Kernel_Flags[1],not kf1_GLOBALNOTIFY
pop Win_PDB ; Restore PDB
;;; pop bx ; Saved PDB
;;; or bx, bx ; Was there one?
;;; jz @F ; Nope.
;;; push ax
;;; mov ah, 50h ; Set it back...
;;; mov cur_dos_PDB, bx
;;; mov Win_PDB, bx
;;; call real_dos
;;; pop ax
;;;@@:
pop ds
UnSetKernelDS
dont_ask:
pop dx
pop cx
pop bx
or ax,ax ; Well, can we?
jz notify_exit
dont_bother_asking:
cCall get_arena_pointer,<si>
mov es,ax
mov es,es:[ga_owner]
cmp es:[ne_magic],NEMAGIC
jnz not_in_exe
mov di,es:[ne_segtab]
mov cx,es:[ne_cseg]
jcxz not_in_exe
; test es:[ne_flags],NENOTP
; jnz pt0a
; inc bx
pt0a:
cmp bx,es:[di].ns_handle
jz pt0b
add di,SIZE NEW_SEG1
loop pt0a
jmps not_in_exe
pt0b:
and byte ptr es:[di].ns_flags,not NSLOADED ; Mark as not loaded.
not_in_exe:
why_bother_again:
xor di,di
if SDEBUG
cCall DebugMovedSegment,<si,di>
endif
if LDCHKSUM
call ZeroSegmentChksum ; SI points to segment
endif
mov ax,1
notify_exit:
or ax,ax
pop di
pop si
ret
gn_error:
kerror 0FFh,<gnotify - cant discard segment>,si,si
xor ax,ax
jmp notify_exit
cEnd nogen
;-----------------------------------------------------------------------;
; MemoryFreed ;
; ;
; This call is apps that have a GlobalNotify procedure. Some apps ;
; may shrink the segment instead of allowing it to be discarded, or ;
; they may free other blocks. This call tells the memory manager ;
; that some memory was freed somewhere. ;
; ;
; Arguments: ;
; WORD = # paragraphs freed ;
; ;
; Returns: ;
; DX:AX = amount of memory that still needs freeing ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Fri 08-Apr-1988 10:25:55 -by- David N. Weise [davidw] ;
; Wrote it! ;
;-----------------------------------------------------------------------;
cProc MemoryFreed,<PUBLIC,FAR>
parmW memory_freed
cBegin
xor ax,ax
SetKernelDS
test Kernel_Flags[1],kf1_GLOBALNOTIFY
jz mf_done
mov ds,pGlobalHeap
UnSetKernelDS
mov ax,memory_freed
or ax,ax
jz mf_inquire
or ds:[hi_ncompact],1 ; Remember we discarded something
sub ds:[hi_distotal],ax ; Have we discarded enough yet?
ja mf_inquire
or ds:[hi_ncompact],10h ; To tell gdiscard that we're done.
mf_inquire:
mov ax,ds:[hi_distotal] ; Have we discarded enough yet?
mf_done:
xor dx,dx
cEnd
;-----------------------------------------------------------------------;
; SetSwapAreaSize ;
; ;
; Sets the current task's code swap area size. ;
; ;
; Arguments: ;
; WORD == 0 then current size is just returned ;
; != 0 number paragraphs wanted for swap area ;
; Returns: ;
; AX = Size actually set ;
; DX = Max size you can get ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; ;
; History: ;
; ;
; Thu Apr 18, 1988 08:00:00a -by- T.H. SpeedWagon [-????-] ;
; Move routine into CODE segment, so applications can query the ;
; code working set value without paging in the NRESCODE segment. ;
; ;
; Thu Apr 23, 1987 09:36:00p -by- R.E.O. SpeedWagon [-???-] ;
; Added ability to get size without setting it. ;
; ;
; Wed Dec 03, 1986 10:52:16p -by- David N. Weise [davidw] ;
; Rewrote it. ;
;-----------------------------------------------------------------------;
cProc SetSwapAreaSize,<PUBLIC,FAR>
parmW nParas
localW MxCodeSwapArea
cBegin
SetKernelDS
mov ax,nParas
xor bx,bx
mov cx,MaxCodeSwapArea
mov MxCodeSwapArea,cx ; avoid a segment load later
cmp ax,cx
jbe requested_size_OK
mov ax,cx
requested_size_OK:
mov ds,CurTDB
UnSetKernelDS
mov ds,ds:[bx].TDB_pModule ; Get exe header address
or ax,ax ; just a query request?
jz got_it ; yes
cmp ax,ds:[bx].ne_swaparea
jz got_it ; just want what we have?
ja calc_it
; To restart a NP fault both caller and callee must be in memory.
; So we cheat here, and set the minimum to be twice the rmode number.
; We have to prevent the app from setting the swap area too
; small. So we recalculate the minimum needed. We should
; do this is rmode as well, but the chance of messing up is
; much larger in pmode, where new app writers are apt to
; be very cavalier with lots of memory.
; We should keep another NE variable instead of always
; recalculating the largest ne_seg BUT at this late
; date this is simpler!
push ax
xor ax,ax ; No max yet.
mov cx,ds:[bx].ne_cseg
mov bx,ds:[bx].ne_segtab
jcxz no_NR_segments
get_largest_loop:
test ds:[bx].ns_flags,NSDATA ; Code segment?
jnz not_disc_code
.errnz NSCODE
test ds:[bx].ns_flags,NSDISCARD ; Discardable?
jz not_disc_code ; No, ignore.
mov dx,ds:[bx].ns_minalloc ; Yes, get size
add dx,0Fh ; in paragraphs
shr dx,4
cmp ax,dx ; Biggest NR Seg?
jae not_disc_code
mov ax,dx
not_disc_code:
add bx,SIZE NEW_SEG1
loop get_largest_loop
no_NR_segments:
shl ax,1
mov cx,ax
pop ax
cmp ax,cx
ja calc_it
mov ax,cx
calc_it:
xchg ds:[ne_swaparea],ax
push ax
call CalcMaxNRSeg
pop dx
or ax,ax
jnz got_it
mov ds:[ne_swaparea],dx
got_it:
mov ax,ds:[ne_swaparea]
mov dx,MxCodeSwapArea
cmp cx,dx
ja cant_get_that_much
mov dx,cx
cant_get_that_much:
cEnd
;
; SetReserve - Sets gi_reserve to the given number of paragraphs
;
; Registers Destroyed:
; AX, BX, DX
;
cProc SetReserve,<PUBLIC,FAR>,<di>
parmW paras
cBegin
SetKernelDS
mov ds, pGlobalHeap
UnSetKernelDS
xor di,di
mov bx, paras
mov ds:[di].gi_reserve, bx
shl bx, 4
cCall get_physical_address,<ds:[di].hi_last>
sub ax, bx
sbb dx, di
mov ds:[di].gi_disfence_lo, ax
mov ds:[di].gi_disfence_hi, dx
cEnd
sEnd CODE
end