home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
QBasic & Borland Pascal & C
/
Delphi5.iso
/
C
/
Samples
/
CASM.ARJ
/
ALLOC.ASM
next >
Wrap
Assembly Source File
|
1988-05-05
|
15KB
|
771 lines
;_ alloc.asm Thu May 5 1988 Modified by: Walter Bright */
; Copyright (C) 1985-1988 by Northwest Software
; All rights reserved
; Written by Walter Bright
include macros.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;
; Do far pointer normalization
; SCRATCH is a scratch register we can destroy
normptr macro MSREG,LSREG,SCRATCH
mov SCRATCH,LSREG
and LSREG,0Fh
shr SCRATCH,1
shr SCRATCH,1
shr SCRATCH,1
shr SCRATCH,1
add MSREG,SCRATCH
endm
if LCODE
c_extrn sbrk,far
else
c_extrn sbrk,near
endif
begcode alloc
c_public malloc,calloc,realloc,free
; Storage allocator
begdata
c_public _baslnk
if SPTR
c_extrn _pastdata,word, _heapbottom,word
endif
if MSC
if SPTR
__baslnk dw offset DGROUP:__baslnk ;starting link for
; storage allocator
dw 0 ;give it a size of 0 so it is never allocated
__allocp dw offset DGROUP:__baslnk ;roving pointer for allocator
else
__baslnk dw offset DGROUP:__baslnk
dw seg DGROUP:__baslnk
dw 0
__allocp dw -1,?
endif
_allocp equ __allocp
_baslnk equ __baslnk
else
if SPTR
_baslnk dw offset dgroup:_baslnk ;starting link for
; storage allocator
dw 0 ;give it a size of 0 so it is never allocated
_allocp dw offset dgroup:_baslnk ;roving pointer for allocator
else
_baslnk dw offset dgroup:_baslnk
dw seg dgroup:_baslnk
dw 0
_allocp dw -1,?
endif
endif
enddata
; A block in the free list consists of:
; dw pointer to next block in list
; dw segment of next block in list (for LPTR)
; dw size of block in bytes (must be even) (including both words)
; When it's allocated,
; dw # of bytes in this block including this word
; db... the bytes allocated
.if32 macro r1H,r1L,b,r2H,r2L,lbl
local L1
.if r1H ne r2H, L1
cmp r1L,r2L
L1: j&b lbl
endm
mov32 macro ah,al,bh,bl
mov ah,bh
mov al,bl
endm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Allocate a block of data and clear it.
; Use:
; p = calloc(numelems,sizeof(elem));
; Returns:
; pointer to allocated data else NULL
func calloc
push BP
mov BP,SP
mov AX,P[BP] ;get numelems
mov BX,P+2[BP] ;get sizeof(elem)
.if BX e 1, C1 ;no need to multiply
mul BX
jc C3 ;if overflow
C1: push AX ;nbytes
callm malloc
mov SP,BP
ifdef MSC
if SPTR
tst AX
else
tst DX
endif
else
tst AX ;error?
endif
jz C2 ;yes
.save <DI>
if SPTR
ife ESeqDS
mov DX,DS
mov ES,DX
endif
mov DI,AX
mov DX,AX ;save pointer to result
mov CX,-2[DI] ;# of bytes
else
ifdef MSC
mov ES,DX
mov DI,AX
mov BX,AX
else
mov ES,AX
mov DI,BX
endif
mov CX,ES:-2[DI]
endif
shr CX,1 ;# of words (including byte count)
dec CX ;skip # of bytes
clr AX
cld
rep stosw ;clear the memory
if SPTR
mov AX,DX ;restore pointer to result
else
ifdef MSC
mov AX,BX ;DX:AX is pointer to result
else
mov AX,ES ;restore pointer to result
endif
endif
.restore <DI>
C2:
pop BP
ret
if SPTR
C3: clr AX
pop BP
ret
endif
if LPTR
C3: clr AX
ifdef MSC
cwd
else
mov BX,AX
endif
pop BP
ret
endif
c_endp calloc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Allocate a block of data.
; Use:
; char *malloc();
; p = malloc(nbytes);
; Returns:
; pointer to allocated data else NULL
func malloc
if SPTR
push BP
A4:
mov BP,SP
.save <SI,DI>
mov AX,P[BP] ;get nbytes
add AX,3 ;need another word for length info
and AX,0FFFEh ;round up to nearest word
.if AX b 4, allocerr ;can't allocate 0 bytes
mov BP,2 ;save some bytes
; mov SI,_allocp ;last item
mov SI,_baslnk ;last item
mov CX,SI ;CX to save bytes
jmps A2
A1: mov SI,DI
.if SI e CX, trysbrk ;wrapped around, didn't find any
A2: mov DI,[SI] ;next item in list
.if AX a [DI+BP], A1 ;not big enough
je A3 ;exactly big enough
add AX,BP ;we'll need another 2 bytes
.if AX e [DI+BP],A3 ;have to allocate an entire block
sub AX,BP
;Allocate from bottom of free block. Desirable in order to delay
;stack overflow as long as possible.
; DI -> free block
; SI -> previous free block
; AX = # of bytes in allocated block
add [SI],AX ;link to new free block
mov SI,[SI] ;pointer to new free block
mov CX,[DI+BP] ;number of bytes in block we're splitting
sub CX,AX ;CX = remaining bytes
mov [SI+BP],CX ;# of bytes in this block
A3: xchg AX,[DI] ;[DI] = # of bytes, AX = next free block
mov [SI],AX ;skip the DI entry in list
mov _allocp,SI
lea AX,[DI+BP] ;pointer to area allocated (DI + 2)
A6: .restore <DI,SI>
pop BP
ret
trysbrk: ;try sbrk() to grow our data segment
.if AX ae 256, A5
mov AX,256 ;256 byte chunk minimum size
A5: push AX
callm sbrk
pop BX
.if AX e -1, allocerr ;failed
add AX,2 ;point past # of bytes allocated
push AX
callm free ;add allocated memory into free list
pop BX
.restore <DI,SI>
jmp A4 ;try again
allocerr:
clr AX ;NULL
jmp A6
else ;LPTR
;;;;;;;;;;;;;;;;;;;;;;;;
; malloc() for large data models
push BP
push DS
.if _allocp ne -1, A4 ;if already initialized
mov32 AX,BX _baslnk+2,_baslnk
normptr AX,BX, CX ;normalize _baslnk
mov32 _baslnk+2,_baslnk AX,BX
mov32 _allocp+2,_allocp AX,BX
A4: mov BP,SP
.save <SI,DI>
;A4: nbytes = (nbytes + 3) / 4 * 4
mov BP,P+2[BP]
add BP,3
and BP,0FFFEh
; if (nbytes < 4)
; return 0
.if BP ae 4, A12
A13: jmp mallocerr
A12:
; if (nbytes < 6)
.if BP ae 6, A5
; nbytes = 6
mov BP,6
A5:
; pstart = _baslnk
mov32 DX,DI _baslnk+2,_baslnk
; p = pstart
mov32 CX,SI DX,DI
; loop
A7: mov ES,CX
; pnext = p->next
mov32 AX,BX ES:2[SI],ES:[SI]
; if (nbytes <= pnext->size)
; break
mov ES,AX
.if BP be ES:4[BX], A6
; p = pnext
mov32 CX,SI AX,BX
; if (p == pstart)
.if32 CX,SI ne DX,DI, A7
; p = wsbrk(nbytes)
.if BP ae 512, A14
mov BP,512 ;512 minimum growth size
A14: push BP
callm sbrk ;extend program segment
pop BP ;fix stack
; if (p == -1)
; return 0
ifdef MSC
mov BX,AX
mov AX,DX
endif
.if BX e -1, A13 ;error
; wfree(p + 2)
add BX,2
push AX
push BX
callm free ;free new block
add SP,4
.if AX e -1, mallocerr
; goto A4
.restore <DI,SI>
jmp A4
A6: ; We have:
; pnext -> block to alloc
; pnext = AX,BX
;
; _allocp = p
mov32 _allocp+2,_allocp CX,SI
; if (nbytes + sizeof(*pnext) > pnext->size)
mov DX,BP
add DX,6
jc A10
.if DX be ES:4[BX], A9
; Allocate entire block that pnext points to.
; p->next = pnext->next
A10: mov32 DX,DI ES:2[BX],ES:[BX]
mov ES,CX
mov32 ES:2[SI],ES:[SI] DX,DI
; *pnext = pnext->size
mov ES,AX
mov DX,ES:4[BX]
mov ES:[BX],DX
; goto A8
jmp A8
A9:
; Create new block pnew that consists of the remainder of pnext.
; At this point, we have:
; BP = nbytes
; CX:SI = p
; AX:BX = pnext
; ES = AX
; pnew = pnext + nbytes
mov32 DX,DI AX,BX
add DI,BP ;no overflow possible here
normptr DX,DI, AX
mov AX,ES ;restore AX
; pnew->size = pnext->size - nbytes
push BP
neg BP
add BP,ES:4[BX]
mov ES,DX
mov ES:4[DI],BP
; pnew->next = pnext->next
mov DS,AX
mov DX,[BX]
mov ES:[DI],DX
mov DX,2[BX]
mov ES:2[DI],DX
mov DX,ES
; p->next = pnew
mov ES,CX
mov32 ES:2[SI],ES:[SI] DX,DI
; *pnext = nbytes
pop BP
mov [BX],BP ;DS == AX
;A8: return pnext + 2
A8: add BX,2
ifdef MSC
xchg DX,AX
xchg AX,BX
endif
A11: .restore <DI,SI>
pop DS
pop BP
ret
mallocerr:
clr AX
ifdef MSC
cwd
else
mov BX,AX
endif
jmp A11
endif
c_endp malloc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Reallocate memory that was allocated by malloc() or calloc().
; Use:
; char *realloc(char *p, unsigned nbytes)
; Returns:
; 0 error
; else pointer to reallocated memory
func realloc
push BP
mov BP,SP
mov AX,P+SIZEPTR[BP] ;AX = nbytes
tst AX ;trying to realloc() to 0 size?
jnz R6 ;no
pop BP
jmp near ptr free ;free(p)
R6:
;if realloced size is smaller, attempt to just shrink current block
if SPTR
mov BX,P[BP] ;BX = p
tst BX ;is p NULL?
jnz R5 ;no
;function just like malloc(nbytes)
push AX
callm malloc
mov SP,BP
pop BP
ret
R5: sub BX,2
mov CX,[BX] ;CX = # of bytes in this block
else
les BX,P[BP] ;ES:BX = p
mov CX,ES
or CX,BX ;is p NULL?
jnz R5 ;no
;function just like malloc(nbytes)
push AX
callm malloc
mov SP,BP
pop BP
ret
R5: sub BX,2
mov CX,ES:[BX]
endif
add AX,3
and AL,0FEh ;AX = real new size
sub CX,AX
jb R3 ;if allocating more bytes
.if CX b <SIZEPTR+2>, R4 ;size of free list entry
.save <DI>
mov DI,BX
add DI,AX
if SPTR
mov [DI],CX ;size of new fragment
add DI,2
mov [BX],AX ;realloced size of p
push DI
callm free
pop DI
else
mov ES:[DI],CX ;size of new fragment
mov ES:[BX],AX ;realloced size of p
mov BX,DI
mov AX,ES ;AX:BX is pointer to new fragment
normptr AX,BX, CX ;normalize it
add BX,2 ;point past size of fragment
push AX
push BX
callm free
add SP,SIZEPTR
endif
.restore <DI>
R4:
if SPTR
mov AX,P[BP]
else
ifdef MSC
mov32 DX,AX P+2[BP],P[BP] ;reload original pointer p
else
mov32 AX,BX P+2[BP],P[BP] ;reload original pointer p
endif
endif
jmps R1 ;no change, return p
;we'll have to allocate a new block, and free the old one
R3:
push P+SIZEPTR[BP]
callm malloc ;malloc(nbytes)
mov SP,BP
if LPTR
ifdef MSC
tst DX
else
tst AX
endif
else
tst AX
endif
jz rallocerr ;error
push AX ;save pointer to new memory
.save <SI,DI>
if SPTR
mov SI,P[BP] ;DS:SI -> original
ife ESeqDS
mov CX,DS
mov ES,CX
endif
mov DI,AX ;ES:DI -> new item
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
cld
rep movsw ;transfer the words
push P[BP]
callm free ;free the old one
add SP,SIZEPTR
else
ifdef MSC
push DX
else
push BX
endif
push DS
lds SI,P[BP] ;DS:SI -> original
ifdef MSC
mov ES,DX
mov DI,AX ;ES:DI -> new item
else
mov ES,AX
mov DI,BX ;ES:DI -> new item
endif
mov CX,-2[SI]
.if CX be ES:-2[DI], R2
mov CX,ES:-2[DI] ;CX = smaller of two sizes
R2: shr CX,1 ;# of words
dec CX ;compensate for extra word in beginning
cld
rep movsw ;transfer the words
pop DS
push P+2[BP]
push P[BP]
callm free ;free the old one
add SP,SIZEPTR
ifdef MSC
pop DX
else
pop BX
endif
endif
.restore <DI,SI>
tst AX
pop AX ;restore pointer to new memory
jz R1
rallocerr:
clr AX
if LPTR
ifdef MSC
cwd
else
mov BX,AX
endif
endif
R1: pop BP
ret
c_endp realloc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Free memory that was allocated by malloc() or calloc().
; Use:
; free(p);
; Returns:
; 0 success
; -1 error
func free
if SPTR
push BP
mov BP,SP
.save <SI,DI>
mov BX,P[BP] ;get p
tst BX ;pass a NULL pointer?
jz F5 ;yes, return 0
mov BP,2 ;to save some bytes
;check if below bottom of pool
.if BX be _heapbottom, freeerr ;if below bottom of pool
.if BX ae _pastdata, freeerr ;if above top of pool
test BX,1 ;odd?
jne freeerr
sub BX,BP ;point to start of block
mov AX,[BX] ;# of bytes in block to be freed
; Try to find SI and DI such that SI < BX < DI
mov SI,_allocp ;try our roving pointer
.if SI b BX, F1 ;a good starting point
mov SI, offset DGROUP:_baslnk
jmps F1
F6: mov SI,DI
F1: mov DI,[SI] ;the next in the list
.if SI ae BX, freeerr
.if DI a BX, F2 ;got it
.if DI a SI, F6 ;no wrap around (SI < DI < BX)
; We have SI < BX < DI (relative position in list)
F2: mov CX,[SI+BP] ;# of bytes in previous block
add CX,SI ;+ link
.if CX ne BX, F3 ;if can't collapse with prev block
add [SI+BP],AX
jmps F4
F3: mov 2[BX],AX ;store # of bytes in freed block
mov [BX],DI ;link to block after BX
mov [SI],BX ;link to BX
mov SI,BX
; See if we can collapse SI with DI
; SI -> block just before DI
; DI -> block just after SI
; BP = 2
F4: mov _allocp,SI ;for next time
mov AX,[SI+BP]
add AX,SI
.if AX ne DI, F5 ;nope
mov AX,[DI] ;link after DI
mov [SI],AX ;becomes link after SI
mov AX,[DI+BP] ;# of bytes in DI
add [SI+BP],AX ;add to # of bytes in SI
F5: clr AX ;success
F7: .restore <DI,SI>
pop BP
ret
freeerr:
mov AX,-1 ;error
jmp F7
else ;LPTR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; free() for large data models
push BP
mov BP,SP
.save <SI,DI>
push DS
mov32 DX,DI P+2[BP],P[BP] ;DX:DI = pfree
; if (pfree == NULL)
; return 0;
mov AX,DX
or AX,DI
jnz F9
jmp F4
; if (pfree <= _heapbottom || pfree >= _pastdata || pfree & 1)
; goto freeerr
F9: test DI,1 ;odd pointers are errors
je F7
F8: jmp freeerr
F7:
; pfree -= 2
sub DI,2
; p = (_allocp < pfree) ? _allocp : &_baslnk
mov32 CX,SI _allocp+2,_allocp ;CX:SI = _allocp
.if32 CX,SI b DX,DI, F1
mov32 CX,SI <seg DGROUP:_baslnk>,<offset DGROUP:_baslnk>
F1: mov ES,CX ;ES:SI = p
; loop
; pnext = p->next
mov32 AX,BX ES:2[SI],ES:[SI] ;AX:BX = pnext
; if (p >= pfree)
; goto freeerr
.if32 CX,SI ae DX,DI, F8
; if (pnext > pfree || pnext <= p)
; break
.if32 AX,BX a DX,DI, F2
.if32 AX,BX be CX,SI, F2
; p = pnext
mov32 CX,SI AX,BX
jmp F1
F2:
; _allocp = p
mov32 _allocp+2,_allocp CX,SI
; pfree->size = *pfree
mov DS,DX
mov BP,[DI]
mov 4[DI],BP
; pfree->next = pnext
mov32 2[DI],[DI] AX,BX
; p->next = pfree
mov DS,CX
mov32 2[SI],[SI] DX,DI
; if (p + p->size != pfree)
mov32 AX,BX CX,SI
add BX,4[SI]
normptr AX,BX, CX ;normalize AX,BX
mov CX,DS ;restore CX
.if32 AX,BX e DX,DI, F3
; p = pfree
mov32 CX,SI DX,DI
F3:
; while (p + p->size == p->next)
mov DS,CX
mov32 AX,BX CX,SI
add BX,4[SI]
normptr AX,BX, DX ;normalize AX,BX
mov32 DX,DI 2[SI],[SI] ;DX:DI = p->pnext
mov ES,DX
.if32 AX,BX ne DX,DI, F4
; if (p->size + p->next->size < 64k)
mov BP,4[SI]
add BP,ES:4[DI]
jc F5
; p->size += p->next->size
mov 4[SI],BP
; p->next = p->next->next
mov AX,ES:[DI]
mov [SI],AX
mov AX,ES:2[DI]
mov 2[SI],AX
jmp F3
; else
F5:
; pnew = p + 64k - 16 ;CX:SI = p
mov AX,CX
add AX,0FFFh ;AX:SI = pnew
mov DS,AX ;DS:SI = pnew
; pnew->next = p->next->next ;ES:DI = p->next
mov32 AX,BX ES:2[DI],ES:[DI]
mov32 2[SI],[SI] AX,BX
; pnew->size = p->size + p->next->size - (64k - 16)
sub BP,0FFF0h
mov 4[SI],BP
; p->next = pnew
mov AX,DS
mov DS,CX
mov32 2[SI],[SI] AX,SI
; p->size = (64k - 16)
mov 4[SI],0FFF0h
; p = pnew
mov CX,AX ;only the segments are different
jmp F3
; return 0
F4: clr AX
ifdef MSC
cwd ;return NULL (for realloc())
else
mov BX,AX
endif
F6: pop DS
.restore <DI,SI>
pop BP
ret
;
freeerr:
; return -1
mov AX,-1
jmp F6
endif
c_endp free
endcode alloc
end