; *Not* the original file, but an edit to turn it into an includable slice. ; Changes include: ; * removal of RULES.ASI to eliminate redundancy ; * data declarations being moved to a separate file ; * removal of the segment declarations (for obvious reasons) ; * the "Header" structure being renamed to "NEARHEAP_Header" to avoid ; collisions with master.lib's global bfnt_header structure ;[]-----------------------------------------------------------------[] ;| NEARHEAP.ASM | ;[]-----------------------------------------------------------------[] ; ; C/C++ Run Time Library - Version 5.0 ; ; Copyright (c) 1987, 1992 by Borland International ; All Rights Reserved. ; IF LDATA EQ false ;----------------------------------------------------------------------- ; Memory Block Header (near heap) ;----------------------------------------------------------------------- ; Each block in the heap, whether allocated or free, has a header. ; For an allocated block, only the first two fields of the header are ; used. For a free block all eight bytes are used, thus the smallest ; possible block is the size of a free header. ; ; Field Description ; --------- ---------------------------------------------------------- ; size total size, in bytes, of this block (+1 if the block is in use) ; prev_real pointer to the physically previous block in the heap ; prev_free pointer to the logically previous free block ; next_free pointer to the logically next free block ; ; Note that the size field is also used to indicate whether the block ; is allocated or free. A doubly-linked queue is maintained of the ; free blocks and it is important to know that ordering of the blocks ; in this queue is logical rather than physical. If there is only one ; free block on the heap prev_free and next_free point to itself. ;----------------------------------------------------------------------- NEARHEAP_Header STRUC bsize dw ? prev_real dw ? prev_free dw ? next_free dw ? ENDS ;----------------------------------------------------------------------- ; heapinfo structure (near heap) ;----------------------------------------------------------------------- ; Used by the heapwalk function. ; heapwalk accepts a pointer to a struct of this type. ; On entry, the pointer field contains the address of the previous ; memory block in the heap (NULL for the first call). The next block ; in the heap is then found and its address is stored in the structure ; along with its size, in bytes, and a 'used' flag. ;----------------------------------------------------------------------- HeapInfo STRUC hi_ptr dw ? hi_size dw ? hi_inuse dw ? ENDS UsedHeaderSize EQU 4 FreeHeaderSize EQU 8 IF LPROG EXTRADISP equ 2 ; Allow for FAR returns when getting parms ELSE EXTRADISP equ 0 ENDIF ;----------------------------------------------------------------------------- ; C callable function to free a memory block ;----------------------------------------------------------------------------- ; Args: Pointer to the block to free (stack) ; Returns: void ;----------------------------------------------------------------------------- PUBLIC _free _free PROC DIST push si push di mov si,sp mov bx,[si+(6+EXTRADISP)] ;bx = first parameter passed sub bx,UsedHeaderSize ;bx = the address of the block to free jc @@AllDone ;skip if NULL cmp bx,[__last] ;is this the last block in the heap? je @@lastBlock @@InnerBlock: call FreeInnerBlock ;free the inner block jmp short @@AllDone @@LastBlock: call FreeLastBlock ;free the last block @@AllDone: pop di ;all done pop si ret _free ENDP ;----------------------------------------------------------------------------- ; Frees the last block on the heap ; free helper function ;----------------------------------------------------------------------------- ; Args: Pointer to the block (bx) ; Returns: void ;----------------------------------------------------------------------------- FreeLastBlock PROC NEAR cmp [__first],bx ;freeing the ONLY block? je @@KillHeap mov si,[bx.prev_real] ;si = next-to-last block test BYTE PTR [si.bsize],01h jz @@PreviousBlockIsFree @@PreviousBlockIsUsed: mov __last,si jmp short @@ResetBreak ;done! @@PreviousBlockIsFree: cmp si,[__first] ;is the previous block the je @@ResetHeap ;first block in the heap? mov bx,si ;remove the next-to-last block call PullFreeBlock ;from the free-block queue mov ax,[bx.prev_real] mov __last,ax jmp short @@ResetBreak ;done! @@ResetHeap: mov bx,si ;we are freeing the only block so reset the break level and kill the heap @@KillHeap: xor ax,ax mov __first,ax ;clear variables mov __last,ax mov __rover,ax @@ResetBreak: push bx call ___brk ;reset the break level pop bx ;cleanup stack ret FreeLastBlock ENDP ;----------------------------------------------------------------------------- ; Frees an interior block from within the heap ; free helper function ;----------------------------------------------------------------------------- ; Args: Pointer to the block (bx) ; Returns: void ;----------------------------------------------------------------------------- FreeInnerBlock PROC NEAR dec WORD PTR [bx.bsize] ;mark the block as free cmp bx,[__first] ;first block? je @@PreviousBlockIsUsed mov si,[bx.prev_real] ;get the previous block (si) mov ax,[si.bsize] ;is the previous block free? test al,01h jnz @@PreviousBlockIsUsed ; join this block to the previous block @@PreviousBlockIsFree: add ax,[bx.bsize] ;add the size of this block to mov [si.bsize],ax ;the size of the previous block mov di,[bx.bsize] ;get the next block (di) add di,bx mov [di.prev_real],si ;adjust the prev_real pointer mov bx,si ;set up the current block jmp SHORT @@CheckNextBlock @@PreviousBlockIsUsed: call InsertFreeBlock ;add it to the free queue @@CheckNextBlock: mov di,[bx.bsize] ;get the next block (di) add di,bx mov ax,[di.bsize] ;is the next block free? test al,01h jz JoinFreeBlocks ;join this block to the next @@AllDone: ret ;all done FreeInnerBlock ENDP ;----------------------------------------------------------------------------- ; Joins two physically adjacent free blocks together ; free helper function ;----------------------------------------------------------------------------- ; Args: Pointer to the lower block (bx) ; Pointer to the upper block (di) ; Size of the upper block, in bytes (ax) ; Returns: void ; Registers destroyed: ax si di ; This routine falls through to PullFreeBlock ;----------------------------------------------------------------------------- JoinFreeBlocks PROC NEAR add [bx.bsize],ax ;adjust the size of the lower block mov si,di ;si = the next block after di add si,ax mov [si.prev_real],bx ;adjust the link mov bx,di ;;;; jmp SHORT PullFreeBlock ;eliminate the upper block JoinFreeBlocks ENDP ;----------------------------------------------------------------------------- ; Removes a block from the free block queue ; free helper function ; malloc helper function ;----------------------------------------------------------------------------- ; Args: Pointer to the block (bx) ; Returns: void ;----------------------------------------------------------------------------- PullFreeBlock PROC NEAR mov di,[bx.next_free] ;di = the next free block cmp bx,di ;removing the last free block? je @@NoFreeBlocks mov __rover,di mov si,[bx.prev_free] ;si = previous free block mov [di.prev_free],si ;adjust the links mov [si.next_free],di ret ;all done @@NoFreeBlocks: mov __rover,0 ret ;all done PullFreeBlock ENDP ;----------------------------------------------------------------------------- ; Inserts a block into the free block queue ; free helper function ;----------------------------------------------------------------------------- ; Args: Pointer to the block (bx) ; Returns: void ;----------------------------------------------------------------------------- InsertFreeBlock PROC NEAR mov si,[__rover] ;si = rover pointer or si,si ;no free blocks? jz @@FirstFreeBlock @@AnotherFreeBlock: mov di,[si.next_free] ;di = free block after rover mov [si.next_free],bx ;adjust links mov [di.prev_free],bx mov [bx.next_free],di mov [bx.prev_free],si ret @@FirstFreeBlock: mov __rover,bx mov [bx.prev_free],bx mov [bx.next_free],bx ret InsertFreeBlock ENDP ;----------------------------------------------------------------------------- ; C callable function to allocates a given number of bytes from the heap ;----------------------------------------------------------------------------- ; Args: Number of bytes requested (stack) ; Returns: Address of the first byte of user space available ; from the heap if successful (ax) ; NULL if failure (ax) ;----------------------------------------------------------------------------- PUBLIC _malloc _malloc PROC DIST push si ;should be on an odd address push di mov si,sp mov ax,[si+(6+EXTRADISP)] ;ax = number of bytes requested or ax,ax ;does he want zero bytes? jz @@AllDone add ax,UsedHeaderSize+1 ;add the header size jc @@NoCanDo ;was size too great? and ax,0fffeh ;force a word boundary cmp ax,FreeHeaderSize jae @@BigEnough mov ax,FreeHeaderSize @@BigEnough: cmp [__first],0 ;do we have a heap yet? jz @@BuildHeap mov bx,[__rover] ;bx = rover pointer or bx,bx ;are there any free blocks at all? jz @@AddToHeap mov dx,bx ;dx = rover pointer @@SearchHeap: cmp [bx.bsize],ax ;big enough to use at all? jae @@AllocateBlock @@TooSmall: mov bx,[bx.next_free] ;move to the next free block cmp bx,dx ;at the end of the list? jne @@SearchHeap @@AddToHeap: call ExtendHeap jmp SHORT @@AllDone @@DivideFreeBlock: call AllocatePartialBlock jmp SHORT @@AllDone @@BuildHeap: call CreateHeap jmp SHORT @@AllDone @@NoCanDo: xor ax,ax jmp SHORT @@AllDone @@AllocateBlock: mov si,ax ;si = smallest divisible block size add si,FreeHeaderSize cmp [bx.bsize],si ;big enough to break up? jae @@DivideFreeBlock call PullFreeBlock ;remove it from the free-block queue inc [bx.bsize] ;mark it as allocated mov ax,bx add ax,UsedHeaderSize @@AllDone: pop di ;all done pop si ret _malloc ENDP ;----------------------------------------------------------------------------- ; Creates a heap from scratch ; malloc helper function ;----------------------------------------------------------------------------- ; Args: Number of bytes for the first block requested (ax) ; Returns: Address of the first byte of user space available ; from the heap if successful (ax) ; NULL if failure (ax) ;----------------------------------------------------------------------------- CreateHeap PROC NEAR push ax ;save the size xor ax,ax ;align the heap on word push ax push ax call ___sbrk ;retrieve the break level pop bx ;cleanup stack pop bx and ax,0001h jz @@Aligned xor dx,dx push dx push ax call ___sbrk ;align the heap pop bx ;cleanup stack pop bx @@Aligned: pop ax ;retrieve and save the size push ax xor bx,bx ;convert size request from push bx ;unsigned int to signed long push ax call ___sbrk ;adjust the break level pop bx ;cleanup stack pop bx cmp ax,-1 ;failure? je @@NoRoom mov bx,ax ;bx = new block mov __first,bx ;save pointers mov __last,bx pop ax ;retrieve the size inc ax ;mark it as allocated mov [bx.bsize],ax add bx,UsedHeaderSize mov ax,bx ret @@NoRoom: pop bx ;clear the size from the stack xor ax,ax ret CreateHeap ENDP ;----------------------------------------------------------------------------- ; Attempts to extend the heap. ; malloc helper function ;----------------------------------------------------------------------------- ; Args: Number of bytes for the block requested (ax) ; Returns: Address of the first byte of user space available ; from the heap if successful (ax) ; NULL if failure (ax) ;----------------------------------------------------------------------------- ExtendHeap PROC NEAR push ax ;save the size xor bx,bx ;convert size request from push bx ;unsigned int to signed long push ax call ___sbrk ;adjust the break level pop bx ;cleanup stack pop bx cmp ax,-1 ;failure? je @@NoRoom mov bx,ax ;bx = new block mov ax,[__last] ;ax = next-to-the-last block mov [bx.prev_real],ax mov __last,bx ;update last-block pointer pop ax ;retrieve the size inc ax ;mark it as allocated mov [bx.bsize],ax add bx,UsedHeaderSize mov ax,bx ret @@NoRoom: pop ax ;retrieve the size xor ax,ax ret ExtendHeap ENDP ;----------------------------------------------------------------------------- ; Divides a free block into two pieces. ; malloc helper function ;----------------------------------------------------------------------------- ; Args: Number of bytes for the block requested (ax) ; Pointer of the block to divide (bx) ; Returns: Address of the first byte of user space available ; from the heap (ax) ;----------------------------------------------------------------------------- AllocatePartialBlock PROC NEAR sub [bx.bsize],ax ;make room! mov si,bx ;si = new block address add si,[bx.bsize] mov di,si ;di = the block after the new block add di,ax inc ax mov [si.bsize],ax mov [si.prev_real],bx mov [di.prev_real],si add si,UsedHeaderSize mov ax,si ret AllocatePartialBlock ENDP ;----------------------------------------------------------------------------- ; Attempts to expand a block, relocating it if necessary ; realloc helper function ;----------------------------------------------------------------------------- ; Args: Pointer to the old block (bx) ; Size of the block (cx) ; Number of bytes requested (ax) ; Returns: Address of the first byte of user space available ; from the heap if successful (bx) ; NULL if failure (bx) ;----------------------------------------------------------------------------- ExpandBlock PROC NEAR mov bp,sp push bx ;[bp-2] = old block push ax ;[bp-4] = new size push cx ;[bp-6] = old block size push ax call _malloc ;ax = data area of new block pop bx ;cleanup stack mov bx,ax or ax,ax jz @@AllDone ;malloc failed @@MallocOK: push ds ;move the data to the new block pop es cld mov di,ax ;di = data area of new block mov si,[bp-2] ;si = old block mov cx,[si.bsize] ;cx = old block size add si,UsedHeaderSize ;si = data area of old block push si ;save for call to _free sub cx,UsedHeaderSize+1 ;cx = number of bytes in old data area @@MoveIt: shr cx,1 ;cx = number of words in data area rep movsw mov [bp-2],ax ;save data area of new block in scratch area call _free pop bx ;cleanup stack mov bx,[bp-2] @@AllDone: add sp,6 ret ExpandBlock ENDP ;----------------------------------------------------------------------------- ; Shrinks a block ; realloc helper function ;----------------------------------------------------------------------------- ; Args: Pointer to the block (bx) ; Size of the block (cx) ; Normalized number of bytes requested (dx) ; Returns: Address of the first byte of user space available ; from the heap if successful (bx) ;----------------------------------------------------------------------------- ShrinkBlock PROC NEAR mov ax,dx ;ax = requested block size add dx,FreeHeaderSize cmp dx,cx ja @@AllDone mov dx,cx ;dx = old block size @@DivideTheBlock: cmp bx,[__last] ;last block in the heap? jne @@InnerBlock @@LastBlock: mov [bx.bsize],ax inc [bx.bsize] add ax,bx push bx ;save the old block push ax call ___brk ;reset the break level pop bx ;cleanup stack pop bx ;restore old block jmp SHORT @@AllDone @@InnerBlock: mov di,bx add di,ax ;di = new (free) block mov [di.prev_real],bx sub dx,ax ;dx = size of new (free) block sub [bx.bsize],dx mov si,di ;si = next block after the new one add si,dx mov [si.prev_real],di ;adjust the link inc dx ;mark it as used mov [di.bsize],dx mov cx,bx ;save the old block mov bx,di call FreeInnerBlock mov bx,cx ;restore old block @@AllDone: add bx,UsedHeaderSize ret ShrinkBlock ENDP ;----------------------------------------------------------------------------- ; Attempts to reallocate a block ;----------------------------------------------------------------------------- ; Args: Pointer to the old block (stack) ; Number of bytes requested (stack) ; Returns: Address of the first byte of user space available ; from the heap if successful (ax) ; NULL if failure (ax) ;----------------------------------------------------------------------------- PUBLIC _realloc _realloc PROC DIST push si push di push bp mov bp,sp mov bx,[bp+(8+EXTRADISP)] ;bx = pointer to the block to realloc mov ax,[bp+(10+EXTRADISP)] ;ax = number of bytes requested or ax,ax ;does he really want 0 bytes??? jz @@FreeIt ;let's give him what he wants! or bx,bx ;did we get a NULL pointer? jz @@MallocIt ;OK, try to malloc it sub bx,UsedHeaderSize ;make bx = start of block mov cx,[bx.bsize] ;cx = size of block dec cx mov dx,ax add dx,UsedHeaderSize+1 ;add the header size and and dx,0fffeh ;force a word boundary cmp dx,FreeHeaderSize jae @@BigEnough mov dx,FreeHeaderSize @@BigEnough: cmp cx,dx jb @@ExpandIt ja @@ShrinkIt @@NoChange: add bx,UsedHeaderSize jmp SHORT @@Resized @@ShrinkIt: call ShrinkBlock jmp SHORT @@Resized @@ExpandIt: call ExpandBlock @@Resized: mov ax,bx jmp SHORT @@AllDone @@MallocIt: push ax call _malloc jmp SHORT @@Cleanup @@FreeIt: push bx call _free xor ax, ax @@Cleanup: pop bx ;cleanup stack @@AllDone: pop bp ;all done pop di pop si ret _realloc ENDP ENDIF