360 lines
8.3 KiB
NASM
360 lines
8.3 KiB
NASM
|
;_ page.asm Wed May 24 1989 Modified by: Walter Bright */
|
|||
|
; Copyright (C) 1989 by Walter Bright
|
|||
|
; All rights reserved
|
|||
|
; Written by Walter Bright
|
|||
|
|
|||
|
include macros.asm
|
|||
|
|
|||
|
; Storage allocator
|
|||
|
|
|||
|
begcode page
|
|||
|
|
|||
|
c_public page_malloc,page_calloc,page_realloc,page_free
|
|||
|
|
|||
|
; The start of each page (set by page_initialize()) looks like:
|
|||
|
pagesize equ 0 ;total size of this page
|
|||
|
maxsize equ 2 ;max size of a free block in free list
|
|||
|
allocp equ 4 ;roving pointer for allocator
|
|||
|
bassize equ 6 ;size of first block (0 so it
|
|||
|
; is never allocated)
|
|||
|
baslnk equ 8 ;offset of next free block
|
|||
|
pageoverhead equ 10 ;# of bytes of bookkeeping
|
|||
|
|
|||
|
; A block in the free list consists of:
|
|||
|
; dw size of block in bytes (must be even) (including both words)
|
|||
|
; dw pointer to next block in list
|
|||
|
|
|||
|
; When it's allocated,
|
|||
|
; dw # of bytes in this block including this word
|
|||
|
; db... the bytes allocated
|
|||
|
|
|||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|||
|
; Allocate a block of data and clear it.
|
|||
|
; Use:
|
|||
|
; unsigned page_calloc(void far *baseptr,unsigned size);
|
|||
|
; Returns:
|
|||
|
; offset of allocated data else 0
|
|||
|
|
|||
|
func page_calloc
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
push P+4[BP]
|
|||
|
push P+2[BP]
|
|||
|
push P[BP]
|
|||
|
callm page_malloc ;allocate a chunk
|
|||
|
mov SP,BP
|
|||
|
tst AX ;out of memory?
|
|||
|
jz C2 ;yes
|
|||
|
.save <DI>
|
|||
|
les DI,P[BP] ;ES:DI = baseptr
|
|||
|
add DI,AX ;offset to start of allocated memory
|
|||
|
mov DX,AX ;save offset of result
|
|||
|
mov CX,ES:-2[DI] ;# of bytes
|
|||
|
shr CX,1 ;# of words (including byte count)
|
|||
|
dec CX ;skip byte count
|
|||
|
clr AX
|
|||
|
rep stosw ;clear the memory
|
|||
|
mov AX,DX ;restore offset of result
|
|||
|
.restore <DI>
|
|||
|
C2:
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
c_endp page_calloc
|
|||
|
|
|||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|||
|
; Allocate a block of data.
|
|||
|
; unsigned page_malloc(void far *baseptr,unsigned size);
|
|||
|
; Returns:
|
|||
|
; offset of allocated data else 0
|
|||
|
|
|||
|
func page_malloc
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
.save <SI,DI>
|
|||
|
push DS
|
|||
|
lds BX,P[BP] ;DS:BX = baseptr
|
|||
|
mov AX,P+4[BP] ;get size
|
|||
|
add AX,3 ;need another word for length info
|
|||
|
and AL,0FEh ;round up to nearest word
|
|||
|
.if AX b 4, allocerr ;can't allocate 0 bytes
|
|||
|
|
|||
|
; mov SI,allocp[BX] ;last item
|
|||
|
mov SI,baslnk[BX] ;last item
|
|||
|
mov CX,SI ;CX to save bytes
|
|||
|
jmps A2
|
|||
|
|
|||
|
A1: mov SI,DI
|
|||
|
.if SI e CX, allocerr ;wrapped around, didn't find any
|
|||
|
A2: mov DI,2[BX+SI] ;next item in list
|
|||
|
.if AX a [BX+DI], A1 ;not big enough
|
|||
|
|
|||
|
je A3 ;exactly big enough
|
|||
|
add AX,2 ;we'll need another 2 bytes
|
|||
|
.if AX e [BX+DI],A3 ;have to allocate an entire block
|
|||
|
sub AX,2
|
|||
|
|
|||
|
;Allocate from bottom of free block.
|
|||
|
; DI -> free block
|
|||
|
; SI -> previous free block
|
|||
|
; AX = # of bytes in allocated block
|
|||
|
add 2[BX+SI],AX ;link to new free block
|
|||
|
mov SI,2[BX+SI] ;pointer to new free block
|
|||
|
mov CX,[BX+DI] ;number of bytes in block we're splitting
|
|||
|
sub CX,AX ;CX = remaining bytes
|
|||
|
mov [BX+SI],CX ;# of bytes in this block
|
|||
|
mov [BX+DI],AX ;[DI] = # of bytes
|
|||
|
|
|||
|
A3: mov AX,2[BX+DI] ;AX = next free block
|
|||
|
mov 2[BX+SI],AX ;skip the DI entry in list
|
|||
|
mov allocp[BX],SI
|
|||
|
mov AX,DI
|
|||
|
add AX,2 ;pointer to area allocated (DI + 2)
|
|||
|
A6: mov word ptr maxsize[BX],0 ;recalculate size of largest available block
|
|||
|
pop DS
|
|||
|
.restore <DI,SI>
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
|
|||
|
allocerr:
|
|||
|
clr AX ;NULL
|
|||
|
jmp A6
|
|||
|
c_endp page_malloc
|
|||
|
|
|||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|||
|
; Reallocate memory that was allocated by page_malloc() or page_calloc().
|
|||
|
; Use:
|
|||
|
; unsigned page_realloc(void far *baseptr,unsigned p, unsigned nbytes)
|
|||
|
; Returns:
|
|||
|
; 0 error
|
|||
|
; else offset of reallocated memory
|
|||
|
|
|||
|
func page_realloc
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
|
|||
|
mov AX,P+4+2[BP] ;AX = nbytes
|
|||
|
tst AX ;trying to realloc() to 0 size?
|
|||
|
jnz R6 ;no
|
|||
|
pop BP
|
|||
|
jmp near ptr page_free ;page_free(baseptr,p)
|
|||
|
|
|||
|
R6: ;If p is 0, this is just a page_malloc()
|
|||
|
mov BX,P+4[BP] ;BX = p
|
|||
|
tst BX ;is p NULL?
|
|||
|
jnz R5 ;no
|
|||
|
;function just like page_malloc(baseptr,nbytes)
|
|||
|
push AX
|
|||
|
push P+2[BP]
|
|||
|
push P[BP]
|
|||
|
callm page_malloc
|
|||
|
mov SP,BP
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
|
|||
|
;if realloced size is smaller, attempt to just shrink current block
|
|||
|
R5: push SI
|
|||
|
les SI,P[BP] ;ES:SI = baseptr
|
|||
|
sub BX,2
|
|||
|
mov CX,ES:[SI+BX] ;CX = # of bytes in this block
|
|||
|
add AX,3
|
|||
|
and AL,0FEh ;AX = real new size
|
|||
|
sub CX,AX
|
|||
|
jb R3 ;if allocating more bytes
|
|||
|
.if CX b 4, R4 ;size of free list entry
|
|||
|
mov ES:[SI+BX],AX ;realloced size of p
|
|||
|
add BX,AX ;BX -> new fragment
|
|||
|
mov ES:[SI+BX],CX ;size of new fragment
|
|||
|
add BX,2
|
|||
|
push BX
|
|||
|
push P+2[BP]
|
|||
|
push P[BP]
|
|||
|
callm page_free
|
|||
|
add SP,6
|
|||
|
R4:
|
|||
|
mov AX,P+4[BP]
|
|||
|
jmps R1 ;no change, return p
|
|||
|
|
|||
|
;we'll have to allocate a new block, copy the data over,
|
|||
|
;and free the old one
|
|||
|
R3:
|
|||
|
push P+6[BP]
|
|||
|
push P+2[BP]
|
|||
|
push P[BP]
|
|||
|
callm page_malloc ;page_malloc(baseptr,nbytes)
|
|||
|
add SP,6
|
|||
|
tst AX
|
|||
|
jz rallocerr ;error
|
|||
|
push AX ;save pointer to new memory
|
|||
|
.save <DI>
|
|||
|
push DS
|
|||
|
mov DI,AX
|
|||
|
add DI,SI ;ES:DI -> new item
|
|||
|
add SI,P+4[BP] ;DS:SI -> original
|
|||
|
mov CX,ES
|
|||
|
mov DS,CX
|
|||
|
mov CX,-2[SI]
|
|||
|
.if CX be -2[DI], R2
|
|||
|
mov CX,-2[DI] ;CX = smaller of two size
|
|||
|
R2: shr CX,1 ;# of words
|
|||
|
dec CX ;compensate for extra word in beginning
|
|||
|
rep movsw ;transfer the words
|
|||
|
pop DS
|
|||
|
push P+4[BP]
|
|||
|
push P+2[BP]
|
|||
|
push P[BP]
|
|||
|
callm page_free ;free the old one
|
|||
|
add SP,6
|
|||
|
.restore <DI>
|
|||
|
tst AX
|
|||
|
pop AX ;restore pointer to new memory
|
|||
|
jz R1
|
|||
|
rallocerr:
|
|||
|
clr AX
|
|||
|
R1: pop SI
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
c_endp page_realloc
|
|||
|
|
|||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|||
|
; Free memory that was allocated by page_malloc() or page_calloc().
|
|||
|
; Use:
|
|||
|
; int page_free(void far *baseptr,unsigned p);
|
|||
|
; Returns:
|
|||
|
; 0 success
|
|||
|
; -1 error
|
|||
|
|
|||
|
func page_free
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
.save <SI,DI>
|
|||
|
push DS
|
|||
|
mov DX,P+4[BP] ;get p
|
|||
|
tst DX ;pass a NULL pointer?
|
|||
|
jz F5 ;yes, return 0
|
|||
|
lds BX,P[BP] ;DS:BX = baseptr
|
|||
|
.if DX be baslnk+1, freeerr ;if below bottom of pool
|
|||
|
.if DX ae pagesize[BX], freeerr ;if above top of pool
|
|||
|
test DL,1 ;odd?
|
|||
|
jne freeerr
|
|||
|
sub DX,2 ;point to start of block
|
|||
|
mov SI,DX
|
|||
|
mov AX,[BX+SI] ;# of bytes in block to be freed
|
|||
|
|
|||
|
; Try to find SI and DI such that SI < DX < DI
|
|||
|
|
|||
|
mov SI,allocp[BX] ;try our roving pointer
|
|||
|
.if SI b DX, F1 ;a good starting point
|
|||
|
mov SI, bassize
|
|||
|
jmps F1
|
|||
|
|
|||
|
F6: mov SI,DI
|
|||
|
F1: mov DI,2[BX+SI] ;the next in the list
|
|||
|
.if SI ae DX, freeerr
|
|||
|
.if DI a DX, F2 ;got it
|
|||
|
.if DI a SI, F6 ;no wrap around (SI < DI < DX)
|
|||
|
|
|||
|
; We have SI < DX < DI (relative position in list)
|
|||
|
|
|||
|
F2: mov CX,[BX+SI] ;# of bytes in previous block
|
|||
|
add CX,SI ;+ link
|
|||
|
.if CX ne DX, F3 ;if can't collapse with prev block
|
|||
|
add [BX+SI],AX
|
|||
|
jmps F4
|
|||
|
|
|||
|
F3: mov 2[BX+SI],DX ;link to DX
|
|||
|
mov SI,DX
|
|||
|
mov 2[BX+SI],DI ;link to block after DX
|
|||
|
|
|||
|
; See if we can collapse SI with DI
|
|||
|
; SI -> block just before DI
|
|||
|
; DI -> block just after SI
|
|||
|
|
|||
|
F4: mov allocp[BX],SI ;for next time
|
|||
|
mov AX,[BX+SI]
|
|||
|
add AX,SI
|
|||
|
.if AX ne DI, F5 ;nope
|
|||
|
mov AX,2[DI+BX] ;link after DI
|
|||
|
mov 2[SI+BX],AX ;becomes link after SI
|
|||
|
mov AX,[BX+DI] ;# of bytes in DI
|
|||
|
add [BX+SI],AX ;add to # of bytes in SI
|
|||
|
|
|||
|
F5: clr AX ;success
|
|||
|
F7: mov word ptr maxsize[BX],0 ;recalc max size
|
|||
|
pop DS
|
|||
|
.restore <DI,SI>
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
|
|||
|
freeerr:
|
|||
|
mov AX,-1 ;error
|
|||
|
jmp F7
|
|||
|
c_endp page_free
|
|||
|
|
|||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|||
|
; Determine size of largest free block in page.
|
|||
|
; unsigned page_maxfree(void far *baseptr);
|
|||
|
|
|||
|
c_public page_maxfree
|
|||
|
func page_maxfree
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
push SI
|
|||
|
push DS
|
|||
|
lds SI,P[BP] ;DS:SI = baseptr
|
|||
|
mov AX,maxsize[SI]
|
|||
|
tst AX
|
|||
|
jnz M3 ;does not need recalculation
|
|||
|
clr AX
|
|||
|
mov BX,baslnk[SI] ;offset to first free block
|
|||
|
M1: mov CX,[SI+BX] ;size of free block
|
|||
|
.if CX b AX, M2
|
|||
|
mov AX,CX
|
|||
|
M2: mov CX,2[SI+BX] ;offset to next link
|
|||
|
xchg CX,BX
|
|||
|
.if BX a CX, M1
|
|||
|
sub AX,2
|
|||
|
jc M4
|
|||
|
mov maxsize[SI],AX
|
|||
|
M3: pop DS
|
|||
|
pop SI
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
|
|||
|
M4: clr AX
|
|||
|
jmp M3
|
|||
|
c_endp page_maxfree
|
|||
|
|
|||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|||
|
; Initialize memory allocation system in a page.
|
|||
|
; unsigned page_initialize(void far *baseptr,unsigned pagesize);
|
|||
|
; Returns:
|
|||
|
; size of largest allocatable block
|
|||
|
|
|||
|
c_public page_initialize
|
|||
|
func page_initialize
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
push DS
|
|||
|
lds BX,P[BP] ;DS:BX = baseptr
|
|||
|
mov AX,P+4[BP] ;AX = pagesize
|
|||
|
mov pagesize[BX],AX
|
|||
|
sub AX,pageoverhead
|
|||
|
mov word ptr allocp[BX],bassize
|
|||
|
mov word ptr bassize[BX],0
|
|||
|
mov word ptr baslnk[BX],pageoverhead
|
|||
|
|
|||
|
;Construct one big free block of the remainder
|
|||
|
mov pageoverhead[BX],AX ;size of that block
|
|||
|
mov word ptr pageoverhead+2[BX],bassize
|
|||
|
|
|||
|
sub AX,2 ;overhead per allocated block
|
|||
|
mov maxsize[BX],AX
|
|||
|
|
|||
|
pop DS
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
c_endp page_initialize
|
|||
|
|
|||
|
endcode page
|
|||
|
end
|
|||
|
|