; ; Colin Leroy-Mira, 2024 ; ; void* __fastcall__ realloc (void* block, register size_t size) ; .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4, sp .import _malloc, _memcpy, _free .import pushax, popptr1, return0 .import incsp2, decsp2 .export _realloc .include "_heap.inc" .macpack generic ;---------------------------------------------------------------------------- ; Aliases for clarity block = ptr1 size = ptr2 ublock = ptr3 oldsize = ptr4 newblock = tmp1 ; (and tmp2) orgblock = tmp3 ; (and tmp4) ;---------------------------------------------------------------------------- ; Code _realloc: sta size ; Store size stx size+1 jsr popptr1 ; Pop block lda block+1 ; Is block null? tax ora block bne :+ lda size ; Block is null, just malloc ldx size+1 jmp _malloc : lda size ; Is size 0? ora size+1 bne :+ lda block ; It is: free block (high byte already in X) jsr _free jmp return0 : clc ; Add internal used size lda size adc #HEAP_ADMIN_SPACE sta size bcc :+ inc size+1 bne :+ lda #$00 ; Size high byte now 0: We overflowed! tax rts : ldx size+1 ; Should we round size up? bne :+ cmp #.sizeof (freeblock) bcs :+ lda #.sizeof (freeblock) sta size ; (we presuppose that sizeof (freeblock) is < 256) : lda block ; Get pointer to raw memory block sta orgblock ; Store original pointer sec sbc #.sizeof(usedblock) sta ublock lda block+1 sta orgblock+1 ; Finish storing original pointer sbc #0 sta ublock+1 ; We have our usedblock struct ; Get block start ldy #usedblock::start+1 lda (ublock),y tax ; Backup ublock high dey lda (ublock),y sta ublock ; Store ublock stx ublock+1 ; Remember oldsize ldy #usedblock::size+1 lda (ublock),y sta oldsize+1 dey lda (ublock),y sta oldsize clc ; Is the block at heap top? adc ublock tay lda ublock+1 adc oldsize+1 cmp ___heapptr+1 bne must_malloc_new cpy ___heapptr bne must_malloc_new tya ; Put ___heapptr back in A sec ; Check if we have enough memory at heap top sbc oldsize ; Substract oldsize sta newblock lda ___heapptr+1 sbc oldsize+1 sta newblock+1 clc lda newblock ; And add size adc size sta newblock lda newblock+1 adc size+1 sta newblock+1 bcs must_malloc_new ; If we have a carry there we overflowed cmp ___heapend+1 bne :+ lda newblock cmp ___heapend : bcc :+ bne must_malloc_new : lda newblock ; There is enough space sta ___heapptr ; Update heapptr lda newblock+1 sta ___heapptr+1 ldy #usedblock::start+1 lda ublock+1 sta (ublock),y ; Update block start dey lda ublock sta (ublock),y dey .assert usedblock::size = usedblock::start-2, error lda size+1 sta (ublock),y ; Update block size dey lda size sta (ublock),y lda orgblock ; Return original block ldx orgblock+1 rts must_malloc_new: ; The block is not at heap top, or too big lda size+1 pha ; Backup new size (at this point the only ptr tax ; we'll need after malloc). tmp* are safe lda size ; from malloc, memcpy and free. pha jsr _malloc cmp #$00 ; Did malloc succeed? bne :+ cpx #$00 bne :+ pla ; Pop size backup and return NULL pla txa ; X already 0 rts ; No : sta newblock ; Yes, store newblock stx newblock+1 jsr pushax ; Push newblock for memcpy lda orgblock ; Push orgblock for memcpy ldx orgblock+1 jsr pushax sec ; Remove admin space from oldsize lda oldsize sbc #HEAP_ADMIN_SPACE sta oldsize+1 pla ; Restore new size to AX tay pla tax tya cmp oldsize ; Find the smallest size bcc :+ cpx oldsize+1 bcc :+ lda oldsize ldx oldsize+1 : jsr _memcpy ; And copy data lda orgblock ; Free old block ldx orgblock+1 jsr _free lda newblock ; Return new block ldx newblock+1 rts