]> git.sur5r.net Git - cc65/blob - libsrc/common/free.s
Reset signal handler to SIG_DFL on a call to raise()
[cc65] / libsrc / common / free.s
1 ;
2 ; Ullrich von Bassewitz, 19.03.2000
3 ;
4 ; Free a block on the heap.
5 ;
6 ; void __fastcall__ free (void* block);
7 ;
8 ;
9 ; C implementation was:
10 ;
11 ; void free (void* block)
12 ; /* Release an allocated memory block. The function will accept NULL pointers
13 ;  * (and do nothing in this case).
14 ;  */
15 ; {
16 ;     unsigned* b;
17 ;     unsigned size;
18 ;     struct freeblock* f;
19 ;
20 ;
21 ;     /* Allow NULL arguments */
22 ;     if (block == 0) {
23 ;         return;
24 ;     }
25 ;
26 ;     /* Get a pointer to the real memory block, then get the size */
27 ;     b = (unsigned*) block;
28 ;     size = *--b;
29 ;
30 ;     /* Check if the block is at the top of the heap */
31 ;     if (((int) b) + size == (int) _hptr) {
32 ;
33 ;         /* Decrease _hptr to release the block */
34 ;         _hptr = (unsigned*) (((int) _hptr) - size);
35 ;
36 ;         /* Check if the last block in the freelist is now at heap top. If so,
37 ;          * remove this block from the freelist.
38 ;          */
39 ;         if (f = _hlast) {
40 ;             if (((int) f) + f->size == (int) _hptr) {
41 ;                 /* Remove the last block */
42 ;                 _hptr = (unsigned*) (((int) _hptr) - f->size);
43 ;                 if (_hlast = f->prev) {
44 ;                   /* Block before is now last block */
45 ;                     f->prev->next = 0;
46 ;                 } else {
47 ;                     /* The freelist is empty now */
48 ;                     _hfirst = 0;
49 ;                 }
50 ;             }
51 ;         }
52 ;
53 ;     } else {
54 ;
55 ;               /* Not at heap top, enter the block into the free list */
56 ;       _hadd (b, size);
57 ;
58 ;     }
59 ; }
60 ;
61
62         .importzp       ptr1, ptr2, ptr3, ptr4
63         .export         _free, heapadd
64
65         .include        "_heap.inc"
66
67         .macpack        generic
68
69 ;-----------------------------------------------------------------------------
70 ; Code
71
72 _free:  sta     ptr2
73         stx     ptr2+1                  ; Save block
74
75 ; Is the argument NULL?
76
77         ora     ptr2+1                  ; Is the argument NULL?
78         beq     @L9                     ; Jump if yes
79
80 ; Decrement the given pointer by the admin space amount, so it points to the
81 ; real block allocated. The size of the block is stored in the admin space.
82 ; Remember the block size in ptr1.
83
84         lda     ptr2
85         sub     #HEAP_ADMIN_SPACE
86         sta     ptr2
87         bcs     @L1
88         dec     ptr2+1
89 @L1:    ldy     #freeblock_size+1
90         lda     (ptr2),y                ; High byte of size
91         sta     ptr1+1                  ; Save it
92         dey
93         lda     (ptr2),y
94         sta     ptr1
95
96 ; Check if the block is on top of the heap
97
98         add     ptr2
99         tay
100         lda     ptr2+1
101         adc     ptr1+1
102         cpy     __heapptr
103         bne     heapadd                 ; Add to free list
104         cmp     __heapptr+1
105         bne     heapadd
106
107 ; The pointer is located at the heap top. Lower the heap top pointer to
108 ; release the block.
109
110 @L3:    lda     ptr2
111         sta     __heapptr
112         lda     ptr2+1
113         sta     __heapptr+1
114
115 ; Check if the last block in the freelist is now at heap top. If so, remove
116 ; this block from the freelist.
117
118         lda     __heaplast
119         sta     ptr1
120         ora     __heaplast+1
121         beq     @L9                     ; Jump if free list empty
122         lda     __heaplast+1
123         sta     ptr1+1                  ; Pointer to last block now in ptr1
124
125         ldy     #freeblock_size
126         lda     (ptr1),y                ; Low byte of block size
127         add     ptr1
128         tax
129         iny                             ; High byte of block size
130         lda     (ptr1),y
131         adc     ptr1+1
132
133         cmp     __heapptr+1
134         bne     @L9                     ; Jump if last block not on top of heap
135         cpx     __heapptr
136         bne     @L9                     ; Jump if last block not on top of heap
137
138 ; Remove the last block
139
140         lda     ptr1
141         sta     __heapptr
142         lda     ptr1+1
143         sta     __heapptr+1
144
145 ; Correct the next pointer of the now last block
146
147         ldy     #freeblock_prev+1       ; Offset of ->prev field
148         lda     (ptr1),y
149         sta     ptr2+1                  ; Remember f->prev in ptr2
150         sta     __heaplast+1
151         dey
152         lda     (ptr1),y
153         sta     ptr2                    ; Remember f->prev in ptr2
154         sta     __heaplast
155         ora     __heaplast+1            ; -> prev == 0?
156         bne     @L8                     ; Jump if free list not empty
157
158 ; Free list is now empty (A = 0)
159
160         sta     __heapfirst
161         sta     __heapfirst+1
162
163 ; Done
164
165 @L9:    rts
166
167 ; Block before is now last block. ptr2 points to f->prev.
168
169 @L8:    lda     #$00
170         dey                             ; Points to high byte of ->next
171         sta     (ptr2),y
172         dey                             ; Low byte of f->prev->next
173         sta     (ptr2),y
174         rts                             ; Done
175
176 ; The block is not on top of the heap. Add it to the free list. This was
177 ; formerly a separate function called __hadd that was implemented in C as
178 ; shown here:
179 ;
180 ; void _hadd (void* mem, size_t size)
181 ; /* Add an arbitrary memory block to the heap. This function is used by
182 ;  * free(), but it does also allow usage of otherwise unused memory
183 ;  * blocks as heap space. The given block is entered in the free list
184 ;  * without any checks, so beware!
185 ;  */
186 ; {
187 ;     struct freeblock* f;
188 ;     struct freeblock* left;
189 ;     struct freeblock* right;
190 ;
191 ;     if (size >= sizeof (struct freeblock)) {
192 ;
193 ;       /* Set the admin data */
194 ;       f = (struct freeblock*) mem;
195 ;       f->size = size;
196 ;
197 ;       /* Check if the freelist is empty */
198 ;       if (_hfirst == 0) {
199 ;
200 ;           /* The freelist is empty until now, insert the block */
201 ;           f->prev = 0;
202 ;           f->next = 0;
203 ;           _hfirst = f;
204 ;           _hlast  = f;
205 ;
206 ;       } else {
207 ;
208 ;           /* We have to search the free list. As we are doing so, we check
209 ;            * if it is possible to combine this block with another already
210 ;            * existing block. Beware: The block may be the "missing link"
211 ;              * between *two* other blocks.
212 ;            */
213 ;           left = 0;
214 ;           right = _hfirst;
215 ;           while (right && f > right) {
216 ;               left = right;
217 ;               right = right->next;
218 ;           }
219 ;
220 ;
221 ;           /* Ok, the current block must be inserted between left and right (but
222 ;            * beware: one of the two may be zero!). Also check for the condition
223 ;            * that we have to merge two or three blocks.
224 ;            */
225 ;           if (right) {
226 ;               /* Check if we must merge the block with the right one */
227 ;                       if (((unsigned) f) + size == (unsigned) right) {
228 ;                   /* Merge with the right block */
229 ;                   f->size += right->size;
230 ;                   if (f->next = right->next) {
231 ;                               f->next->prev = f;
232 ;                   } else {
233 ;                       /* This is now the last block */
234 ;                       _hlast = f;
235 ;                   }
236 ;               } else {
237 ;                   /* No merge, just set the link */
238 ;                   f->next = right;
239 ;                   right->prev = f;
240 ;               }
241 ;           } else {
242 ;               f->next = 0;
243 ;               /* Special case: This is the new freelist end */
244 ;               _hlast = f;
245 ;           }
246 ;           if (left) {
247 ;               /* Check if we must merge the block with the left one */
248 ;               if ((unsigned) f == ((unsigned) left) + left->size) {
249 ;                   /* Merge with the left block */
250 ;                   left->size += f->size;
251 ;                   if (left->next = f->next) {
252 ;                       left->next->prev = left;
253 ;                   } else {
254 ;                       /* This is now the last block */
255 ;                       _hlast = left;
256 ;                   }
257 ;               } else {
258 ;                   /* No merge, just set the link */
259 ;                   left->next = f;
260 ;                   f->prev = left;
261 ;               }
262 ;           } else {
263 ;               f->prev = 0;
264 ;               /* Special case: This is the new freelist start */
265 ;               _hfirst = f;
266 ;           }
267 ;       }
268 ;     }
269 ; }
270 ;
271
272 ; Check if the free list is empty, storing _hfirst into ptr3 for later
273
274 heapadd:
275         lda     __heapfirst
276         sta     ptr3
277         lda     __heapfirst+1
278         sta     ptr3+1
279         ora     ptr3
280         bne     SearchFreeList
281
282 ; The free list is empty, so this is the first and only block. A contains
283 ; zero if we come here.
284
285         ldy     #freeblock_next-1
286 @L2:    iny                             ; f->next = f->prev = 0;
287         sta     (ptr2),y
288         cpy     #freeblock_prev+1        ; Done?
289         bne     @L2
290
291         lda     ptr2
292         ldx     ptr2+1
293         sta     __heapfirst
294         stx     __heapfirst+1           ; _heapfirst = f;
295         sta     __heaplast
296         stx     __heaplast+1            ; _heaplast = f;
297
298         rts                             ; Done
299
300 ; We have to search the free list. As we are doing so, check if it is possible
301 ; to combine this block with another, already existing block. Beware: The
302 ; block may be the "missing link" between two blocks.
303 ; ptr3 contains _hfirst (the start value of the search) when execution reaches
304 ; this point, Y contains size+1. We do also know that _heapfirst (and therefore
305 ; ptr3) is not zero on entry.
306
307 SearchFreeList:
308         lda     #0
309         sta     ptr4
310         sta     ptr4+1                  ; left = 0;
311         ldy     #freeblock_next+1
312         ldx     ptr3
313
314 @Loop:  lda     ptr3+1                  ; High byte of right
315         cmp     ptr2+1
316         bne     @L1
317         cpx     ptr2
318         beq     @L2
319 @L1:    bcs     CheckRightMerge
320
321 @L2:    stx     ptr4                    ; left = right;
322         sta     ptr4+1
323
324         dey                             ; Points to next
325         lda     (ptr3),y                ; right = right->next;
326         tax
327         iny                             ; Points to next+1
328         lda     (ptr3),y
329         stx     ptr3
330         sta     ptr3+1
331         ora     ptr3
332         bne     @Loop
333
334 ; If we come here, the right pointer is zero, so we don't need to check for
335 ; a merge. The new block is the new freelist end.
336 ; A is zero when we come here, Y points to next+1
337
338         sta     (ptr2),y                ; Clear high byte of f->next
339         dey
340         sta     (ptr2),y                ; Clear low byte of f->next
341
342         lda     ptr2                    ; _heaplast = f;
343         sta     __heaplast
344         lda     ptr2+1
345         sta     __heaplast+1
346
347 ; Since we have checked the case that the freelist is empty before, if the
348 ; right pointer is NULL, the left *cannot* be NULL here. So skip the
349 ; pointer check and jump right to the left block merge
350
351         jmp     CheckLeftMerge2
352
353 ; The given block must be inserted between left and right, and right is not
354 ; zero.
355
356 CheckRightMerge:
357         lda     ptr2
358         add     ptr1                    ; f + size
359         tax
360         lda     ptr2+1
361         adc     ptr1+1
362
363         cpx     ptr3
364         bne     NoRightMerge
365         cmp     ptr3+1
366         bne     NoRightMerge
367
368 ; Merge with the right block. Do f->size += right->size;
369
370         ldy     #freeblock_size
371         lda     ptr1
372         add     (ptr3),y
373         sta     (ptr2),y
374         iny                             ; Points to size+1
375         lda     ptr1+1
376         adc     (ptr3),y
377         sta     (ptr2),y
378
379 ; Set f->next = right->next and remember f->next in ptr1 (we don't need the
380 ; size stored there any longer)
381
382         iny                             ; Points to next
383         lda     (ptr3),y                ; Low byte of right->next
384         sta     (ptr2),y                ; Store to low byte of f->next
385         sta     ptr1
386         iny                             ; Points to next+1
387         lda     (ptr3),y                ; High byte of right->next
388         sta     (ptr2),y                ; Store to high byte of f->next
389         sta     ptr1+1
390         ora     ptr1
391         beq     @L1                     ; Jump if f->next zero
392
393 ; f->next->prev = f;
394
395         iny                             ; Points to prev
396         lda     ptr2                    ; Low byte of f
397         sta     (ptr1),y                ; Low byte of f->next->prev
398         iny                             ; Points to prev+1
399         lda     ptr2+1                  ; High byte of f
400         sta     (ptr1),y                ; High byte of f->next->prev
401         jmp     CheckLeftMerge          ; Done
402
403 ; f->next is zero, this is now the last block
404
405 @L1:    lda     ptr2                    ; _heaplast = f;
406         sta     __heaplast
407         lda     ptr2+1
408         sta     __heaplast+1
409         jmp     CheckLeftMerge
410
411 ; No right merge, just set the link.
412
413 NoRightMerge:
414         ldy     #freeblock_next         ; f->next = right;
415         lda     ptr3
416         sta     (ptr2),y
417         iny                             ; Points to next+1
418         lda     ptr3+1
419         sta     (ptr2),y
420
421         iny                             ; Points to prev
422         lda     ptr2                    ; right->prev = f;
423         sta     (ptr3),y
424         iny                             ; Points to prev+1
425         lda     ptr2+1
426         sta     (ptr3),y
427
428 ; Check if the left pointer is zero
429
430 CheckLeftMerge:
431         lda     ptr4                    ; left == NULL?
432         ora     ptr4+1
433         bne     CheckLeftMerge2         ; Jump if there is a left block
434
435 ; We don't have a left block, so f is actually the new freelist start
436
437         ldy     #freeblock_prev
438         sta     (ptr2),y                ; f->prev = 0;
439         iny
440         sta     (ptr2),y
441
442         lda     ptr2                    ; _heapfirst = f;
443         sta     __heapfirst
444         lda     ptr2+1
445         sta     __heapfirst+1
446
447         rts                             ; Done
448
449 ; Check if the left block is adjacent to the following one
450
451 CheckLeftMerge2:
452         ldy     #freeblock_size         ; Calculate left + left->size
453         lda     (ptr4),y                ; Low byte of left->size
454         add     ptr4
455         tax
456         iny                             ; Points to size+1
457         lda     (ptr4),y                ; High byte of left->size
458         adc     ptr4+1
459
460         cpx     ptr2
461         bne     NoLeftMerge
462         cmp     ptr2+1
463         bne     NoLeftMerge             ; Jump if blocks not adjacent
464
465 ; Merge with the left block. Do left->size += f->size;
466
467         dey                             ; Points to size
468         lda     (ptr4),y
469         add     (ptr2),y
470         sta     (ptr4),y
471         iny                             ; Points to size+1
472         lda     (ptr4),y
473         adc     (ptr2),y
474         sta     (ptr4),y
475
476 ; Set left->next = f->next and remember left->next in ptr1.
477
478         iny                             ; Points to next
479         lda     (ptr2),y                ; Low byte of f->next
480         sta     (ptr4),y
481         sta     ptr1
482         iny                             ; Points to next+1
483         lda     (ptr2),y                ; High byte of f->next
484         sta     (ptr4),y
485         sta     ptr1+1
486         ora     ptr1                    ; left->next == NULL?
487         beq     @L1
488
489 ; Do left->next->prev = left
490
491         iny                             ; Points to prev
492         lda     ptr4                    ; Low byte of left
493         sta     (ptr1),y
494         iny
495         lda     ptr4+1                  ; High byte of left
496         sta     (ptr1),y
497         rts                             ; Done
498
499 ; This is now the last block, do _heaplast = left
500
501 @L1:    lda     ptr4
502         sta     __heaplast
503         lda     ptr4+1
504         sta     __heaplast+1
505         rts                             ; Done
506
507 ; No merge of the left block, just set the link. Y points to size+1 if
508 ; we come here. Do left->next = f.
509
510 NoLeftMerge:
511         iny                             ; Points to next
512         lda     ptr2                    ; Low byte of left
513         sta     (ptr4),y
514         iny
515         lda     ptr2+1                  ; High byte of left
516         sta     (ptr4),y
517
518 ; Do f->prev = left
519
520         iny                             ; Points to prev
521         lda     ptr4
522         sta     (ptr2),y
523         iny
524         lda     ptr4+1
525         sta     (ptr2),y
526         rts                             ; Done
527
528
529
530
531
532