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 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,, 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, 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, ; ; 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, dec ax cmp ax,0 jne alm60 ; ; Just one selector left, so set the correct limit ; alm70: add si,8 inc cx cCall NSetSegmentDscr, ; ; 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, ; ; 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, 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, dec ax add si,8 jmp axb70 ; ; Set up the last one with the remaining limit ; axb80: inc cx cCall NSetSegmentDscr, add si,8 ; ; Set up Header selector ; axb90: mov ax,word ptr MemAddr + 2 mov bx,word ptr MemAddr cCall NSetSegmentDscr, 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, 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, dec ax add si,8 jmp mxb80 ; ; Set up the last one with the remaining limit ; mxb90: inc cx cCall NSetSegmentDscr, 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,, 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, 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, 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, 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, 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, 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, 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, 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