1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
|
;
; Ullrich von Bassewitz, 17.7.2000
;
; Allocate a block from the heap.
;
; void* __fastcall__ malloc (size_t size);
;
;
; C implementation was:
;
; void* malloc (size_t size)
; /* Allocate memory from the given heap. The function returns a pointer to the
; ** allocated memory block or a NULL pointer if not enough memory is available.
; ** Allocating a zero size block is not allowed.
; */
; {
; struct freeblock* f;
; unsigned* p;
;
;
; /* Check for a size of zero, then add the administration space and round
; ** up the size if needed.
; */
; if (size == 0) {
; return 0;
; }
; size += HEAP_ADMIN_SPACE;
; if (size < sizeof (struct freeblock)) {
; size = sizeof (struct freeblock);
; }
;
; /* Search the freelist for a block that is big enough */
; f = _hfirst;
; while (f && f->size < size) {
; f = f->next;
; }
;
; /* Did we find one? */
; if (f) {
;
; /* We found a block big enough. If the block can hold just the
; ** requested size, use the block in full. Beware: When slicing blocks,
; ** there must be space enough to create a new one! If this is not the
; ** case, then use the complete block.
; */
; if (f->size - size < sizeof (struct freeblock)) {
;
; /* Use the actual size */
; size = f->size;
;
; /* Remove the block from the free list */
; if (f->prev) {
; /* We have a previous block */
; f->prev->next = f->next;
; } else {
; /* This is the first block, correct the freelist pointer */
; _hfirst = f->next;
; }
; if (f->next) {
; /* We have a next block */
; f->next->prev = f->prev;
; } else {
; /* This is the last block, correct the freelist pointer */
; _hlast = f->prev;
; }
;
; } else {
;
; /* We must slice the block found. Cut off space from the upper
; ** end, so we can leave the actual free block chain intact.
; */
;
; /* Decrement the size of the block */
; f->size -= size;
;
; /* Set f to the now unused space above the current block */
; f = (struct freeblock*) (((unsigned) f) + f->size);
;
; }
;
; /* Setup the pointer for the block */
; p = (unsigned*) f;
;
; } else {
;
; /* We did not find a block big enough. Try to use new space from the
; ** heap top.
; */
; if (((unsigned) _hend) - ((unsigned) _hptr) < size) {
; /* Out of heap space */
; return 0;
; }
;
;
; /* There is enough space left, take it from the heap top */
; p = _hptr;
; _hptr = (unsigned*) (((unsigned) _hptr) + size);
;
; }
;
; /* New block is now in p. Fill in the size and return the user pointer */
; *p++ = size;
; return p;
; }
;
.importzp ptr1, ptr2, ptr3
.export _malloc
.include "_heap.inc"
.macpack generic
;-----------------------------------------------------------------------------
; Code
_malloc:
sta ptr1 ; Store size in ptr1
stx ptr1+1
; Check for a size of zero, if so, return NULL
ora ptr1+1
beq Done ; a/x already contains zero
; Add the administration space and round up the size if needed
lda ptr1
add #HEAP_ADMIN_SPACE
sta ptr1
bcc @L1
inc ptr1+1
@L1: ldx ptr1+1
bne @L2
cmp #HEAP_MIN_BLOCKSIZE+1
bcs @L2
lda #HEAP_MIN_BLOCKSIZE
sta ptr1 ; High byte is already zero
; Load a pointer to the freelist into ptr2
@L2: lda __heapfirst
sta ptr2
lda __heapfirst+1
sta ptr2+1
; Search the freelist for a block that is big enough. We will calculate
; (f->size - size) here and keep it, since we need the value later.
jmp @L4
@L3: ldy #freeblock::size
lda (ptr2),y
sub ptr1
tax ; Remember low byte for later
iny ; Y points to freeblock::size+1
lda (ptr2),y
sbc ptr1+1
bcs BlockFound ; Beware: Contents of a/x/y are known!
; Next block in list
iny ; Points to freeblock::next
lda (ptr2),y
tax
iny ; Points to freeblock::next+1
lda (ptr2),y
stx ptr2
sta ptr2+1
@L4: ora ptr2
bne @L3
; We did not find a block big enough. Try to use new space from the heap top.
lda __heapptr
add ptr1 ; _heapptr + size
tay
lda __heapptr+1
adc ptr1+1
bcs OutOfHeapSpace ; On overflow, we're surely out of space
cmp __heapend+1
bne @L5
cpy __heapend
@L5: bcc TakeFromTop
beq TakeFromTop
; Out of heap space
OutOfHeapSpace:
lda #0
tax
Done: rts
; There is enough space left, take it from the heap top
TakeFromTop:
ldx __heapptr ; p = _heapptr;
stx ptr2
ldx __heapptr+1
stx ptr2+1
sty __heapptr ; _heapptr += size;
sta __heapptr+1
jmp FillSizeAndRet ; Done
; We found a block big enough. If the block can hold just the
; requested size, use the block in full. Beware: When slicing blocks,
; there must be space enough to create a new one! If this is not the
; case, then use the complete block.
; On input, x/a do contain the remaining size of the block. The zero
; flag is set if the high byte of this remaining size is zero.
BlockFound:
bne SliceBlock ; Block is large enough to slice
cpx #HEAP_MIN_BLOCKSIZE ; Check low byte
bcs SliceBlock ; Jump if block is large enough to slice
; The block is too small to slice it. Use the block in full. The block
; does already contain the correct size word, all we have to do is to
; remove it from the free list.
ldy #freeblock::prev+1 ; Load f->prev
lda (ptr2),y
sta ptr3+1
dey
lda (ptr2),y
sta ptr3
dey ; Points to freeblock::next+1
ora ptr3+1
beq @L1 ; Jump if f->prev zero
; We have a previous block, ptr3 contains its address.
; Do f->prev->next = f->next
lda (ptr2),y ; Load high byte of f->next
sta (ptr3),y ; Store high byte of f->prev->next
dey ; Points to next
lda (ptr2),y ; Load low byte of f->next
sta (ptr3),y ; Store low byte of f->prev->next
jmp @L2
; This is the first block, correct the freelist pointer
; Do _hfirst = f->next
@L1: lda (ptr2),y ; Load high byte of f->next
sta __heapfirst+1
dey ; Points to next
lda (ptr2),y ; Load low byte of f->next
sta __heapfirst
; Check f->next. Y points always to next if we come here
@L2: lda (ptr2),y ; Load low byte of f->next
sta ptr3
iny ; Points to next+1
lda (ptr2),y ; Load high byte of f->next
sta ptr3+1
iny ; Points to prev
ora ptr3
beq @L3 ; Jump if f->next zero
; We have a next block, ptr3 contains its address.
; Do f->next->prev = f->prev
lda (ptr2),y ; Load low byte of f->prev
sta (ptr3),y ; Store low byte of f->next->prev
iny ; Points to prev+1
lda (ptr2),y ; Load high byte of f->prev
sta (ptr3),y ; Store high byte of f->prev->next
jmp RetUserPtr ; Done
; This is the last block, correct the freelist pointer.
; Do _hlast = f->prev
@L3: lda (ptr2),y ; Load low byte of f->prev
sta __heaplast
iny ; Points to prev+1
lda (ptr2),y ; Load high byte of f->prev
sta __heaplast+1
jmp RetUserPtr ; Done
; We must slice the block found. Cut off space from the upper end, so we
; can leave the actual free block chain intact.
SliceBlock:
; Decrement the size of the block. Y points to size+1.
dey ; Points to size
lda (ptr2),y ; Low byte of f->size
sub ptr1
sta (ptr2),y
tax ; Save low byte of f->size in X
iny ; Points to size+1
lda (ptr2),y ; High byte of f->size
sbc ptr1+1
sta (ptr2),y
; Set f to the space above the current block, which is the new block returned
; to the caller.
txa ; Get low byte of f->size
add ptr2
tax
lda (ptr2),y ; Get high byte of f->size
adc ptr2+1
stx ptr2
sta ptr2+1
; Fill the size and start address into the admin space of the block
; (struct usedblock) and return the user pointer
FillSizeAndRet:
ldy #usedblock::size ; p->size = size;
lda ptr1 ; Low byte of block size
sta (ptr2),y
iny ; Points to freeblock::size+1
lda ptr1+1
sta (ptr2),y
RetUserPtr:
ldy #usedblock::start ; p->start = p
lda ptr2
sta (ptr2),y
iny
lda ptr2+1
sta (ptr2),y
; Return the user pointer, which points behind the struct usedblock
lda ptr2 ; return ++p;
ldx ptr2+1
add #HEAP_ADMIN_SPACE
bcc @L9
inx
@L9: rts
|