;_ 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 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 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 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 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 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 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 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 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