mirror of https://github.com/nmlgc/ReC98.git
587 lines
26 KiB
NASM
587 lines
26 KiB
NASM
; *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 ?
|
|
NEARHEAP_Header 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 ?
|
|
HeapInfo 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
|
|
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
|
|
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
|
|
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
|