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, 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, 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, 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, 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, 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, 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,, ; 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, 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, 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, cBegin nogen push si push di xor ah,ah mov di,cx mov cx,ax mov si,bx loop notify_discard errnz ;-----------------------------------------------------------------------; ; 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 ;-----------------------------------------------------------------------; ; 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, 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, 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, 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,,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, 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, 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,, 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, sub ax, bx sbb dx, di mov ds:[di].gi_disfence_lo, ax mov ds:[di].gi_disfence_hi, dx cEnd sEnd CODE end