45 Commits
Author SHA1 Message Date
ShatteredMINT b7621187e6 mention globabls.asm 2026-09-03 20:40:37 +02:00
ShatteredMINT 560855f6ac fix path to stdlib.asm 2026-09-03 20:40:37 +02:00
ShatteredMINT 0ad8cf639c add examples directory 2026-09-03 20:40:29 +02:00
ShatteredMINT 51c7869ce4 add tests folder 2026-09-03 20:40:29 +02:00
ShatteredMINT 443623c945 move assembly files into src directory 2026-09-03 20:40:29 +02:00
Michał Isalski a91895b8ac Fixed at-address 2026-09-03 20:40:29 +02:00
Michal Isalski ef80825f40 Added a shift conversion LUT and finished read_line (except Writeback) 2026-09-03 20:40:29 +02:00
Michał Isalski d0afb22568 Added storing shift status 2026-09-03 20:40:23 +02:00
Michal Isalski a4ecadfad5 Made read_line compliant to new doc guidelines 2026-09-03 20:40:23 +02:00
Michal Isalski 6e5d0acfeb One instruction less by using another register 2026-09-03 20:40:23 +02:00
Michal Isalski 1508c22fc7 Added handling for skipping non-renderable characters
Added handling for backspace character
2026-09-03 20:40:23 +02:00
Michał Isalski 2b85e5a851 Added read_line function 2026-09-03 20:40:23 +02:00
ShatteredMINT cbdcec77b5 clean up confusion about function template 2026-09-03 20:40:19 +02:00
Michał Isalski 6686bc54fc Changed docs of math functions to conform to new guidelines 2026-09-03 20:40:19 +02:00
Michal Isalski eb9dbfb8ad Fixed swapped pop instructions 2026-09-03 20:40:19 +02:00
Michal Isalski 75da3d730f Made find_index conform to new doc guidelines 2026-09-03 20:40:19 +02:00
Michał Isalski 267dcba57d pleegwat's code review fixes 2026-09-03 20:40:19 +02:00
Michał Isalski 0894cdca40 Added comment about predicate context 2026-09-03 20:40:19 +02:00
Michał Isalski a3f74a1f05 Tested and fixed stride->shift conversion missing 2026-09-03 20:40:19 +02:00
Michal Isalski f91c56deb1 Reduced operations to get -1 in register 2026-09-03 20:40:19 +02:00
Michal Isalski 63e9d43440 Fixed the predicate return address 2026-09-03 20:40:19 +02:00
Michal Isalski 8a463207ae Added find_index array function 2026-09-03 20:40:19 +02:00
Michał Isalski 98ecc3619c Added comment about predicate context 2026-09-03 20:39:59 +02:00
Michał Isalski 6be5e520bc Tested and fixed stride->shift conversion missing 2026-09-03 20:39:59 +02:00
Michal Isalski bb4095b8fc Reduced operations to get -1 in register 2026-09-03 20:39:59 +02:00
Michal Isalski bda60bd486 Fixed the predicate return address 2026-09-03 20:39:59 +02:00
Michal Isalski 46e8fad974 Added find_index array function 2026-09-03 20:39:59 +02:00
ShatteredMINT 993bbb797d mention globabls.asm 2026-09-02 11:41:27 +02:00
ShatteredMINT 7a9f2da1c0 create teaching directory 2026-09-02 11:40:39 +02:00
ShatteredMINT db8462a2fe change stack start 2026-09-02 11:40:39 +02:00
ShatteredMINT aa5cc1dbe0 remove mention of non existent file 2026-09-02 11:40:39 +02:00
ShatteredMINT f7d3ca2ec5 relax r7 requirement for result stack 2026-09-02 11:40:39 +02:00
ShatteredMINT da811fd65d remove duplicate documentation from stdlib.asm 2026-09-02 11:40:39 +02:00
ShatteredMINT 49767b61df clarify stack arguments 2026-09-02 11:40:39 +02:00
ShatteredMINT 6eb91f3433 explain inline comment 2026-09-02 11:40:39 +02:00
ShatteredMINT 9220e3dc28 basic contribution guidelines 2026-09-02 11:40:39 +02:00
ShatteredMINT 25ecd155e1 add arrays to readme 2026-09-02 11:40:39 +02:00
ShatteredMINT c071a00746 format calling convention 2026-09-02 11:40:39 +02:00
ShatteredMINT 5400da0157 fix remaining links 2026-09-02 11:40:39 +02:00
ShatteredMINT f19dfd3611 link test 2026-09-02 11:40:39 +02:00
ShatteredMINT a9fa16a241 start readme 2026-09-02 11:40:39 +02:00
ShatteredMINT de9ac7fe72 create globals.asm with placeholder globals 2026-09-02 11:37:26 +02:00
ShatteredMINT 1fc4515f6b include LUTs in memory map 2026-09-02 11:15:43 +02:00
ShatteredMINT f4ae43bbc1 update memory overview 2026-09-02 11:13:48 +02:00
ShatteredMINT f27715b5b3 start memory documentation 2026-09-01 13:45:23 +02:00
13 changed files with 115 additions and 252 deletions
+21
View File
@@ -0,0 +1,21 @@
# Memory Map
To not have to allocate a ton of things at run time the Standard Library uses a static memory map for some purposes
## Overview
| Start | Use |
| ---- | ---- |
| `0x0` | Reset Vector |
| `0x16` | Zero Page |
| `0x100` | User Code |
| `?` | Library Code |
| `0x1_0000` | LUTs |
| `?` | heap |
| `0xXXF0_0000` | Stack |
| `0xXXFF_0000` | quick access |
## Zero Page
Some values are needed not often enough to get their own special register, but often enough that it makes sense to keep them at a quickly accessible location.
This is what the zero page is for. its position in memory allows us to load them into a register with a single instruction.
+1
View File
@@ -4,6 +4,7 @@ This is a standard library for symphony.
It is both intended as a practical toolkit to develop more complex software as well as a teaching resource.
If you just want to use the standard library [[stdlib.asm]] is your main header, include it after your code.
You also need to include [[globals.asm]] as the first line in your assembly file.
If you are using it as a learning resource have a look at the [teaching folder](teaching).
+3
View File
@@ -0,0 +1,3 @@
# Examples
Examples of how to use the standard library to accomplish a task.
+10
View File
@@ -0,0 +1,10 @@
jmp 0x100
@0x10
screen:
frambuffer_ptr: U32 0x0
size: U32 0x0
position_xy: U32 0x0
mode: U32 0x0
@0x100
-174
View File
@@ -1,174 +0,0 @@
; int compare(uint8_t* a, uint8_t* b, size_t count);
; Compares two memory segment of equal length lexicographically.
;
; Arguments:
; - `r1`: A pointer to the first memory segment.
; - `r2`: A pointer to the second memory segment.
; - `r3`: The size of both memory segments.
; Results:
; - `r1`:
; - `0` if both segments are equal.
; - `<0` if the first segment is less than the second segment.
; - `>0` if the first segment is greater than the second segment.
;
pub compare:
; Exclusive end point of the first segment.
add r3, r3, r1
sub r3, r3, 4
_compare__loop:
load_32 r4, [r1]
add r1, r1, 4
load_32 r5, [r2]
add r2, r2, 4
; Comparing two sequences of 4 bytes lexicographically is equivalent to
; comparing the corresponding big endian 32 bit words.
cmp r4, r5
jne _compare__break
; Check if there are enough bytes left to continue with the vectorized loop.
cmp r1, r3
jbe _compare__loop
; `r3 + 4 - r1 = <remaining byte count> = r3 - r1 mod 4`
sub flags, r3, r1
; Check if one of the lowest 2 bits is non-zero
jbe _compare__rem
; If not, we are done. Both segments are equal.
mov r1, 0
jmp r13
_compare__break:
; `flags` is the comparison result in the format of `cmp`. Convert it to the desired format.
; 00 => 0x40000000 > 0
; 01 => 0x00000000 = 0
; 10 => 0xC0000000 < 0
xor r1, flags, 1
lsl r1, r1, 30
jmp r13
_compare__rem:
; Compute `S = 8*(4 - <remaining byte count>)` and
; [r1] >> S, [r2] >> S
mov r3, 8
load_32 r4, [r1]
sub r3, r3, flags
load_32 r5, [r2]
lsl r3, r3, 3
lsr r4, r4, r3
lsr r5, r5, r3
; Compare both values, now with garbage bytes removed.
cmp r4, r5
jmp _compare__break
; void copy(void* src, void* dest, size_t count);
; Copies `count` bytes from `src` to `dest`. The two memory segments must not overlap.
;
; Arguments:
; - `r1`: Pointer to the memory segment to be copied.
; - `r2`: Pointer to the memory segment to be copied into.
; - `r3`: Byte size of both the `src` and `dest` segments.
;
pub copy:
; Exclusive end point of the source segment.
add r3, r1, r3
; Last index from where we can safely copy 8 bytes per loop iteration.
sub r3, r3, 8
jmp _copy__loop_entry
_copy__loop:
; Copy 8 bytes from `src` to `dest`.
load_32 flags, [r1]
add r1, r1, 4
store_32 [r2], flags
add r2, r2, 4
load_32 flags, [r1]
add r1, r1, 4
store_32 [r2], flags
add r2, r2, 4
_copy__loop_entry:
; Check if we can process more data in the vectorized loop.
cmp r1, r3
jbe _copy__loop
; The remaining amount of bytes `R` is `R = r3 + 8 - r1 = r3 - r1 mod 8`.
sub flags, r3, r1
; Test if `R` is not a multiple of `4`, i.e. the lowest 2 bits are non-zero.
jbe _copy__rem
; `R` is a multiple of `4`. Special case this.
; Check if `R` is `0`, i.e. the third bit is also 0. In that case, we are already done.
; There are no conditional indirect jumps, so we can't return immediately.
jge _copy__ret
; `R = 4`. No need to update `r1` or `r2`, we don't need them anymore.
load_32 flags, [r1]
store_32 [r2], flags
_copy__ret:
; Return
jmp r13
_copy__rem:
; Optimize the remaining cases for code size.
; End point of the source segment.
add r3, r3, 8
; We already handled the case `R = 0` earlier,
; so no bounds check needed for the first iteration.
_copy__rem_loop:
; Copy 1 byte.
load_8 flags, [r1]
add r1, r1, 1
store_8 [r2], flags
add r2, r2, 1
; Check if we are still within the bounds.
cmp r1, r3
jb _copy__rem_loop
jmp r13
; void fill32(uint8_t* dest, size_t count, uint32_t value);
; Fills `count` bytes in `dest` with `value`. If `count` is not a multiple of 4,
; the least significant bytes of `value` are cut off for the last entry.
;
; Arguments:
; - `r1`: A pointer to the destination segment.
; - `r2`: The size of the destination segment.
; - `r3`: The 32 bit value that the segment is filled with.
;
pub fill32:
; Exclusive end point of the destination segment.
add r2, r1, r2
; Last index from where we can safely write 8 bytes per loop iteration.
sub r2, r2, 8
jmp _fill32__entry
_fill32__loop:
; Set 8 bytes per loop iteraion.
store_32 [r1], r3
add r1, r1, 4
store_32 [r1], r3
add r1, r1, 4
_fill32__entry:
; Check if we can process more data in the vectorized loop.
cmp r1, r2
jbe _fill32__loop
; The remaining amount of bytes `R` is `R = r2 + 8 - r1 = r2 - r1 mod 8`.
sub flags, r2, r1
; Check if the third bit of the remainder is cleared.
jge _fill32__r4
; Otherwise set 4 bytes.
store_32 [r1], r3
add r1, r1, 4
_fill32__r4:
; Check if the two least significant bits of the remainder are zero.
ja _fill32__ret
; Handle the remaining bytes `R` individually, in reverse order.
add r2, r2, 4
; `r1 + 4 - r2 = 4 - R`.
sub flags, r1, r2
; Exclusive end point of the destination segment.
add r2, r2, 4
; Shift out the least significant `8*(4 - R)` bits of the value.
lsl flags, flags, 3
lsr r3, r3, flags
jmp _fill32__loop2_entry
_fill32__loop2:
sub r2, r2, 1
; Write the least significant byte of the value...
store_8 [r2], r3
; and then shift it out.
lsr r3, r3, 8
_fill32__loop2_entry:
cmp r1, r2
jb _fill32__loop2
_fill32__ret:
jmp r13
View File
+77
View File
@@ -0,0 +1,77 @@
; Returns the index of the first element matching the provided predicate function (or -1 if not found)
; Arguments:
; r1 - The array pointer
; r2 - The array length (number of items)
; r3 - The stride (size of one item) - either 1, 2 or 4 (bytes)
; r4 - The predicate
; r5 - Predicate context
; Result:
; r1 - The index of the first element matching the provided predicate function (or -1 if not found)
; Clobbers: r2, r3, r4, r5, r6, + what the predicate clobbers
; Info:
; The predicate function should follow the stdlib calling convention
; The predicate receives two arguments (the value and the predicate context) and should return either a zero when the value is not the one we search for
; , or any other result if it is the searched-for item.
pub find_index:
push r12 ; We will store the predicate pointer here
push r11 ; We will store the current pointer here
push r10 ; We will store the stride here
push r9 ; We will store the final address here
push r8 ; We will store the mask here
mov r12, r4
mov r11, r1
mov r10, r3
mov r9, r2
lsr r6, r3, 1 ; We turn the stride into a byte shift
lsl r9, r9, r6 ; We calculate bytes left
add r9, r9, r1 ; We add the start address to get the final address
push r13 ; We save up the return address because we will provide our own to the predicate
push r1 ; We need the array pointer to calculate the item index
counter r13
add r13, r13, 52 ; Point to just after the predicate call - we can set this up now so we don't waste loop cycles
nand r8, zr, zr ; We create a mask of 0xFFFFFFFF
mov r6, 4
sub r6, r6, r3 ; We create a "negative stride", e.g. 4 -> 0, 2 -> 2, 1 -> 3
lsl r6, r6, 3
lsr r8, r8, r6 ; We shift the mask by the negative stride to obtain the proper mask for a value
; e.g. stride 4 -> mask is 0xFFFFFFFF
; stride 2 -> mask is 0x0000FFFF
; stride 1 -> mask is 0x000000FF
push r5 ; We save the predicate context on the stack
find_index_loop:
load_32 r1, [r11] ; We load the element
and r1, r1, r8 ; We mask it to handle stride 2 and 1 cases
load_32 r2, [sp] ; We load the predicate context into r2
jmp r12 ; We call the predicate
cmp r1, zr
jne find_index_found_item ; If we found the item, we jump out
; If we didn't, move to next item
add r11, r11, r10 ; We add the stride to the pointer
cmp r11, r9 ; We compare with the final address
jne find_index_loop ; If we did not reach the end we jump back into the loop
find_index_not_found:
add sp, sp, 8 ; The predicate context and old array pointer are not useful
pop r13 ; We get our return address
nand r1, zr, zr ; We put -1 in r1
jmp find_index_postamble
find_index_found_item:
add sp, sp, 4 ; The predicate context is not useful
pop r1 ; We get the array pointer
pop r13 ; We get our return address
sub r1, r11, r1 ; We calculate the bytes from the start
lsr r10, r10, 1 ; We shift the stride to get the amount to shift the bytes for
lsr r1, r1, r10 ; We shift to get the index of the item
find_index_postamble:
pop r8
pop r9
pop r10
pop r11
pop r12
jmp r13 ; Return
View File
View File
View File
-1
View File
@@ -1,6 +1,5 @@
pub include bit
pub include imath
pub include mem
pub include console
; Needs to be last!
+3
View File
@@ -0,0 +1,3 @@
# Tests
Tests for the standard library go here, tests are allowed to depend on the recommended spec.isa changes.
-77
View File
@@ -1,77 +0,0 @@
; int compare(uint8_t* a, uint8_t* b, size_t count);
; Compares two memory segment of equal length lexicographically.
; Temporarily modifies the byte at address `a + count`.
;
; Arguments:
; - `r1`: A pointer to the first memory segment.
; - `r2`: A pointer to the second memory segment.
; - `r3`: The size of both memory segments.
; Results:
; - `r1`:
; - `0` if both segments are equal.
; - `<0` if the first segment is less than the second segment.
; - `>0` if the first segment is greater than the second segment.
pub compare:
cmp r1, r2
je _compare__is_eq
; Exclusive end point of the second segment.
add r4, r3, r2
; Exclusive end point of the first segment.
add r3, r3, r1
load_8 r6, [r3]
load_8 flags, [r4]
; Check if the first bytes behind the sequences are equal.
cmp flags, r6
jne _compare__loop
; Change the byte directly behind the first segment.
xor r4, r6, 1
; This would be problematic if someone calls compare with a first segment
; whose end point overlaps the program memory of this function.
store_8 [r3], r4
_compare__loop:
load_32 r4, [r1]
add r1, r1, 4
load_32 r5, [r2]
add r2, r2, 4
; Comparing two sequences of 4 bytes lexicographically is equivalent to
; comparing the corresponding big endian 32 bit words.
cmp r4, r5
je _compare__loop
; We overshot in the loop; decrement r1 again. (Only by 2, we backtrack the rest if necessary later)
sub r1, r1, 2
; Restore the byte we changed.
store_8 [r3], r6
; We encountered two different words. Figure out what byte they differ on.
xor r4, r4, r5
; Store the flags for later, to figure out the return value.
mov r5, flags
; Check if at least one of the two most significant bytes is not 0.
cmp r4, 0xffff
jbe _compare__low2
; If it is, backtrack the remaining 2 indices.
; Shift the most significant bytes to the least significant ones.
sub r1, r1, 2
lsr r4, r4, 16
_compare__low2:
; r1 now points to a non-zero 16 bit value.
; If the 16 bit value at r1-2 is in-bounds, then it is 0.
; Check if the most significant byte of the 16 bit value is 0.
cmp r4, 0xff
ja _compare__low1
; If it is, our target is the least significant byte.
add r1, r1, 1
_compare__low1:
; Otherwise, the target is that non-zero byte.
; Check if the target is out of bounds, i.e. the loop terminated through the "bounds check".
cmp r1, r3
jae _compare__is_eq
; r5 is the comparison result in the format of `cmp`. Convert it to the desired format.
; 00 => 0x40000000 > 0
; 01 => 0x00000000 = 0
; 10 => 0xC0000000 < 0
xor r1, r5, 1
lsl r1, r1, 30
jmp r13
_compare__is_eq:
mov r1, 0
jmp r13