forked from cc65/cc65
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request cc65#2384 from colinleroy/asm-realloc
Rewrite realloc in asm
- Loading branch information
Showing
2 changed files
with
213 additions
and
114 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
; | ||
; 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 | ||
lda oldsize+1 | ||
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 |