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

2690 lines
83 KiB
NASM

PAGE ,132
TITLE DXMMGR - Dos Extender Memory Management Routines
; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
;****************************************************************
;* *
;* DXMMGR.ASM - Dos Extender Memory Manager *
;* *
;****************************************************************
;* *
;* Module Description: *
;* *
;* This module contains routines for maintaining a dynamic *
;* memory heap in extended memory for use in the Dos Extender. *
;* *
;* There are two kinds of objects in the memory heap. There *
;* are block headers and data blocks. The block headers are *
;* each 16 bytes long, and are organized in a doubly linked *
;* list. There is a segment descriptor in the global *
;* descriptor table for each block header, with each header *
;* containing the selectors for the next and previous block *
;* to form the list. Block headers can either control a free *
;* block or they can control a data block. In either case, *
;* the block immediately follows the header in memory, and *
;* the header contains a field that gives its size. Free *
;* blocks do not have a descriptor in the global descriptor *
;* table, but in use data blocks do. In the case where the *
;* in use data block is larger than 64k, there will be a *
;* contiguous range of selectors for the block. The block *
;* header contains a field giving the initial selector for *
;* the data block for in use blocks. *
;* *
;* A special type of free block serve as sentinels. They mark *
;* start and end of each physical block of memory available to *
;* the memory manager. They are identified by the reserved *
;* value of 0FFFE (start sentinel) and 0FFFF (end sentinel) in *
;* the selBlkOwner field. Unlike other blocks, they may not be *
;* moved without appeal to some higher level of memory *
;* allocation. *
;* *
;* Except for a pair of sentinels, a pair of blocks are *
;* contiguous in memory if and only if their headers are *
;* adjacent in the header chain. *
;* *
;* The blocks at the start and end of the header chain are *
;* sentinals. Their selectors are stored in global variables. *
;* The start and end of the block header chain are identified *
;* by NULL (0) pointers in the Next and Previous pointer fields*
;* respectively. *
;* *
;****************************************************************
;* Naming Conventions Used: *
;* *
;* The following hungarian prefixes are used in this module: *
;* lp - far pointer (selector:offset) *
;* bp - linear byte pointer (32 bit byte offset from *
;* the beginning of memory) *
;* p - near pointer (16 bit offset) *
;* sel - protected mode segment selector *
;* seg - real mode paragraph address *
;* cb - count of bytes *
;* id - generic byte that contains an ID code *
;* hmem - handle to XMS extended memory block *
;* *
;****************************************************************
;* Revision History: *
;* *
;* 11/28/90 amitc IntLowHeap allocates memory from Switcher *
;* if it can. *
;* *
;* 08/08/90 earleh DOSX and client privilege ring determined *
;* by equate in pmdefs.inc *
;* 05/09/90 jimmat Started VCPI changes. *
;* 05/09/90 jimmat Incorporated bug fix from CodeView folks, *
;* changes marked with [01] *
;* 06/23/89 jimmat: Added init of variable with heap handle *
;* 04/17/89 jimmat: Added routines for special low memory *
;* heap for use by network mapping *
;* 03/28/89 jimmat: Incorporated bug fixes from GeneA *
;* 02/10/89 (GeneA): changed Dos Extender from small model to *
;* medium model *
;* 12/07/88 (GeneA): moved SetupHimemDriver to dxinit.asm *
;* 08/30/88 (GeneA): created *
;* *
;****************************************************************
.286p
; -------------------------------------------------------
; INCLUDE FILE DEFINITIONS
; -------------------------------------------------------
include gendefs.inc
include segdefs.inc
include pmdefs.inc
include dpmi.inc
; -------------------------------------------------------
; GENERAL SYMBOL DEFINITIONS
; -------------------------------------------------------
; -------------------------------------------------------
; EXTERNAL SYMBOL DEFINITIONS
; -------------------------------------------------------
extrn XMScontrol:FAR
pmxmssvc macro fcn
ifnb <fcn>
mov ah, fcn
endif
call XMScontrol
endm
extrn FreeSelector:NEAR
extrn FreeSelectorBlock:NEAR
extrn AllocateSelector:NEAR
extrn AllocateSelectorBlock:NEAR
extrn GetSegmentAddress:NEAR
extrn SetSegmentAddress:NEAR
extrn DupSegmentDscr:NEAR
externFP NSetSegmentDscr
extrn RemoveFreeDescriptor:NEAR
extrn EnterProtectedMode:NEAR
extrn EnterRealMode:NEAR
extrn MoveMemBlock:NEAR
ifdef WOW_x86
externNP NSetSegmentAccess
externNP NSetSegmentBase
externNP NSetSegmentLimit
endif
DXDATA segment
extrn selPSPChild:WORD
extrn selGDT:WORD
extrn bpGDT:FWORD
ifdef WOW_x86
extrn FastBop:fword
endif
DXDATA ends
; -------------------------------------------------------
; DATA SEGMENT DECLARATIONS
; -------------------------------------------------------
DXDATA segment
; Minimum xmem allocation in K
; Note: This value MUST be a power of 2!!!
; Note: We will allocate a smaller block if only smaller blocks
; are available from xms
XMEM_MIN_ALLOC equ 256
extrn lpfnXmsFunc:DWORD
;
; These variables control access to the extended memory heap.
public cKbInitialHeapSize
cKbInitialHeapSize dw -1 ; Optional specification for first block
; of extended memory in KB.
public dsegCurrent
dsegCurrent dw 0 ; Paragraph count if the current block comes from DOS
public hmemHeap
hmemHeap dw ? ;XMS memory manager handle to the memory
; block containing the heap
bpHeapStart dd ? ;byte address of start of heap
; (points to block header of first heap block)
bpHeapEnd dd ? ;byte address of end of heap
; (points to block header of last heap block)
cbHeapMove dd ? ;number of bytes by which a subheap moved
public cbHeapSize,selLowHeapStart,selHiHeapStart,selHeapStart
cbHeapSize dd ? ;number of bytes in the heap
selHeapStart dw ? ;selector for the sentinel block at the
; start of the heap list
bpCurTop dd 0 ;current top of compacted heap
; (used during heap compaction)
cbHeapBlkMax dd ? ;size of largest free block seen while
; searching the heap
cbHeapFreeSpace dd ? ;total free space in the heap
selHiHeapStart dw ? ;selector for the sentinel block at the start
; of the higm memory heap
selLowHeapStart dw ? ;selector for the sentinel block at the start
; of the special low heap
segLowHeap dw ? ;segment address of low memory heap
selLowHeapEnd dw ? ;selector for the sentinel block at the end
; of the special low heap
fDosErr db ?
public hmem_XMS_Table,hmem_XMS_Count
hmem_XMS_Table dw CXMSBLOCKSDX DUP(0)
hmem_XMS_Count dw 0
public NoAsyncSwitching
NoAsyncSwitching db 0 ;0=> async switching allowed
public LowMemAllocFn,LowMemFreeFn
LowMemAllocFn dd 0
LowMemFreeFn dd 0
DXDATA ends
; -------------------------------------------------------
; MISCELLANEOUS DATA STRUCTURE DECLARATIONS
; -------------------------------------------------------
; ****** also in dxvcpi.asm ******
.ERRE CB_MEMHDR EQ 10h ;size of memory block header
; ****** also in dxvcpi.asm ******
; This segment declaration describes the format of the memory
; block header at the beginning of each object in the heap.
MEMHDR segment at 0 ;Analogous to MS-DOS arenas
fBlkStatus db ? ;Status bits about the arena block
cselBlkData db ? ;number of selectors used by the data block
; that this arena header controls
selBlkData dw ? ;initial selector for the data block that this
; arena header controls.
; (if the block is >64k, this is the first of the
; range of selectors assigned to the block)
selBlkOwner dw ? ;PSP selector of owner. 0 if free.
;
; !!!!! There is code in ModifyXmemBlock which depends on the previous
; !!!!! four fields (and only those) being at lower addresses than selBlkHdr.
;
selBlkHdr dw ? ;the selector of the block header. This points
; back at itself, so that we can find the selector
; to the header from its address
selBlkNext dw ? ; next block header selector
selBlkPrev dw ? ; previous block header selector
cbBlkLen LABEL DWORD ; Block size in bytes
hBlockHandle dw ? ; The handle returned by DOS or HIMEM
dsegBlock dw ? ; 0 => HIMEM ELSE DOS paragraph count
MEMHDR ends
; -------------------------------------------------------
subttl Initialization Routines
page
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; INITIALIZATION ROUTINES
; -------------------------------------------------------
;
; InitLowHeap -- Allocate and initialize the special low
; memory heap for use by network buffers
; and the like.
;
; Input: AX - number of K bytes of LOW heap needed
; (assumed to be < 1 Meg)
; Output: none
; Errors: CY set if error occurs
; Uses: All registers preserved
;
; Note: This routine must be called in protected mode
; and it assumes that interrupts should be enabled
; while it runs.
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public InitLowHeap
InitLowHeap proc near
ret
InitLowHeap endp
; -------------------------------------------------------
; AddToXmemHeap -- This routine will add a block of memory
; to the extended memory heap. It creates the sentinel header
; blocks and a header block for a free object containing
; the new memory.
;
; Input: CX - Least significant part of minimum block length required
; DX - Most significant part of minimum block length required
; Output: None
; Errors: None
; Uses:
; NOTES: This routine must be called in protected mode.
; With XMS-only providing memory, all XMS memory is allocated
; the first time this routine is called.
;
; If VCPI is active, and VCPI memory is being used, then
; enough memory is allocated from the server to satisfy
; the request. The allocation is handled in dxvcpi.asm.
;
assume ds:DGROUP,es:NOTHING,ss:NOTHING
cProc AddToXmemHeap,<NEAR,PUBLIC>,<ax,bx,cx,dx,si,es>
cBegin
;
; Add to requested size the size of our header overhead
;
add cx,3*CB_MEMHDR ; Adjust for header overhead
adc dx,0
;
; Round allocation up to next MIN_ALLOC size by first rounding up to the
; nearest 1K, then converting to K and rounding
;
add cx,1023
adc dx,0
shr cx,10
push dx
shl dx,6
or cx,dx ; cx is now size rounded to K
pop dx
add cx,XMEM_MIN_ALLOC - 1
adc dx,0
and cx,NOT (XMEM_MIN_ALLOC - 1) ; rounded to next higher MIN_ALLOC
; See How much memory is available from the HIMEM driver
atxh100:
pmxmssvc 8 ; query freespace available
cmp bl,0 ; Test for error
jnz atxhDone ; No luck - Try DOS
cmp ax,0
je atxhDone
; cmp ax,cx ; is largest block > allocation desired?
; jb atxh110 ; no, use largest block
; mov ax,cx ; yes, use desired alloc
atxh110:
mov dx,ax ; Load reported size of largest free block
mov bx,ax ; Calculate number of bytes available
shl bx,10
shr ax,6 ; AX:BX = available bytes
sub bx,3*CB_MEMHDR ; Adjust for header overhead
sbb ax,0
mov word ptr cbHeapSize,bx ; Save lower order bytes available
mov word ptr [cbHeapSize+2],ax ; Save higher order bytes available
pmxmssvc 9 ; Allocate the largest possible block
cmp ax,0 ; Check for error
jz atxhDone ; HIMEM driver speak with forked tongue
mov hmemHeap,dx ; Save the handle
pmxmssvc 0Ch ; Lock and query address
xchg bx,dx
mov dsegCurrent,0 ; Flag this allocate was not from DOS
cmp ax,0 ; Test result
jnz atxh300 ; Rejoin common code
Trace_Out "AddToXmemHeap: Lock of XMS handle #DX failed."
jmp atxhDone ; Jump on error
atxh300:
call StructureHeap ; Build sentinels and free block
mov ax,selHeapStart ; Remember the high heap start selector
mov selHiHeapStart,ax
; Done allocating memory.
atxhDone:
cEnd
; -------------------------------------------------------
; InitXmemHeap -- This routine will initialize the
; extended memory heap.
;
; Input:
; Output:
; Errors:
; Uses:
;
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public InitXmemHeap
InitXmemHeap:
ret
IFNDEF WOW_x86
; -------------------------------------------------------
; FreeXmemHeap -- This routine gives the xms memory back to
; the xms driver. The selectors for the heap are not
; freed. Shortly after we release the heap, the LDT will
; be reinitialized, and that will free the selectors
;
public FreeXmemHeap
FreeXmemHeap:
ret ; bugbug
ENDIF
; -------------------------------------------------------
; StructureHeap -- This routine creates the sentinel header
; blocks and a header block for a free object containing
; the new memory.
;
; Input: BX - Most significant part of heap block address
; DX - Least significant part of heap block address
;
; Output: None
; Errors: Carry flag set if there was not enough memory available
; Uses: BX, DX
;
; NOTE: This routine must be called in protected mode.
;
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public StructureHeap
StructureHeap proc near
push ax
push cx
push es
push bx ; most significant part of address
push dx ; least significant part of address
; Allocate selectors for the two sentinel blocks, and the initial free
; block and link them at the start of the chain.
;
; Create the end sentinel
add dx,word ptr cbHeapSize ;Calculate address of end sentinel
adc bx,word ptr [cbHeapSize + 2]
add dx,2*CB_MEMHDR
adc bx,0
call AddSelectorToChain ;Allocate/link the end sentinel
; Initialize the ending sentinel block.
assume es:MEMHDR
xor ax,ax
mov fBlkStatus,al
mov cselBlkData,al
mov selBlkData,ax
mov selBlkOwner,0FFFFh
mov cx,hmemHeap ; Save handle
mov hBlockHandle,cx
mov cx,dsegCurrent ; Save paragraph count
mov dsegBlock,cx
; Create the free block
pop dx ; least significant part of address
pop bx ; most significant part of address
add dx,CB_MEMHDR ; Calculate address of Free block
adc bx,0
call AddSelectorToChain ; Allocate and link the Free Block
; Initialize the header for the free data block.
mov fBlkStatus,al
mov cselBlkData,al
mov selBlkData,ax
mov selBlkOwner,ax
mov cx,word ptr [cbHeapSize]
mov word ptr [cbBlkLen],cx
mov cx,word ptr [cbHeapSize+2]
mov word ptr [cbBlkLen+2],cx
; Create the starting sentinel
sub dx,CB_MEMHDR ; Calculate address of start sentinel
sbb bx,0
call AddSelectorToChain ; Allocate and link the start sentinel
; Initialize the starting sentinel block.
mov fBlkStatus,al
mov cselBlkData,al
mov selBlkData,ax
mov selBlkOwner,0FFFEh ;mark it as in use
mov cx,hmemHeap ; Save handle
mov hBlockHandle,cx
mov cx,dsegCurrent ; Save paragraph count
mov dsegBlock,cx
pop es
pop cx
pop ax
ret
StructureHeap endp
; -------------------------------------------------------
; AddSelectorToChain -- This function will create a header block at a
; specified address and link it to the head of the
; header chain. It is the caller's responsibility
; to initialize all fields in the header other
; than the chain link pointers themselves.
;
; This function can only be called in protected mode.
;
; Input: BX - Most significant part of header address
; DX - Least significant part of header address
; Output: ES - selector for the new header
; Errors: Carry flag set on error
; Uses: all registers except ES preserved
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public AddSelectorToChain
AddSelectorToChain:
push ax ; Save callers regs
push bx ;
push cx ;
call AllocateSelector ; Get a free selector
jc astcFail ; Jump on error
mov cx,CB_MEMHDR - 1 ;
cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
mov cx,selHeapStart ; Load old start of chain
jcxz @f
mov es,cx ; Point to old start of chain
assume es:MEMHDR ;
mov selBlkPrev,ax ; Link to new start of chain
@@:
mov es,ax ; Point to new head of chain
mov selBlkNext,cx ; Link it to old head
mov selBlkPrev,0 ; NULL back pointer
mov selBlkHdr,ax ; Block header points to itself
mov selHeapStart,ax ; Store new head of chain
clc ; Flag no error
astcFail:
pop cx ; Restore Users regs
pop bx ;
pop ax ;
ret ; AddSelectorToChain
; -------------------------------------------------------
; AllocateXmem32 -- This function will return a 32-bit address and
; handle of a block of memory allocated in extended
; memory.
;
; Input: BX:CX - Size of block desired
; DX - Owner of block
; Output: BX:CX - Address of memory
; SI:DI - handle of memory
; Errors: Carry flag set on error
; Uses:
; -------------------------------------------------------
public AllocateXmem32
assume ds:DGROUP,es:NOTHING,ss:NOTHING
AllocateXmem32 proc near
FBOP BOP_DPMI, AllocXmem, FastBop
ret
AllocateXmem32 endp
; -------------------------------------------------------
; FreeXmem32 -- This function will free a block of extended memory
; allocated by AllocateXmem.
;
; Input: SI:DI - Handle of memory
; Errors: Carry flag set on error
; Uses:
; -------------------------------------------------------
public FreeXmem32
assume ds:DGROUP,es:NOTHING,ss:NOTHING
FreeXmem32 proc near
FBOP BOP_DPMI, FreeXmem, FastBop
ret
FreeXmem32 endp
DXPMCODE ends
DXCODE segment
assume cs:DXCODE
; -------------------------------------------------------
; ReleaseLowHeap -- This routine will release the
; special low memory heap. After this function is
; called, InitLowHeap must be called again before
; any other low heap functions can be used.
;
; Currently this routine doesn't bother to release
; selectors used by the blocks in the low heap (like
; ReleaseXmemHeap does) under the assumption that
; the DOS extender is about to terminate. If you are
; really going reinitialize the low heap with InitLowHeap,
; then you should do more clean up here.
;
; Input: none
; Output: none
; Errors:
; Uses: All preserved
;
; Note: This routine must be called in real mode!
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public ReleaseLowHeap
ReleaseLowHeap proc near
push ax
push es
mov ax,segLowHeap ;make sure there really is a low heap
or ax,ax
jz rlh90
mov es,ax ;now use DOS to get rid of it
mov ah,49h
pushf
sub sp,8 ; make room for stack frame
push bp
mov bp,sp
push es
push ax
xor ax,ax
mov es,ax
mov [bp + 8],cs
mov [bp + 6],word ptr (offset rlh10)
mov ax,es:[21h*4]
mov [bp + 2],ax
mov ax,es:[21h*4 + 2]
mov [bp + 4],ax
pop ax
pop es
pop bp
retf
rlh10: xor ax,ax ;just to be tidy
mov segLowHeap,ax
mov selLowHeapStart,ax
rlh90:
pop es
pop ax
ret
ReleaseLowHeap endp
; -------------------------------------------------------
; ReleaseXmemHeap -- This routine will release memory
; used by the extended memory heap. After this function
; is called, no extended memory manager routines can
; be called except InitXmemHeap.
;
; Input:
; Output:
; Errors:
; Uses:
;
; Note: Do NOT enable interrupts while in protected mode!
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public ReleaseXmemHeap
ReleaseXmemHeap:
push ax
push bx
push cx
push dx
push ds
push es
SwitchToProtectedMode ; Needed for selector juggling
ifdef MD
call CheckXmemHeap
endif
mov bx,selHeapStart ; Point to the start of the block chain
mov es,bx ;
assume es:MEMHDR ;
;
; This is the start of a loop on all blocks. ES and BX both contain the
; selector for the current block, or zero if at the end of the chain.
rxh100:
or bx,bx ; End of chain?
jz rxhEnd ; Yes - Jump
mov cl,cselBlkData ; Load number of data segments
xor ch,ch ;
jcxz rxh200 ; Jump if nothing to do
mov ax,selBlkData ; Load first data segment
and ax,SELECTOR_INDEX ; Treat like GDT entry for FreeSelector
rxh110:
push ax ; Since it is destroyed by FreeSelector
call FreeSelector ; Free each of the data selectors
pop ax ;
add ax,8 ; Point to the next data descriptor
loop rxh110 ;
;
; All data descriptors for this block now free (if there ever were any)
rxh200:
push selBlkNext ; Push pointer to the next block in the chain
push es ; Push the pointer to this one
cmp selBlkOwner,0FFFFh ; Is this an end sentinel ?
jne rxh300 ; No - jump
;
; Time to free a HIMEM allocated memory blcok
rxh210:
mov dx,hBlockHandle ;
push dx ; Since the data may move after the unlock
ASSUME es:NOTHING ;
pmxmssvc 0Dh,disable ;unlock the memory block containing the heap
pop dx ;
pmxmssvc 0Ah,disable ;free the block
;
; Time to free the header selector.
rxh300:
pop ax ; Retrieve the selector for the current header
pop bx ; Retrieve the selector for the next one
mov es,bx ;
call FreeSelector ; Free the selector for the current header
jmp rxh100 ; Loop for the next block
;
; All done
rxhEnd:
mov selHeapStart,0 ; Point to the start of the block chain
ifdef MD
call CheckXmemHeap
endif
SwitchToRealMode ; Restore callers environment
sti ;
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret
; -------------------------------------------------------
DXCODE ends
; -------------------------------------------------------
subttl Main Routines
page
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; MAIN MEMORY MANAGEMENT ROUTINES
; -------------------------------------------------------
;
; AllocateLowBlock -- This function will allocate a block
; of memory from the special low memory heap.
; If the requested block is larger than 64k, multiple segment
; descriptors will be allocated, one for each full 64k and one
; for whatever is left over. When multiple segment descriptors
; are created, the selectors will differ by 8 for each segment.
; (i.e. the returned selector accesses the first 64k, add 8 to
; get to the next 64k, etc.)
;
; Input: CX - low word of requested block size
; DX - high word of requested block size
; Output: AX - initial selector for the memory block
; Errors: returns CY set and size of largest free memory block in CX,DX.
; This can occur either because there isn't a large enough
; free block in the memory pool, or there isn't a contiguous
; range of free segment descriptors that is large enough.
; Uses: AX, all else preserved. Modifies the segment descriptors
; for the SEL_SCR0 and SEL_SCR1 segments
;
; Note: This routine is _very_ similar to AllocateXmemBlock,
; but just different enough that it's a separate routine.
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public AllocateLowBlock
AllocateLowBlock proc near
push bp
mov bp,sp
sub sp,14
push es
push si
push bx
push cx
push dx
Segm equ word ptr [bp - 2]
StartSel equ word ptr [bp - 4]
Largest equ word ptr [bp - 6]
MemSize equ [bp - 10]
cSelector equ word ptr [bp - 12]
selHeader equ word ptr [bp - 14]
mov word ptr MemSize,cx
mov word ptr MemSize + 2,dx
;
; See if we need to use wow kernel to manage low memory
;
mov ax,word ptr [LowMemAllocFn]
or ax,word ptr [LowMemAllocFn + 2]
je alm3
jmp alm130
;
; Round up to the next paragraph
;
alm3: add cx,15
adc dx,0
and cx,0FFF0h
;
; Form a paragraph count
;
mov bx,cx
shr bx,4
shl dx,12
or bx,dx
;
; Add one to make room for the heap header
;
cmp bx,0ffffh
je alm5 ; won't succed, but get size
inc bx
alm5:
;
; Switch to real mode and allocate the memory
;
SwitchToRealMode
mov ax,4800h
int 21h ; call dos
jnc alm10
mov Segm,ax
mov Largest,bx
mov cx,0
jmp alm20
;
; Remember the values returned
;
alm10: mov Segm,ax
mov cx,1
alm20: SwitchToProtectedMode
cmp cx,0
jne alm40
;
; return an error
;
alm30: mov cx,bx
mov dx,bx
shr dx,12
shl cx,4 ; form bytes available
mov ax,Segm ; actually error code for fail
add sp,4 ; skip cx,dx
stc
jmp alm110
alm40: mov ax,word ptr MemSize + 2
mov bx,word ptr MemSize
or bx, bx
jnz short alm44
or ax, ax
jz short alm45 ; if zero then don't adjust
alm44:
sub bx,1
sbb ax,0 ; go from size to limit
alm45:
mov word ptr MemSize + 2,ax
mov word ptr MemSize,bx
inc ax ; we always want 2 more
inc ax
mov cSelector,ax
xor cx, cx ;allocate from lower range
call AllocateSelectorBlock
jnc alm50
mov ax,8 ; insufficient memory
mov bx,Largest
jmp alm30
alm50: or ax,STD_RING
mov si,ax
mov StartSel,ax
mov ax,word ptr MemSize + 2
mov bx,word ptr MemSize ; ax:bx - block size
mov cx,Segm
mov dx,cx
shr cx,12
shl dx,4 ; cx:dx - block base
;
; Set up the first one to have the entire limit
;
cCall NSetSegmentDscr,<si,cx,dx,ax,bx,STD_DATA>
;
; Set up the rest to have 64K limits
;
dec ax ; already set one
cmp ax,0FFFFh
je alm80
cmp ax,0
je alm70
mov di,0FFFFh
alm60: add si,8
inc cx ; add 64K to base
cCall NSetSegmentDscr,<si,cx,dx,0,di,STD_DATA>
dec ax
cmp ax,0
jne alm60
;
; Just one selector left, so set the correct limit
;
alm70: add si,8
inc cx
cCall NSetSegmentDscr,<si,cx,dx,0,bx,STD_DATA>
;
; Set up header
;
alm80: mov ax,Segm
mov bx,ax
shr ax,12
shl bx,4 ; ax:bx = base of header
add bx,word ptr MemSize
adc ax,word ptr MemSize + 2
add si,8
cCall NSetSegmentDscr,<si,ax,bx,0,CB_MEMHDR,STD_DATA>
;
; Set up values in header and add to chain
;
mov es,si
mov selHeader,si
assume es:MEMHDR
mov [selBlkHdr],si
mov ax,StartSel
mov [selBlkData],ax
mov ax,cSelector
mov [cselBlkData],al
mov ax,selPSPChild
mov [selBlkOwner],ax
mov ax,Segm
mov [hBlockHandle],ax
mov ax,selLowHeapStart
mov [selBlkNext],ax
mov bx,0
mov [selBlkPrev],bx
or ax,ax
jz alm100
mov bx,es
mov es,ax
mov [selBlkPrev],bx
;
; set up return values
;
alm100: mov ax,selHeader
mov selLowHeapStart,ax
mov ax,StartSel
clc
alm105: pop dx
pop cx
alm110: pop bx
pop si
pop es
mov sp,bp
pop bp
ret
alm130: push dx
push cx
call [LowMemAllocFn]
or ax,ax
jz alm140
clc
jmp alm105
alm140: xor cx,cx
stc
add sp,4
jmp alm110
AllocateLowBlock endp
; -------------------------------------------------------
; FreeLowBlock -- This function will free the low heap
; memory block specified by the given selector. It
; will return the memory block to the free memory pool,
; and release all selectors used by this block.
;
; Input: AX - selector of the data block to free
; Output: none
; Errors: returns CY set if invalid selector
; Uses: AX, all other registers preserved
; Modifies the descriptor for segment SEL_SCR0
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public FreeLowBlock
FreeLowBlock proc near
push bp
mov bp,sp
sub sp,4
Segm equ word ptr [bp - 2]
HeaderSel equ word ptr [bp - 4]
push es
push bx
push cx
;
; See if we need to use the wow kernel to manage memory
;
mov bx,word ptr [LowMemFreeFn]
or bx,word ptr [LowMemFreeFn + 2]
jz flm5
jmp flm130
;
; search for the block to free
;
flm5: mov bx,selLowHeapStart
flm10: or bx,bx ; any low blocks?
jz flm100
mov es,bx
assume es:MEMHDR
cmp [selBlkData],ax
je flm30 ; found the correct block
mov bx,[selBlkNext]
jmp flm10
;
; Unlink the block from the list
;
flm30: mov ax,es
mov HeaderSel,ax
mov ax,[selBlkPrev]
mov bx,[selBlkNext]
cmp ax,0
je flm40
mov es,ax
mov [selBlkNext],bx
jmp flm50
flm40: mov SelLowHeapStart,bx
flm50: cmp bx,0
je flm60
mov es,bx
mov [selBlkPrev],ax
flm60: mov ax,HeaderSel
mov es,ax
mov ax,[hBlockHandle]
mov Segm,ax
mov ax,[selBlkData]
xor cx,cx
mov cl,[cselBlkData]
push 0
pop es
call FreeSelectorBlock
SwitchToRealMode
mov ax,Segm
mov es,ax
mov ax,4900h
int 21h ; free the memory
push ax ; save return code
jnc flm70
mov cx,0 ; error
jmp flm80
flm70: mov cx,1 ; no error
flm80: SwitchToProtectedMode
pop ax
cmp cx,0
je flm100 ; error path
flm85: clc
flm90: pop cx
pop bx
pop es
mov sp,bp
pop bp
ret
flm100: stc
jmp flm90
flm130: push ax
call [LowMemFreeFn]
or ax,ax
jz flm85
mov ax,9
stc
jmp flm90
FreeLowBlock endp
; -------------------------------------------------------
;
; AllocateXmemBlock -- This function will allocate a block
; of extended memory and return selectors for accessing it.
; If the requested block is larger than 64k, multiple segment
; descriptors will be allocated, one for each full 64k and one
; for whatever is left over. When multiple segment descriptors
; are created, the selectors will differ by 8 for each segment.
; (i.e. the returned selector accesses the first 64k, add 8 to
; get to the next 64k, etc.)
;
; Input: BL - flag controlling allocation of selectors,
; if 0 - a selector will be allocated for each 64k
; if != 0 - only one selector will be allocated to
; the block.
; CX - low word of requested block size
; DX - high word of requested block size
; Output: AX - initial selector for the memory block
; Errors: returns CY set and size of largest free memory block in CX,DX.
; This can occur either because there isn't a large enough
; free block in the memory pool, or there isn't a contiguous
; range of free segment descriptors that is large enough.
; Uses: AX, all else preserved. Modifies the segment descriptors
; for the SEL_SCR0 and SEL_SCR1 segments
;
; Notes: Hard coding an int 3 in this routine is fatal. This routine
; is used to allocate the stack used for exception handling.
;
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public AllocateXmemBlock
AllocateXmemBlock proc near
axb1: push bp
mov bp,sp
sub sp,20
push es
push bx
push si
push di
push cx
push dx
AllocFlag equ byte ptr [bp - 2]
cSelector equ word ptr [bp - 4]
MemHandle equ [bp - 8]
MemSize equ [bp - 12]
MemAddr equ [bp - 16]
StartSel equ word ptr [bp - 18]
HeaderSel equ word ptr [bp - 20]
mov AllocFlag,bl
mov word ptr MemSize,cx
mov word ptr MemSize + 2,dx
mov word ptr MemHandle,0
mov word ptr MemHandle + 2,0
;
; Save room for header
;
add cx,16
adc dx,0
mov bx,dx
mov dx, selPSPChild
call AllocateXmem32
jnc axb40
jmp axb130
axb40: mov word ptr MemAddr,cx
mov word ptr MemAddr + 2,bx
mov word ptr MemHandle,di
mov word ptr MemHandle + 2,si
;
; Change size to limit
;
mov ax,word ptr MemSize + 2
mov bx,word ptr MemSize
sub bx,1
sbb ax,0 ; size -> limit
mov word ptr MemSize,bx
mov word ptr MemSize + 2,ax
;
; Figure out how many selectors to allocate
;
cmp AllocFlag,0
jne axb50
inc ax
jmp axb60
axb50: mov ax,1
;
; Add one additional for the header block
;
axb60: inc ax
mov cSelector,ax
;
; Allocate the selectors
;
xor cx,cx ; allocate from lower range
call AllocateSelectorBlock
jnc axb65
jmp axb120
axb65: or ax,STD_RING
mov StartSel,ax
;
; Set up the first selector to have the entire limit
;
mov di,cSelector
mov si,ax
mov ax,word ptr MemSize + 2
mov bx,word ptr MemSize
mov cx,word ptr MemAddr + 2
mov dx,word ptr MemAddr
add dx,16 ; room for header
adc cx,0
cCall NSetSegmentDscr,<si,cx,dx,ax,bx,STD_DATA>
dec di
dec ax
add si,8
cmp di,1
je axb90
;
; Set up the rest with 64K limits
;
axb70: cmp ax,0
je axb80
inc cx
cCall NSetSegmentDscr,<si,cx,dx,0,0FFFFh,STD_DATA>
dec ax
add si,8
jmp axb70
;
; Set up the last one with the remaining limit
;
axb80: inc cx
cCall NSetSegmentDscr,<si,cx,dx,0,bx,STD_DATA>
add si,8
;
; Set up Header selector
;
axb90: mov ax,word ptr MemAddr + 2
mov bx,word ptr MemAddr
cCall NSetSegmentDscr,<si,ax,bx,0,CB_MEMHDR,STD_DATA>
mov HeaderSel,si
;
; Set up header
;
mov es,si
assume es:MEMHDR
mov [selBlkHdr],si
mov ax,StartSel
mov [selBlkData],ax
mov ax,cSelector
mov [cselBlkData],al
mov ax,selPSPChild
mov [selBlkOwner],ax
mov ax,MemHandle
mov [hBlockHandle],ax
mov ax,word ptr MemHandle + 2
mov [dsegBlock],ax ; use for high half handle
mov ax,selHiHeapStart
mov [selBlkNext],ax
mov bx,0
mov [selBlkPrev],bx
or ax,ax
jz axb100
mov bx,es
mov es,ax
mov [selBlkPrev],bx
axb100: mov ax,HeaderSel
mov selHiHeapStart,ax
;
; return information to the caller
;
clc
mov ax,StartSel
pop dx
pop cx
axb110: pop di
pop si
pop bx
pop es
mov sp,bp
pop bp
ret
;
; Error
;
axb120: mov dx,word ptr MemHandle
or dx,word ptr MemHandle + 2
jz axb130
mov di,word ptr MemHandle
mov si,word ptr MemHandle + 2
FBOP BOP_DPMI, FreeXmem, FastBop
;
; Get largest free block
;
axb130: pmxmssvc 08h
mov dx,ax
mov cx,ax
shl cx,10
shr dx,6
add sp,4 ; skip cx,dx on stack
stc
jmp axb110
AllocateXmemBlock endp
; -------------------------------------------------------
; FreeXmemBlock -- This function will free the extended
; memory block specified by the given selector. It
; will return the memory block to the free memory pool,
; and release all selectors used by this block.
;
; Input: AX - selector of the data block to free
; Output: none
; Errors: returns CY set if invalid selector
; Uses: AX, all other registers preserved
; Modifies the descriptor for segment SEL_SCR0
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public FreeXmemBlock
FreeXmemBlock:
fxb1: push bp
mov bp,sp
sub sp,6
HeaderSel equ word ptr [bp - 2]
MemHandle equ [bp - 6]
push es
push bx
push si
push di
push dx
push cx
;
; search for the block to free
;
mov bx,selHiHeapStart
fxb10: or bx,bx ; any hi blocks?
jnz fxb20
jmp fxb100
fxb20: mov es,bx
assume es:MEMHDR
cmp [selBlkData],ax
je fxb30 ; found the correct block
mov bx,[selBlkNext]
jmp fxb10
;
; Unlink the block from the list
;
fxb30: mov ax,es
mov HeaderSel,ax
mov ax,[selBlkPrev]
mov bx,[selBlkNext]
cmp ax,0
je fxb40
mov es,ax
mov [selBlkNext],bx
jmp fxb50
fxb40: mov SelHiHeapStart,bx
fxb50: cmp bx,0
je fxb60
mov es,bx
mov [selBlkPrev],ax
fxb60: mov ax,HeaderSel
mov es,ax
mov dx,[dsegBlock] ; high half handle
mov word ptr MemHandle + 2,dx
mov dx,[hBlockHandle]
mov word ptr MemHandle,dx
;
; Free the selectors
;
mov ax,[selBlkData]
xor cx,cx
mov cl,[cselBlkData]
push 0
pop es
assume es:nothing
call FreeSelectorBlock
mov si,word ptr MemHandle + 2
mov di,word ptr MemHandle
call FreeXmem32
clc
fxb90: pop cx
pop dx
pop di
pop si
pop bx
pop es
mov sp,bp
pop bp
ret
fxb100: stc
jmp fxb90
; -------------------------------------------------------
; ModifyXmemBlock -- This function will modify the size
; of the specified extended memory block to the new
; size specified by CX,DX. If the block is being
; shrunk, the new size must be at least CB_HDRSIZ bytes
; smaller than the old size, or nothing is done.If the
; block grows beyond the next 64k boundary,
; it may be necessary to allocate another segment
; descriptor for it. If this is not possible, the grow
; will fail.
;
; Input: AX - segment selector for the data block to modify
; BL - flag controlling allocation of selectors to block,
; if 0 - selectors will be allocated for each 64k
; if != 0 - only one selector is allocated to block
; CX - low word of new desired size
; DX - high word of new desired size
; Output: none
; Errors: CY set if unable to accomplish the request.
; returns error code in AX.
; CX,DX will be set to the largest possible size that the
; block could be modified to have
; Uses: AX,CX,DX,Flags
assume ds:DGROUP,es:NOTHING
public ModifyXmemBlock
ModifyXmemBlock proc near
mxb1: push bp
mov bp,sp
sub sp,20
HeaderSel equ word ptr [bp - 2]
AllocFlags equ byte ptr [bp - 4]
MemSize equ [bp - 8]
MemAddr equ [bp - 12]
Success equ word ptr [bp - 14]
cSelector equ word ptr [bp - 16]
BlockHandle equ word ptr [bp - 20]
push es
push bx
push cx
push dx
mov word ptr MemSize + 2,dx
mov word ptr MemSize,cx
mov AllocFlags,bl
mov Success,1
;
; Search for the block to resize
;
mov bx,selHiHeapStart
mxb10: or bx,bx ; any hi blocks?
jnz mxb15
jmp mxb140
mxb15: mov es,bx
assume es:MEMHDR
cmp [selBlkData],ax
je mxb30 ; found the correct block
mov bx,[selBlkNext]
jmp mxb10
;
; Calculate the number of selectors needed
;
mxb30: mov HeaderSel,es
test AllocFlags,1
jne mxb40
sub cx, 1 ; size -> limit
sbb dx,0
inc dl
jmp mxb50
mxb40: mov dl,1
mxb50: inc dl ; always need 1 more
;
; See if we have enough selectors
;
cmp dl,[cselBlkData]
jna mxb55
jmp mxb120
mxb55: and dl,0ffh
mov cSelector,dx
mov dx,[dsegBlock]
mov word ptr BlockHandle + 2,dx
mov dx,[hBlockHandle]
mov word ptr BlockHandle,dx
mov bx,word ptr MemSize + 2
mov cx,word ptr MemSize
add cx,010h
adc bx,0
mov si,word ptr BlockHandle + 2
mov di,word ptr BlockHandle
FBOP BOP_DPMI, ReallocXmem, FastBop
jnc mxb60
mov Success,0
mxb60: mov word ptr MemAddr,cx
mov word ptr MemAddr + 2,bx
mov word ptr BlockHandle,di
mov word ptr BlockHandle + 2,si
;
; Fix the base of the Header selector
;
mov bx,word ptr MemAddr + 2
mov dx,word ptr MemAddr
mov ax,HeaderSel
call SetSegmentAddress
mov es,ax
;
; Fix the handle (may have changed
;
mov dx,word ptr BlockHandle
mov [hBlockHandle],dx
mov dx,word ptr BlockHandle + 2
mov [dsegBlock],dx
;
; Change size to limit
;
mov ax,word ptr MemSize + 2
mov bx,word ptr MemSize
sub bx,1
sbb ax,0 ; size -> limit
mov word ptr MemSize,bx
mov word ptr MemSize + 2,ax
;
; Set up the first selector to have the entire limit
;
mov di,cSelector
mov si,[selBlkData]
mov ax,word ptr MemSize + 2
mov bx,word ptr MemSize
mov cx,word ptr MemAddr + 2
mov dx,word ptr MemAddr
add dx,16 ; room for header
adc cx,0
cCall NSetSegmentDscr,<si,cx,dx,ax,bx,STD_DATA>
dec di
dec ax
add si,8
cmp di,1
je mxb100
;
; Set up the rest with 64K limits
;
mxb80: cmp ax,0
je mxb90
inc cx
cCall NSetSegmentDscr,<si,cx,dx,0,0FFFFh,STD_DATA>
dec ax
add si,8
jmp mxb80
;
; Set up the last one with the remaining limit
;
mxb90: inc cx
cCall NSetSegmentDscr,<si,cx,dx,0,bx,STD_DATA>
add si,8
;
; Were we successfull?
;
mxb100: cmp Success,1
jne mxb130
clc
pop dx
pop cx
mxb110: pop bx
pop es
mov sp,bp
pop bp
ret
;
; We had an error, not enough selectors. Figure out how large
; it could have been
;
mxb120: xor dx,dx
xor cx,cx
mov dl,[cselBlkData]
dec dl ; don't count header sel
mov ax,8
add sp,4 ; pop cx,dx
stc
jmp mxb110
;
; We had an error calling xms. Get the largest block
;
mxb130: pmxmssvc 08h
mov cx,ax ; convert to bytes
mov dx,ax
shl cx,10
shr dx,6
mov ax,8
add sp,4
stc
jmp mxb110
;
; We had an error, invalid selector
;
mxb140: mov ax,9
add sp,4
stc
jmp mxb110
ModifyXmemBlock endp
ret
;
; -------------------------------------------------------
subttl Utility Routines
page
; -------------------------------------------------------
; UTIITY ROUTINES
; -------------------------------------------------------
; -------------------------------------------------------
; CalculateMaximumSegmentSpace -- This function will see if there
; are enough free segments available
; to expand a block of memory.
; If not it returns with carry set
; and the maximum space available.
;
; Input; ES - segment selector for the data block to modify
; CX - low word of new desired size
; DX - high word of new desired size
;
; Output CX - / If CY set: The maximum available
; DX - \ If CY not set: Unchanged
; Errors: CY set if unable to accomplish the request.
; CX,DX will be set to the largest possible size that the
; block could be modified to have
; Uses: AX,CX,DX,Flags
assume ds:DGROUP,es:MEMHDR
public CalculateMaximumSegmentSpace
CalculateMaximumSegmentSpace:
push bx
push es
xor bx,bx
mov bl,cselBlkData ; Allocated selector count
shl bx,3 ; Convert to byte offset
add bx,selBlkData ; Add starting data selector offset
and bx,SELECTOR_INDEX ; Treat it as a GDT selector
mov es,selGDT ; Base the global descriptor table
assume es:NOTHING ;
; Count through the immediately following selectors
cmssLoop:
cmp bx,word ptr bpgdt ; Off the end of the GDT ?
ja cmssOutOfSelectors ; Yes - Jump
cmp word ptr es:[bx].arbSegAccess,0 ; Is the next selector free ?
jne cmssOutOfSelectors ; No - jump
; Point to the next selector
add bx,8 ; Increment to next selector
jnc cmssLoop ; Try the next one
; BX points to the first following selector which is not free
cmssOutOfSelectors:
pop es ; Recover block header segment
assume es:MEMHDR
push dx ; Subtract base selector address
mov dx,selBlkData
and dx,SELECTOR_INDEX
sub bx,dx
pop dx
shr bx,3 ; Number of contiguous selectors available
cmp bx,dx ; Is it enough ?
jb cmssNotEnough ; No - jump
jne cmssOK ; Yes - jump
or cx,cx ; Don't know - Look at less significant part
jnz cmssNotEnough ; Yes it will fit after all - jump
; Not enough free selectors
cmssNotEnough:
mov dx,bx ; Put max available in CX&DX
xor cx,cx ;
stc ; Flag error
jmp short cmssExit ; Leave
;
; There are enough selectors to satisfy the request
cmssOK:
clc ; Reset the error flag
;
; All Done
cmssExit:
pop bx ; Restore registers
ret ; CalculateMaximumSegmentSpace
; -------------------------------------------------------
; CalculateMaximumSpace -- This function will see if there
; is room to expand a block of memory.
; If not it returns with carry set
; and the maximum space available.
;
; Input: AL - check selectors yes/no flag - 0 = yes, !0 = no
; ES - segment selector for the data block to modify
; CX - low word of new desired size
; DX - high word of new desired size
; Output: AX - Strategy used: 0 = expand into same block
; 1 = expand into next block
; 2 = expand into next and previous blocks
; 3 = allocate a new block and transfer data
; 4 = move lower and higher blocks
; CX - / If CY set: The maximum available
; DX - \ If CY not set: Unchanged
; Errors: CY set if unable to accomplish the request.
; CX,DX will be set to the largest possible size that the
; block could be modified to have
; Uses: AX,CX,DX,Flags
assume ds:DGROUP,es:MEMHDR
public CalculateMaximumSpace
CalculateMaximumSpace proc near
push bp
mov bp,sp
push es
push cx ; Save required length
push dx
push ax ; Save segment/selector flag
mov cx,word ptr [cbBlkLen] ; Load present length
mov dx,word ptr [cbBlkLen+2] ;
mov ax,0 ; Load strategy code
cmp dx,word ptr [bp - 6] ; Compare available with needed
jb cms90 ; Jump if it doesn't fit
ja cmsOKa ; Jump if it fits
cmp cx,word ptr [bp - 4] ;
jb cms90 ;
cmsOKa:
jmp cmsOK
; There is not enough room in the current block. See if the following
; one is free
cms90:
mov es,[selBlkNext] ; Point to the following block
cmp [selBlkOwner],0 ; Is it in use?
jnz cmsFail ; Yes - cannot use it
add cx,CB_MEMHDR ; Add the header of next block
adc dx,0
add cx,word ptr [cbBlkLen] ; Add the body of the next block
adc dx,word ptr [cbBlkLen+2];
mov ax,1 ; Load strategy code
cmp dx,word ptr [bp - 6] ; Compare available with needed
ja cmsOK ; Jump if it fits
cmp cx,word ptr [bp - 4] ;
jae cmsOK ;
; Cannot expand. The max available is in CX&DX
cmsFail:
add sp,6 ; Throw away requested size, and strat
pop es ; Point to original header
cmp byte ptr [bp-8],0 ; Should we check the # of
jz @f ; available selectors?
stc
jmp short cmsExit
@@:
call CalculateMaximumSegmentSpace ;Check for enough selectors
stc ;Flag error
jmp short cmsExit
; Expand will succeed. Strategy number is in ax
cmsOK:
add sp,2 ; discard strategy
pop dx ; Restore requested size
pop cx ;
cms900:
pop es ; Point to original header
cmp byte ptr [bp-8],0 ; Should we check the # of
jz @f ; available selectors?
clc
jmp short cmsExit
@@:
call CalculateMaximumSegmentSpace ;Check for enough selectors
cmsExit:
pop bp
ret
CalculateMaximumSpace endp
; -------------------------------------------------------
;
;
; CombineFreeBlocks -- This routine is used when freeing
; an extended memory block. It will examine the previous
; and following blocks to see if they are free also, and
; combine them into a single larger block if so.
;
; Input: AX - selector of segment descriptor for the block
; Output: AX - selector for new free memory block
; Errors: none
; Uses: AX, ES modified, all other registers preserved
;
; NOTE: This routine may free the segment descriptor for the
; entry block as part of the process of combining blocks.
; The original entry value may no longer be used after
; calling this routine. If access to the block header
; for the free segment is still needed, you must use
; the value returned in AX to access it.
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public CombineFreeBlocks
CombineFreeBlocks:
push bx
push dx
;
mov bx,ax ;save entry value in BX
;
; Look at the previous block and see if it is free.
mov es,bx ;look at block header of entry block
assume es:MEMHDR
mov es,[selBlkPrev] ;look at block header of previous block
cmp [selBlkOwner],0 ;is it in use?
jnz cfrb30 ;if so, continue on
;
; The previous memory block is free. We need to combine it with the
; current one.
mov ax,bx
call SpliceBlocks
mov bx,es
;
; Look at the following block and see if it is free.
cfrb30: mov es,bx ;look at the current lower free block
assume es:MEMHDR
mov es,[selBlkNext] ;look at the following block
cmp [selBlkOwner],0 ;is it in use?
jnz cfrb90
;
; The following memory block is free. We need to combine it with the
; current one.
mov ax,es
mov es,bx
call SpliceBlocks
;
; All done
cfrb90: mov ax,bx
pop dx
pop bx
ret
; -------------------------------------------------------
; SpliceBlocks -- This routine is used by CombineFreeBlocks
; to perform the actual combination of two adjacent free
; blocks. The space occupied by the upper block is assigned
; to the lower block, and the upper block is then eliminated.
;
; Input: AX - selector to the upper block
; ES - selector to the lower block
; Output: none
; Errors: none
; Uses: AX, DX modified
assume ds:DGROUP,es:NOTHING,ss:NOTHING
SpliceBlocks:
push ds
mov ds,ax
assume ds:NOTHING ;DS points at upper block
;ES points at lower block
push ax
;
mov dx,word ptr ds:[cbBlkLen]
mov ax,word ptr ds:[cbBlkLen+2]
add dx,CB_MEMHDR
adc ax,0
add word ptr es:[cbBlkLen],dx
adc word ptr es:[cbBlkLen+2],ax
mov ax,ds:[selBlkNext]
mov es:[selBlkNext],ax
mov ds,ax ;DS points at block following entry block
mov ds:[selBlkPrev],es
;
pop ax
pop ds
call FreeSelector ;release the segment descriptor for the
; upper block
ret
; -------------------------------------------------------
; FindFreeBlock -- This function will search the extended
; memory heap looking for a free memory block of at
; least the requested size.
;
; Input: CX - low word of requested block size
; DX - high word or requested block size
; Output: AX - selector for the block header of the free block
; cbHeapBlkMax - size of largest free block found
; cbHeapFreeSpace - total free space seen
; Errors: Returns CY set if large enough free block not found.
; If that happens, then cbHeapBlkMax contains the size of the
; largest free block, and cbHeapFreeSpace contains the total
; of all free blocks.
; Uses: AX modified, all other registers preserved.
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public FindFreeBlock
FindFreeBlock:
push cx
push dx
push es
;
mov word ptr [cbHeapBlkMax],0
mov word ptr [cbHeapBlkMax+2],0
mov word ptr [cbHeapFreeSpace],0
mov word ptr [cbHeapFreeSpace+2],0
;
cmp selHeapStart,0 ;
jz ffrb80 ; No heap - no allocate - jump
mov es,selHeapStart ; ES points at the beginning of the heap
assume es:MEMHDR
;
; Is the current memory block free?
ffrb20:
cmp selBlkOwner,0 ;if the block isn't free, try the next one
jnz ffrb26
;
; Accumulate size into free space count.
mov ax,word ptr [cbBlkLen]
add word ptr [cbHeapFreeSpace],ax
mov ax,word ptr [cbBlkLen+2]
adc word ptr [cbHeapFreeSpace+2],ax
;
; Update our view of what the largest free block is.
mov ax,word ptr [cbBlkLen+2]
cmp ax,word ptr [cbHeapBlkMax+2]
jc ffrb26
jnz ffrb22
mov ax,word ptr [cbBlkLen]
cmp ax,word ptr [cbHeapBlkMax]
jc ffrb26
ffrb22: mov ax,word ptr [cbBlkLen+2]
mov word ptr [cbHeapBlkMax+2],ax
mov ax,word ptr [cbBlkLen]
mov word ptr [cbHeapBlkMax],ax
;
; Check the size of this memory block to see if it is large enough.
ffrb24: cmp dx,word ptr [cbBlkLen+2]
jc ffrb40
jnz ffrb26
cmp cx,word ptr [cbBlkLen]
jna ffrb40
;
; Go to the next block in the heap
ffrb26:
cmp selBlkNext,0 ; End of chain?
jz ffrb80 ; Yes - jump
mov es,[selBlkNext]
jmp ffrb20
;
; Found a free block that is large enough
ffrb40: mov ax,es
clc
jmp short ffrb90
;
; Didn't find one, return error.
ffrb80: stc
;
; All done
ffrb90: pop es
pop dx
pop cx
ret
; -------------------------------------------------------
; FreeSpace -- This function is used to find out how
; much free space there is in the heap, plus that
; which is potentially available from an XMS driver.
;
; Input: None
; Output: AX:DX -- Total free space
; BX:CX -- Largest free block
; Errors: none
; Uses: AX, all else preserved
;
; History:10/09/90 -- earleh wrote it.
;
cProc FreeSpace,<NEAR,PUBLIC>,
cBegin
;
; Call xms to get free memory space
;
pmxmssvc 08h
;
; convert to bytes
;
mov bx,dx
mov dx,ax
mov cx,bx
shl dx,10
shr ax,6
shl cx,10
shr bx,6
;
; account for heap header
;
sub dx,CB_MEMHDR
sbb ax,0
sub cx,CB_MEMHDR
sbb bx,0
ret
cEnd
; -------------------------------------------------------
; SplitBlock -- This function will split the specified
; memory block into two blocks, with the first one
; being the specified size, and the second one containing
; the remaining space.
;
; Input: AX - selector of block header for memory block to split
; CX - low word of requested size
; DX - high word of requested size
; Output: none
; Errors: none
; Uses: AX, all else preserved
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public SplitBlock
SplitBlock:
push bx
push cx
push dx
push si
push es
;
mov es,ax
assume es:MEMHDR
;
; We are going to need a segment descriptor for the remainder block
; when we do the split. Make sure that we can allocate the selector
; now, before we change anything.
call AllocateSelector
jnc spbl20 ;
jmp spbl90 ;get out if error
spbl20: mov si,ax ;save selector in SI for later
;
; Adjust the size of the current block and figure the size of the
; remainder.
xchg cx,word ptr [cbBlkLen] ;store the new block length
xchg dx,word ptr [cbBlkLen+2] ; and get the old block length
push cx ;Save the original size for recovery
push dx
sub cx,word ptr [cbBlkLen] ;compute the amount of space
sbb dx,word ptr [cbBlkLen+2] ; remaining in the block
sub cx,CB_MEMHDR ;also account for the new block header
sbb dx,0
jc spbl25 ;Jump if not enough memory to split
jnz spbl30 ;if there is some remaining memory, then
cmp cx,0 ; we have additional work to do
jnz spbl30
;
; There is no remaining memory. Free the selector that we allocated
; earlier and then get out.
spbl25: mov ax,si
pop dx ;Recover old size
pop cx
mov word ptr [cbBlkLen],cx ;restore the old block length
mov word ptr [cbBlkLen+2],dx
call FreeSelector
jmp spbl90
;
; We need to create a segment descriptor for the new memory block header
; for the remainder block.
spbl30: add sp,4 ;Dispose of unneeded recovery data
push cx ;save memory block length for later
push dx
mov ax,es ;selector of current memory header
call GetSegmentAddress
mov cx,CB_MEMHDR - 1
add dx,word ptr [cbBlkLen] ;bump by size of current block
adc bx,word ptr [cbBlkLen+2]
add dx,CB_MEMHDR ;plus size of block header
adc bx,0
mov ax,si
cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
pop dx
pop cx
;
; Now, we need to build a new memory block header for the remainder of
; the original block. CX,DX has the size of the new block.
push ds
;
mov ds,si
assume ds:NOTHING ;DS points at new block header
assume es:NOTHING ;ES points at old block header
xor ax,ax
mov ds:[fBlkStatus],al
mov ds:[cselBlkData],al
mov ds:[selBlkData],ax
mov ds:[selBlkOwner],ax
mov ds:[selBlkHdr],ds
mov word ptr ds:[cbBlkLen],cx
mov word ptr ds:[cbBlkLen+2],dx
mov ds:[selBlkPrev],es
mov ax,es:[selBlkNext]
mov ds:[selBlkNext],ax
mov es:[selBlkNext],si
mov ds,ax ;DS points at following block
mov ds:[selBlkPrev],si
cmp ds:[selBlkOwner],0 ;is it in use?
;
pop ds
assume ds:DGROUP
jnz spbl90 ;Jump if the following block is not free
;
; The following memory block is free. We need to combine it with the
; current one.
mov es,si
call SpliceBlocks
;
; All done
spbl90: pop es
pop si
pop dx
pop cx
pop bx
ret
; -------------------------------------------------------
; GetBlockHeader -- This function will return the selector
; for the block header of the specified memory data
; block.
;
; Input: AX - selector of the data block
; Output: AX - selector of the block header
; Errors: returns CY set if the entry selector doesn't point
; to a valid data block
; Uses: AX, all other registers preserved
; Modifies the descriptor for segment SEL_SCR0
assume ds:DGROUP,es:NOTHING,ss:NOTHING
GetBlockHeader:
push bx
push cx
push dx
push es
push ax ;save entry value for later
mov bx,SEL_SCR0 or STD_TBL_RING
call DupSegmentDscr ;duplicate input descriptor to scratch
mov ax,bx
call GetSegmentAddress
sub dx,CB_MEMHDR ;backup to supposed header
sbb bx,0
mov cx,CB_MEMHDR-1 ; has the proper limit!
cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
mov es,ax
assume es:MEMHDR
pop cx ;recover data block selector
cmp cx,[selBlkData] ;does the header point back to the
jz gtbh50 ; data block.
stc ;if not, then what were given wasn't a
jmp short gtbh90 ; selector to a memory block
gtbh50: mov ax,[selBlkHdr] ;get the real selector to the header
gtbh90: pop es
pop dx
pop cx
pop bx
ret
; -------------------------------------------------------
; DisposeBlock -- This routine will free all segment
; descriptors associated with the specified memory block,
; and remove the block header from the heap list.
;
; Input: AX - selector of block to dispose
; Output: none
; Errors: none
; Uses: All registers preserved
;
; NOTE: This routine frees selectors for the specified memory block.
; If this selector is already in a segment register when this
; routine is called a GP fault will occur it that segment
; register is later saved or restored. Also, any references to
; memory using that segment register might destroy random data
; in memory.
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public DisposeBlock
DisposeBlock:
push cx
push si
push di
push es
;
; Remove the block header from the heap list.
mov es,ax ;header selector to ES
mov si,es:[selBlkNext] ;SI points to following block
mov di,es:[selBlkPrev] ;DI points to previous block
mov es,di ;ES points to previous block
mov es:[selBlkNext],si ;previous block points to following block
mov es,si ;ES points to following block
mov es:[selBLkPrev],di ;following block points back to prev. block
;
; Free any data block descriptors associated with this memory object.
mov es,ax
cmp es:[selBlkOwner],0
jz dspb40
xor ch,ch
mov cl,es:[cselBlkData]
mov ax,es:[selBlkData]
and ax,SELECTOR_INDEX ;treat as GDT FreeSelectorBlock
call FreeSelectorBlock
;
; Free the descriptor for the block header.
dspb40: mov ax,es
pop es
call FreeSelector
;
; All done
dspb90: pop di
pop si
pop cx
ret
; -------------------------------------------------------
; SetUpDataDescriptors -- This function will initialize
; all of the data segment descriptors associated with
; a block. Those that should point to data will be
; initialized accordingly. Those corresponding to
; addresses beyond the allocated data will be initialized
; as not present. If insufficient data segment
; descriptors have been allocated, an attempt
; will be made to allocate them. If this fails an error
; occurs.
;
; Input: AX - Selector of the header segment.
;
; Output: None
;
; Errors: Returns CY set if not enough data segment descriptors
; are available.
;
; Uses: Segment descriptor table is modified. Possibly the
; data segment count in the header is modified.
; No registers affected.
;
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public SetUpDataDescriptors
SetUpDataDescriptors:
push es
push si
push bx
push cx
push dx
push ax
mov es,ax
mov si,ax ;save header segment for dup
assume es:MEMHDR
mov bx,selBlkData ;Starting data selector index
and bx,SELECTOR_INDEX
xor cx,cx
mov cl,cselBlkData ;Load segment count
inc cx
mov dx,word ptr es:[cbBlkLen+2] ;Full data segment count
mov es,selGDT
assume es:NOTHING
sudd30:
or dx,dx ;is this the last segment?
jnz sudd35
pop es ;Get the header segment
push es
cmp word ptr es:[cbBlkLen],0 ;Test partial length
mov es,selGDT ;Rebase the global descriptor table
jnz sudd35 ;Jump if the partial length is 0
jmp sudd60
sudd35: loop short sudd40 ;Out of segments?
xchg bx,ax
call RemoveFreeDescriptor ;Attempt to get segment
xchg bx,ax
jnc sudd37
jmp sudd80
sudd37: inc cx
sudd40: mov ax,si
call DupSegmentDscr ; replicate the previous seg (in si)
mov si,bx
; Set the DPL of allocated blocks to user DPL
ifndef WOW_x86
mov es:[si].arbSegAccess,STD_DATA
else
push ax
xor ax,ax
mov ah,es:[si].cbLimitHi386
or ax,STD_DATA
cCall NSetSegmentAccess,<si,ax>
pop ax
endif
; Increment the segment start address past the previous one
cmp es:[si].cbLimit,CB_MEMHDR-1 ; Is this the first block?
jne sudd41 ; No - jump
ifndef WOW_x86
add es:[si].adrBaseLow,CB_MEMHDR ; bump segment start to point
adc es:[si].adrBaseHigh,0 ;past the block header
else
push ax
push dx
mov ax,es:[si].adrBaseLow
mov dl,es:[si].adrBaseHigh
mov dh,es:[si].adrbBaseHi386
add ax,CB_MEMHDR
adc dx,0
cCall NSetSegmentBase,<si,dx,ax>
pop dx
pop ax
endif
jmp short sudd42
ifndef WOW_x86
sudd41: add es:[si].adrBaseHigh,1 ; bump segment start by 64k
else
sudd41: push ax
push dx
mov ax, es:[si].adrBaseLow
mov dl, es:[si].adrBaseHigh
mov dh, es:[si].adrbBaseHi386
inc dx
cCall NSetSegmentBase,<si,dx,ax>
pop dx
pop ax
endif
; Set the segment length
sudd42: add bx,8 ; Offset of next segment descriptor
or dx,dx ; Was this a partial segment?
jnz sudd45 ; jump if not
;
; We are at the partial (<64k) chunk at the end.
pop es ;Get the header segment
push es
mov dx,word ptr es:[cbBlkLen] ; Segment length
dec dx ; Last legal offset
mov es,selGDT
mov es:[si].cblimit,dx ; Store in segment descriptor
ifdef WOW_x86
cCall NSetSegmentLimit,<si>
endif
jmp sudd60 ; All valid data segs now exist
sudd45:
mov es:[si].cbLimit,0FFFFh ;set segment limit at 64k
ifdef WOW_x86
cCall NSetSegmentLimit,<si>
endif
dec dx ; On to the next data segment
jmp sudd30
;
; Invalidate remaining segments
sudd50: mov ax,si
call DupSegmentDscr ; replicate the previous one and
mov si,bx
mov word ptr es:[si].arbSegAccess,0 ; Invalidate the segment
add bx,8 ; Offset of next segment descriptor
sudd60: loop sudd50
;
; All done
clc
jmp short sudd90
;
; Allocate of a segment descriptor failed.
sudd80: pop ax ;Header Segment
mov es,ax ;
mov dx,es:selBlkData ;Calculate # segments
and dx,SELECTOR_INDEX ; successfully allocated
sub bx,dx
shr bx,3 ;Succesfully allocated
mov es:[cselBlkData],bl ;Store new value
mov dx,bx ;Return max allocatable space
mov cx,0 ;
add sp,4 ;Pop redundant stack data
stc ;Flag error
jmp short sudd100 ;Join common exit code
;
; Tidy up
sudd90: pop ax ;Header segment
mov es,ax
mov dx,es:selBlkData ;Calculate # segments
and dx,SELECTOR_INDEX
sub bx,dx
shr bx,3
mov es:[cselBlkData],bl ;Store new value
pop dx
pop cx
sudd100:
pop bx
pop si
pop es
ret
;
ifdef MD
; -------------------------------------------------------
; CheckXmemHeap -- This routine will check the integrity
; of the chain of block headers.
;
; Input: NONE
; Output: None
; Errors: INT3 If an error is found
; Uses: All registers preserved
;
; NOTE: This routine must be called in protected mode.
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public CheckXmemHeap
CheckXmemHeap:
push bp
mov bp,sp
pushf
push bx
push cx
push dx
push es
xor cx,cx
mov dx,cx
mov bx,selHeapStart ; Load start of chain
cxmh100:
or bx,bx ;
jnz cxmh101
jmp cxmh200 ;
cxmh101:
mov es,bx ;
assume es:MEMHDR ;
cmp selBlkHdr,bx ;
je cxmh110 ;
int 3
jmp cxmh200
;; Debug_Out "CheckXmemHeap: Block header does not point to block header!"
cxmh110:
cmp selBlkPrev,dx ;
je cxmh120 ;
int 3
jmp cxmh200
;; Debug_Out "CheckXmemHeap: Previous block does not point to previous block!"
cxmh120:
mov dx,bx ;
mov bx,selBlkNext ;
loop cxmh100 ;
int 3
;; Debug_Out "CheckXmemHeap: Can't find end of arena chain!"
cxmh200:
pop es
pop dx
pop cx
pop bx
popf
pop bp
ret
endif
; -------------------------------------------------------
; FreeMemByOwner -- This routine frees all of the mem
; blocks that belong to the specified owner.
;
; Input: bx = owner
; Output: none
; Uses: ax
;
; Note: We have to start at the beginning of the heap
; after we free a block, because the free may
; coalesce the heap, and the selector for the
; next block may no longer be correct. We know we
; are done when we have traversed the entire heap,
; and not found a block to free.
assume ds:DGROUP, es:nothing, ss:nothing
public FreeMemByOwner
FreeMemByOwner:
push es
push cx
cmp bx,0 ; owner is free owner??
je fbo40
; First traverse xmem heap
push selHeapStart
mov ax,selHiHeapStart
mov selHeapStart,ax
call FreeHiMemByOwnerW
call FreeLowMemByOwnerW
pop selHeapStart
fbo40:
pop cx
pop es
ret
FreeHiMemByOwnerW:
fhbow10: mov ax,selHiHeapStart
fhbow20: cmp ax,0
je @f
lar cx, ax ;is this a valid selector?
jz fhbow25 ;yes, go ahead
@@:
ret
fhbow25: mov es,ax
assume es:MEMHDR
cmp selBlkOwner,bx
jne fhbow30
mov ax,selBlkDAta
push 0
pop es
call FreeXmemBlock
jmp fhbow10 ; check again.
fhbow30: mov ax,selBlkNext ; check next block
jmp fhbow20
FreeLowMemByOwnerW:
flbow10: mov ax,selLowHeapStart
flbow20: cmp ax,0
je @f
lar cx, ax ;is this a valid selector?
jz flbow25 ;yes, go ahead
@@:
ret
flbow25: mov es,ax
assume es:MEMHDR
cmp selBlkOwner,bx
jne flbow30
mov ax,selBlkData
push 0
pop es
call FreeLowBlock
jmp flbow10 ; check again.
flbow30: mov ax,selBlkNext ; check next block
jmp flbow20
; -------------------------------------------------------
; -------------------------------------------------------
; -------------------------------------------------------
; -------------------------------------------------------
; -------------------------------------------------------
DXPMCODE ends
;
;****************************************************************
end