summaryrefslogtreecommitdiff
path: root/programs/sub-suite/libc.s
diff options
context:
space:
mode:
authormrb0nk500 <b0nk@b0nk.xyz>2020-10-04 18:22:00 -0400
committermrb0nk500 <b0nk@b0nk.xyz>2020-10-04 18:22:00 -0400
commitca8e2f93acc794b00464c5513956bd84de258913 (patch)
tree83153822fdea777bd11a8254c0e4bd87fe1d8350 /programs/sub-suite/libc.s
parent784ff59108b887e246b0f33ff696dfd981659ab2 (diff)
- Added support for reading, and writing outside the
emulator's memory. All reads outside of the emulator's memory give back $/0xFF bytes, while all writes outside of the emulator's memory are ignored. - Implemented malloc(), and free() in the SuB Suite. In order to do this, I had to add support for a heap, which I did by reserving the first 3 banks of the address space (the first 192K), and by adding a routine that finds the end of the RAM. In this case, I set the starting address for the routine at bank 3 (bank 4 with one indexing), but, the routine's starting address isn't hardcoded, and thus, any starting address can be passed as an argument. The routine uses the fact that we can now read/write outside the emulator's memory, and also uses the fact that writing outside the emulator's memory will be ignored, and that reading outside the emulator's memory will always read $/0xFF bytes, and uses that to signal that it's reached the end of the RAM. - Added a test program for getting the size of RAM starting at address $/0x20000.
Diffstat (limited to 'programs/sub-suite/libc.s')
-rw-r--r--programs/sub-suite/libc.s355
1 files changed, 332 insertions, 23 deletions
diff --git a/programs/sub-suite/libc.s b/programs/sub-suite/libc.s
index d13d983..18c10db 100644
--- a/programs/sub-suite/libc.s
+++ b/programs/sub-suite/libc.s
@@ -1,12 +1,12 @@
; Simple libc implementation for the SuB Suite
strtoull:
- phy.w ; Preserve Y.
+ phy.q ; Preserve Y.
and #0 ; Reset A.
tay ; Reset Y.
pha.q ; Reset the value buffer.
@loop:
- lda (sp+20), y ; Get a character from the string.
+ lda (sp+26), y ; Get a character from the string.
pha ; Preserve the character.
jsr isdigit ; Is this character, a digit?
pla ; Get the character back.
@@ -24,12 +24,12 @@ strtoull:
sec ; Prepare for a non borrowing subtract.
sbc #'0' ; Get the numeric value from this digit.
@chkbase:
- cmp sp+19 ; Does the value match the base?
+ cmp sp+25 ; Does the value match the base?
bcs @end ; No, so we're done.
@addval:
tab ; Save the digit value.
lda.q sp+1 ; Get the value from the value buffer.
- mul sp+19 ; Multiply the value by the base.
+ mul sp+25 ; Multiply the value by the base.
clc ; Prepare for a non carrying add.
adc b ; Add the digit value to the total value.
sta.q sp+1 ; Place the value in the value buffer.
@@ -38,42 +38,39 @@ strtoull:
bra @loop ; Keep looping.
@end:
pla.q ; Get the value buffer back.
- ply.w ; Get Y back.
+ ply.q ; Get Y back.
ldb #0 ; Reset B.
rts ; End of strtoull.
strlen:
+ phy.q ; Preserve Y.
pha.q ; Set the temp variable to the argument.
- ldb #0 ; Reset B.
- tba ; Reset A.
- tax ; Reset X.
- phy.w ; Preserve Y.
- txy ; Reset Y.
+ and #0 ; Reset A.
+ tay ; Reset Y.
@loop:
- lda (sp+3), y ; Are we at the end of the string?
+ lda (sp+1), y ; Are we at the end of the string?
beq @end ; Yes, so we're done.
iny ; No, so increment the index.
bra @loop ; Keep looping.
@end:
- tyx ; Return the length in X.
- ply.w ; Get the preserved value back.
pla.q ; Get the argument back.
- txa ; Get the return value.
+ tya ; Get the return value.
+ ply.q ; Get the preserved value back.
rts ; End of strlen.
strcmp:
ldb #0 ; Reset B.
tba ; Reset A.
- phy.w ; Preserve Y.
+ phy.q ; Preserve Y.
tay ; Reset Y.
@loop:
ldb #0 ; Set the islong flag to false.
- lda (sp+19), y ; Are we at the end of the first string?
+ lda (sp+25), y ; Are we at the end of the first string?
beq cmpr ; Yes, so check if we're too short, or too long.
ldb #1 ; No, so set the islong flag to true.
- cmp (sp+11), y ; Is the character of both strings, the same?
+ cmp (sp+19), y ; Is the character of both strings, the same?
bne cmpr ; No, so check if we're too short, or too long.
iny ; Yes, so increment the index.
bra @loop ; Keep looping.
@@ -82,17 +79,17 @@ strccmp:
strcasecmp:
ldb #0 ; Reset B.
tba ; Reset A.
- phy.w ; Preserve Y.
+ phy.q ; Preserve Y.
tay ; Reset Y.
@loop:
ldb #0 ; Set the islong flag to false.
- lda (sp+19), y ; Are we at the end of the first string?
+ lda (sp+25), y ; Are we at the end of the first string?
beq cmpr ; Yes, so check if we're too short, or too long.
ldb #1 ; No, so set the islong flag to true.
jsr tolower ; Convert the character of string 1 to lowercase.
phb ; Preserve the islong flag.
pha ; Preserve the converted character.
- lda (sp+13), y ; Get the character of the second string.
+ lda (sp+19), y ; Get the character of the second string.
jsr tolower ; Convert the character of string 2 to lowercase.
tab ; Place it in B.
pla ; Get the character of string 1 back.
@@ -103,10 +100,10 @@ strcasecmp:
bra @loop ; Keep looping.
cmpr:
- lda (sp+11), y ; Are we at the end of the second string?
+ lda (sp+17), y ; Are we at the end of the second string?
beq @islong ; Yes, so check the islong flag.
@isshort:
- lda (sp+19), y ; No, but are we at the end of the first string?
+ lda (sp+25), y ; No, but are we at the end of the first string?
beq @short ; Yes, so return -1.
@islong:
cpb #1 ; Is the islong flag true?
@@ -120,7 +117,7 @@ cmpr:
@short:
lda #$FF ; Return -1.
@end:
- ply.w ; Get the preserved value back.
+ ply.q ; Get the preserved value back.
rts ; End of cmpr.
@@ -199,3 +196,315 @@ toupper:
and #$5F ; Yes, so convert it to uppercase.
@end:
rts ; End of toupper.
+
+
+; malloc: Dynamically allocate memory.
+; Input: A = size in bytes to allocate.
+; Output: A = Pointer to allocated memory.
+; Caller preserved registers: none.
+; Callie preserved registers: Y.
+
+malloc:
+ phy.q ; Preserve Y.
+ pha.q ; Preserve the size.
+ and #0 ; Reset A.
+ tay ; Reset Y.
+ pha.q ; Create two more local variables.
+ pha.q ;
+ lda.q sp+17 ; Get the size.
+ ora.d sp+21 ; Is the size zero?
+ beq @end ; Yes, so we're done.
+ lda.q sp+17 ; No, so get back the size.
+ clc ; Prepare for a non carrying add.
+ adc #ublk ; Add the size of a used block struct, to the size.
+ sta.q sp+17 ; Set the actual size.
+ lda.q heapf ; Get the first heap entry.
+ sta.q sp+9 ; Save it in the second local variable.
+ bra @checkblk ; Check the block.
+@findfblk:
+ ldy #fblk.size ; Get the size of this free block.
+ lda.q (sp+9), y ;
+ sec ; Prepare for a non borrowing subtract.
+ sbc.q sp+17 ; Subtract the size of this free block, with the passed size.
+ bcs @blkfound ; The result is less than, or equal to the free size, so return the found block.
+ ldy #fblk.next ; Get the next free block.
+ lda.q (sp+9), y ;
+ sta.q sp+9 ; Set the current free block, to the next free block.
+@checkblk:
+ ora.d sp+13 ; Is the address of this block, zero?
+ bne @findfblk ; No, so keep looping.
+ lda.q heapptr ; Yes, so get the current top of the heap.
+ clc ; Prepare for a non carrying add.
+ adc.q sp+17 ; Add the top of the heap, with the passed size.
+ bcs @outofspace ; The result overflowed, so return zero.
+ cmp.q heapend ; Is the top of the heap, less than, or equal to the max heap size?
+ bcc @inctop ; Yes, so increase the current top of the heap.
+ beq @inctop ;
+@outofspace:
+ and #0 ; Return zero.
+ bra @end ; We are done.
+@inctop:
+ pha.q ; Preserve heapptr + size.
+ lda.q heapptr ; Get the previous top of the heap.
+ sta.q sp+17 ; Set the current free block to the previous top of the heap.
+ pla.q ; Restore heapptr + size.
+ sta.q heapptr ; Increment the current top of the heap by the passed size.
+ bra @setsize ; Set the size, and start address into the used space of the block.
+@blkfound:
+ bne @sliceblk ; The block is big enough to slice.
+ cmp #fblk ; Is the block big enough to slice?
+ bcs @sliceblk ; Yes, so slice the block.
+ ldy #fblk.prev ; No, so get the previous free block.
+ lda.q (sp+9), y ;
+ sta.q sp+1 ; Set the third local variable, to the previous free block.
+ ora.d sp+5 ; Is the previous block, set to zero?
+ beq @setheapf ; Yes, so set the first block.
+ ldy #fblk.next ; No, so get the next free block.
+ lda.q (sp+9), y ;
+ sta.q (sp+1), y ; Set the previous block's next block, to the next block.
+ bra @chknxtblk ; Check the next block.
+@setheapf:
+ lda.q (sp+9), y ; Get the next block.
+ sta.q heapf ; Set the first heap entry to it.
+@chknxtblk:
+ lda.q (sp+9), y ; Get the next free block.
+ sta.q sp+1 ; Set the third local variable to the next free block.
+ ora.d sp+5 ; Is the next block, zero?
+ beq @setheapl ; Yes, so set the least heap entry.
+ ldy #fblk.prev ; No, so get the current previous block.
+ lda.q (sp+9), y ;
+ sta.q (sp+1), y ; Set the next block's previous block to the current previous block.
+ bra @retptr ; Return the pointer.
+@setheapl:
+ ldy #fblk.prev ; Get the current previous block.
+ lda.q (sp+9), y ;
+ sta.q heapl ; Set the last heap entry to the current previous block.
+ bra @retptr ; Return the pointer.
+@sliceblk:
+ ldy #fblk.size ; Get the size of the current block.
+ lda.q (sp+9). y ;
+ sec ; Prepare for a non borrowing subtract.
+ sbc.q sp+17 ; Subtract the current block's size from the passed size.
+ sta.q (sp+9), y ; Set the current block's size to the subtracted size.
+ clc ; Prepare for a non carrying add.
+ adc.q sp+9 ; Add the current block's size to the pointer of the current block.
+ sta.q sp+9 ; Set the current block to the space above it (return value).
+@setsize:
+ ldy #ublk.size ; Start setting the used block's size, to the passed size.
+ lda.q sp+17 ; Get the passed size.
+ sta.q (sp+9), y ; Set the used block's size to the passed size.
+@retptr:
+ ldy #ublk.start ; Start setting the used block's starting address, to the used block.
+ lda.q sp+9 ; Get the used block.
+ sta.q (sp+9), y ; Set the used block's starting address, to the used block.
+ clc ; Prepare for a non carrying add.
+ adc #ublk ; Return the pointer to the real memory block.
+@end:
+ ply.q ; Clean up the stack frame.
+ ply.q ;
+ ply.q ;
+ ply.q ; Restore Y.
+ rts ; End of malloc.
+
+
+; free: Free allocated memory.
+; Input: A = Pointer to allocated memory.
+; Output: none.
+; Caller preserved registers: none.
+; Callie preserved registers: A, B, Y.
+
+free:
+ pha.q ; Preserve A.
+ phb.q ; Preserve B.
+ phy.q ; Preserve Y.
+ pha.q ; Push the pointer argument to the stack.
+ and #0 ; Reset A.
+ tay ; Reset Y.
+ pha.q ; Add 3 more local variables.
+ pha.q ;
+ pha.q ;
+ lda.q sp+25 ; Get the passed pointer.
+ ora.d sp+29 ; Is the passed pointer NULL?
+ bne @getrealblk ; No, so get the real block.
+ bra @end ; Yes, so we're done.
+@getrealblk:
+ lda.q sp+25 ; Get the passed pointer.
+ sec ; Prepare for a non borrowing subtract.
+ sbc #8 ; Move the passed pointer back by one.
+ sta.q sp+25 ; Set the first local variable to the start of the used block.
+ lda.q (sp+25) ; Get the real block address.
+ sta.q sp+25 ; Set the passed pointer to the start of the real block.
+ ldy #ublk.size ; Get the size of the real block.
+ lda.q (sp+25), y;
+ sta.q sp+17 ; Save the size of the real block.
+ clc ; Prepare for a non carrying add.
+ adc.q sp+25 ; Add the size of the real block with the start of the real block.
+ cmp.q heapptr ; Is this block on top of the heap?
+ bne @heapadd ; No, so add it to the free block list.
+@dectop:
+ lda.q sp+25 ; Get the block.
+ sta.q heapptr ; Set the top of the heap to it.
+@chklastblk:
+ lda.q heapl ; Get the last free list entry.
+ sta.q sp+17 ; Copy it into the second local variable.
+ ora.d sp+21 ; Is the free list empty?
+ beq @end ; Yes, so we're done.
+ lda.q sp+17 ; No, so Get the last free list entry.
+ ldy #fblk.size ; Get the size of the block.
+ lda.q (sp+17), y;
+ clc ; Prepare for a non carrying add.
+ adc.q sp+17 ; Add the size of the block, with the address of the block entry.
+ cmp.q heapptr ; Is the last block on top of the heap?
+ bne @end ; No, so we're done.
+@delblk:
+ lda.q sp+17 ; Yes, so remove the last block.
+ sta.q heapptr ;
+@correctblk:
+ ldy #fblk.prev ; Get the previous block.
+ lda.q (sp+17), y;
+ sta.q sp+25 ; Save the previous block for now.
+ sta.q heapl ; Set the last block to the previous block.
+ ora.d sp+29 ; Is the previous block non NULL?
+ bne @delnxtblk ; Yes, so delete the next block.
+ sta.q heapf ; No, so empty the free list.
+ bra @end ; We are done.
+@delnxtblk:
+ and #0 ; Reset A.
+ ldy #fblk.next ; Delete the next block.
+ sta.q (sp+25), y;
+@end:
+ pla.q ; Clean up the stack frame.
+ pla.q ;
+ pla.q ;
+ pla.q ;
+ ply.q ; Restore Y.
+ plb.q ; Restore B.
+ pla.q ; Restore A.
+ rts ; End of free.
+
+
+@heapadd:
+ lda.q heapf ; Get the first block.
+ sta.q sp+9 ; Copy it into the third local variable.
+ ora.d sp+13 ; Is the free list empty?
+ bne @srchflst ; No, so start searching the free list.
+@empty:
+ ldy #fblk.next ; Clear the next block.
+ sta.q (sp+25), y;
+ ldy #fblk.prev ; Clear the previous block.
+ sta.q (sp+25), y;
+ lda.q sp+25 ; Reset A.
+ sta.q heapf ; Clear the first block entry.
+ sta.q heapf ; Clear the last block entry.
+ bra @end ; We are done.
+@srchflst:
+ and #0 ; Reset A.
+ sta.q sp+1 ; Reset the left pointer.
+ ldy #fblk.next ; Setup for the loop.
+@loop:
+ lda.q sp+9 ; Get the right pointer.
+ cmp.q sp+25 ; Is the right pointer at, or below the current block?
+ beq @nextright ; Yes, so get the next right pointer.
+ bcs @chkrmerge ; No, so do the right block merge.
+@nextright:
+ sta.q sp+1 ; Set the left pointer, to the right pointer.
+ lda.q (sp+9), y ; Get the next right pointer.
+ sta.q sp+9 ; Set the current right pointer to the next right pointer.
+ ora.d sp+13 ; Is the next right pointer NULL?
+ bne @loop ; No, so keep looping.
+@st_lmerge2:
+ sta.q (sp+25), y; Clear the next block.
+ lda.q sp+25 ; Get the current block.
+ sta.q heapl ; Set the last free block entry to it.
+ bra @chklmerge2 ; Do the left block merge.
+@chkrmerge:
+ lda.q sp+25 ; Get the current block.
+ clc ; Prepare for a non carrying add.
+ adc.q sp+17 ; Add the size of the current block, to the current block.
+ cmp.q sp+9 ; Is the right pointer NULL?
+ bne @normerge ; No, so don't merge the right block.
+@rmerge:
+ ldy #fblk.size : Get the size of the right pointer.
+ lda.q sp+17 ; Get the size of the current block.
+ clc ; Prepare for a non carrying add.
+ adc.q (sp+9), y ; Add the size of the current block, with the size of the right pointer.
+ sta.q (sp+25), y; Set the size of the current block, to the new size.
+@rmerge2:
+ ldy #fblk.next ; Get the next right pointer.
+ lda.q (sp+9), y ;
+ sta.q (sp+25), y; Set the next block, to the next right pointer.
+ sta.q sp+17 ; Save the next block in the second local variable.
+ ora.d sp+21 ; Is the next block NULL?
+ beq @setheapl ; Yes, so set the last block.
+@setprev:
+ lda.q sp+25 ; Get the current block.
+ ldy #fblk.prev ; Set the previous block to the current block.
+ sta.q (sp+17), y;
+ bra @chklmerge ; Do the left block merge.
+@setheapl:
+ lda.q sp+25 ; Get the current block.
+ sta.q heapl ; Set the last block to the current block.
+ bra @chklmerge ; Do the left block merge.
+@normerge:
+ lda.q sp+9 ; Get the right pointer.
+ ldy #fblk.next ; Set the next block to the right pointer.
+ sta.q (sp+25), y;
+ lda.q sp+25 ; Get the current block.
+ ldy #fblk.prev ; Set the previous right pointer to the current block.
+ lda.q (sp+9), y ;
+@chklmerge:
+ lda.q sp+1 ; Get the left pointer.
+ ora.d sp+5 ; Is the left pointer NULL?
+ bne @chklmerge2 ; No, so keep checking.
+@newstart:
+ ldy #fblk.prev ; Clear the previous block.
+ sta.q (sp+25), y;
+ lda.q sp+25 ; Get the current block.
+ sta.q heapf ; Set the first block, to the current block.
+ bra @end2 ; We are done.
+@chklmerge2:
+ ldy #fblk.size ; Get the size of the left block.
+ lda.q (sp+1), y ;
+ clc ; Prepare for a non carrying add.
+ adc.q sp+1 ; Add the size of the left block, to the left pointer.
+ cmp.q sp+25 ; Is the left block adjacent?
+ bne @nolmerge ; No, so don't merge the left block.
+@lmerge:
+ lda.q (sp+1), y ; Get the size of the left block.
+ clc ; Prepare for a non carrying add.
+ ldb.q (sp+25), y; Get the size of the current block.
+ adc b ; Add the size of the left block, with the size of the current block.
+ sta.q (sp+1), y ; Set the size of the left block to the new size.
+@lmerge2:
+ ldy #fblk.next ; Get the next block.
+ lda.q (sp+25), y;
+ sta.q (sp+1), y ; Set the next left pointer, to the next block.
+ sta.q sp+17 ; Set the second local variable to the next block.
+ ora.d sp+21 ; Is the next left pointer NULL?
+ beq @newlast ; Yes, so set the last block.
+@lprev:
+ lda.q sp+1 ; Get the left pointer.
+ ldy #fblk.prev ; Set the next left pointer's previous pointer to the left pointer.
+ sta.q (sp+17), y;
+ bra @end2 ; We are done.
+@newlast:
+ lda.q sp+1 ; Get the left pointer.
+ sta.q heapl ; Set the last block, to the left pointer.
+ bra @end2 ; We are done.
+@nolmerge:
+ lda.q sp+25 ; Get the current block.
+ ldy #fblk.next ; Set the next left pointer, to the current block.
+ sta.q (sp+1), y ;
+@nolmerge2:
+ lda.q sp+1 ; Get the left pointer.
+ ldy #fblk.prev ; Set the previous block, to the left pointer.
+ sta.q (sp+25), y;
+@end2:
+ pla.q ; Clean up the stack frame.
+ pla.q ;
+ pla.q ;
+ pla.q ;
+ ply.q ; Restore Y.
+ plb.q ; Restore B.
+ pla.q ; Restore A.
+ rts ; End of free.