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
14 changed files with 394 additions and 35 deletions
+25
View File
@@ -0,0 +1,25 @@
# Contributing
## Code of Conduct
Be nice, we are all just doing this to have fun
## General rules
- All text (names, comments, etc.) has to be in English
- You are responsible for ensuring that you have the rights for us to use the code you contribute to the project
- follow the guidelines, for code, documentation, etc.
- all code has to work with the standard symphony ISA
## Documenting Functions
All functions in the standard library should follow the following outline:
```
; <description>
; Arguments: <which register contains what argument>
; Result: <what is the result, and where is it stored>
; Clobbers: <list of registers that are clobbered>
<label>: <;SHOULD BE INLINED>
<CODE>
```
Functions should be in the appropriate asm file, if you are unsure where functionality fits make a seperate file and ask in the pull request
Functions that are provided for convenience/reference but should be inlined in production code should be marked with `;SHOULD BE INLINED` after their label
+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.
+46 -2
View File
@@ -1,3 +1,47 @@
# symphony_stdlib # Symphony Stdlib
standard library for symphony 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).
If you are intersted in contributing have a look at [[CONTRIBUTING.md]]
---
## ABI
### Calling Convention
| class | registers |
| ----- | --------- |
| n.a. | zr |
| preserved | sp, r8 - r12 |
| scratch | flags, r1 - r7 |
| arguments | r1 - r7 |
| result | r1-r7 |
| return address | r13 |
Arguments not fitting into the 7 registers should be passed on the top of the stack, meaning they should be the last values pushed before the function call.
Arguments are passed in reverse order with the stack so:
lowest address = 1st stack arg
highest address = last stack arg
Should a function return more values than fit into the 7 registers, the caller has to allocate space on the stack for them, and pass the pointer to that space in the next free argument register.
This reduces the amount of argument registers to 6 and all arguments above that shall go on the stack, the pointer to the result stack shall **always** be passed in an argument register.
This register points at the highest available address for results, with the 8th result being stored there, the 9th below it and so on.
### Stack
Grows downwards from 0xXXFE_0000 (so top of memory -0x1_0000).
### Types
#### String
Strings are stored in memory as null terminated sequences of bytes encoding ascii characters.
They should be passed by reference.
#### Array
Arrays are stored in memory with a reference to them being the tuple (pointer, length) stored in a register pair.
Array elements may only have a size of 8/16/32 bits
+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
+100
View File
@@ -0,0 +1,100 @@
@0x10000 ; Example address until we get a proper memory map for this
; Shift conversion table
; It stores the mapping from value 32-127 of the ASCII table to their shifted equivalents (both ways) in the standard US keyboard layout
; e.g. 1 -> !
U8 32 ; Space -> Space
U8 49 ; ! -> 1
U8 39 ; " -> '
U8 51 ; # -> 3
U8 52 ; $ -> 4
U8 53 ; % -> 5
U8 55 ; & -> 7
U8 34 ; ' -> "
U8 57 ; ( -> 9
U8 48 ; ) -> 0
U8 56 ; * -> 8
U8 61 ; + -> =
U8 60 ; , -> <
U8 95 ; - -> _
U8 62 ; . -> >
U8 63 ; / -> ?
U8 41 ; 0 -> )
U8 33 ; 1 -> !
U8 64 ; 2 -> @
U8 35 ; 3 -> #
U8 36 ; 4 -> $
U8 37 ; 5 -> %
U8 94 ; 6 -> ^
U8 38 ; 7 -> &
U8 42 ; 8 -> *
U8 40 ; 9 -> (
U8 59 ; : -> ;
U8 58 ; ; -> :
U8 44 ; < -> ,
U8 43 ; = -> +
U8 46 ; > -> .
U8 47 ; ? -> /
U8 50 ; @ -> 2
U8 97 ; A -> a
U8 98 ; B -> b
U8 99 ; C -> c
U8 100; D -> d
U8 101; E -> e
U8 102; F -> f
U8 103; G -> g
U8 104; H -> h
U8 105; I -> i
U8 106; J -> j
U8 107; K -> k
U8 108; L -> l
U8 109; M -> m
U8 110; N -> n
U8 111; O -> o
U8 112; P -> p
U8 113; Q -> q
U8 114; R -> r
U8 115; S -> s
U8 116; T -> t
U8 117; U -> u
U8 118; V -> v
U8 119; W -> w
U8 120; X -> x
U8 121; Y -> y
U8 122; Z -> z
U8 123; [ -> {
U8 124; \ -> |
U8 125; ] -> }
U8 125; ^ -> 6
U8 45 ; _ -> -
U8 126; ` -> ~
U8 65 ; a -> A
U8 66 ; b -> B
U8 67 ; c -> C
U8 68 ; d -> D
U8 69 ; e -> E
U8 70 ; f -> F
U8 71 ; g -> G
U8 72 ; h -> H
U8 73 ; i -> I
U8 74 ; j -> J
U8 75 ; k -> K
U8 76 ; l -> L
U8 77 ; m -> M
U8 78 ; n -> N
U8 79 ; o -> O
U8 80 ; p -> P
U8 81 ; q -> Q
U8 82 ; r -> R
U8 83 ; s -> S
U8 84 ; t -> T
U8 85 ; u -> U
U8 86 ; v -> V
U8 87 ; w -> W
U8 88 ; x -> X
U8 89 ; y -> Y
U8 90 ; z -> Z
U8 91 ; { -> [
U8 92 ; | -> \
U8 93 ; } -> ]
U8 96 ; ~ -> `
+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
+69
View File
@@ -0,0 +1,69 @@
; Reads a line from the keyboard and fills the specified buffer with it
; Does not support Shift or any other special keys
; Arguments:
; r1 - pointer to the buffer
; Result:
; r1 - pointer to the same buffer
; Clobbers: r2, r3, r4, r5, r6, r7
pub read_line:
mov r6, 1
lsl r6, r6, 16
sub r6, r6, 32 ; Calculating the address to the shift LUT
mov r2, 0 ; Storing the shift status here
mov r4, 0 ; Storing the last key here, so we don't repeat the same key
mov r3, r1 ; The pointer to after the last character
read_line_keyloop:
keyboard r5
cmp r5, r4
je read_line_keyloop ; If the current key is same as previous, we loop
mov r4, r5 ; Storing current key as previous
cmp r5, 0x120 ; Is the key renderable or special?
jb read_line_special ; If the key was special, we handle it separately
xor r5, r5, 0x100 ; Clearing the "down" bit
cmp r2, zr ; Checking for shift status
je read_line_store ; If shift is up, we skip conversion
;;; converting from shift-down to shift-up keys
add r7, r6, r5 ; Calculating the index of the shift conversion
load_8 r5, [r7] ; Loading the shifted value
;;;
read_line_store:
store_8 [r3], r5 ; Else, we store the key in the buffer
add r3, r3, 1 ; We advance forward
; TODO: Writeback
jmp read_line_keyloop
read_line_special:
cmp r5, 13 ; Was the key Backspace?
je read_line_backspace ; If yes we need to move one character back
cmp r5, 10 ; Was the key Enter?
je read_line_finished ; If so, we're finished
and r5, r5, 0x1FB ; Mask out the left/right shift direction bit
cmp r5, 0x110 ; Was the key Shift Down?
and flags, flags, 0x1 ; We care only about equality bit
or r2, r2, flags ; If shift was down before, it still is. If it was pressed now, it is down now
cmp r5, 0x010 ; Was the key Shift Up?
and flags, flags, 0x1 ; We care only about equality bit
xor flags, flags, 0x1 ; We invert it, i.e. "if it's not up"
and r2, r2, flags ; The shift can be kept down if it's not currently up
jmp read_line_keyloop ; If no special handling, we loop back
read_line_backspace:
cmp r3, r1 ; Compare the current pointer to start of buffer
je read_line_keyloop ; If we are at the start, we loop
sub r3, r3, 1 ; We move back one character
; TODO: Writeback
jmp read_line_keyloop
read_line_finished:
store_8 [r3], zr ; We store null at the end so the string is finished
jmp r13
+29 -8
View File
@@ -1,8 +1,15 @@
; Multiplies r1 and r2, returning the lower part of the result
; Arguments:
; r1 - The first value
; r2 - The second value
; Result:
; r1 - The lower 32 bits of the result
; Clobbers: r2, r3, r4, r5
pub mul_low: pub mul_low:
mov r3, 0 ; result mov r3, 0 ; result
mov r4, 31 ; loop counter mov r4, 31 ; loop counter
mull_loop: mul_low_loop:
asr r5, r2, 31 asr r5, r2, 31
and r5, r5, r1 and r5, r5, r1
lsl r5, r5, r4 lsl r5, r5, r4
@@ -10,14 +17,18 @@ pub mul_low:
lsl r2, r2, 1 lsl r2, r2, 1
sub r4, r4, 1 sub r4, r4, 1
cmp r4, 0 cmp r4, 0
jge mull_loop jge mul_low_loop
mov r1, r3 mov r1, r3
jmp r13 jmp r13
; Calculates the absolute value of the value provided in the r1 register ; Calculates the absolute value of the value provided in the r1 register
; Based on Stanford's BitHacks ; Arguments:
; Clobbers r2 ; r1 - The value for which we want the absolute value
; Result:
; r1 - The calculated absolute value
; Clobbers: r2
; Info: Based on Stanford's BitHacks
pub abs: ; SHOULD BE INLINED pub abs: ; SHOULD BE INLINED
; mask = v >> 31 ; mask = v >> 31
asr r2, r1, 31 asr r2, r1, 31
@@ -28,8 +39,13 @@ pub abs: ; SHOULD BE INLINED
jmp r13 jmp r13
; Calculates the minimum value of the two values provided in the r1 and r2 registers ; Calculates the minimum value of the two values provided in the r1 and r2 registers
; Based on Stanford's BitHacks ; Arguments:
; Clobbers flags ; r1 - The first value
; r2 - The second value
; Result:
; r1 - The smaller value
; Clobbers: Nothing
; Info: Based on Stanford's BitHacks
pub min: ; SHOULD BE INLINED pub min: ; SHOULD BE INLINED
; x < y ; x < y
cmp r1, r2 cmp r1, r2
@@ -45,8 +61,13 @@ pub min: ; SHOULD BE INLINED
jmp r13 jmp r13
; Calculates the maximum value of the two values provided in the r1 and r2 registers ; Calculates the maximum value of the two values provided in the r1 and r2 registers
; Based on Stanford's BitHacks ; Arguments:
; Clobbers r2 and flags ; r1 - The first value
; r2 - The second value
; Result:
; r1 - The smaller value
; Clobbers: r2
; Info: Based on Stanford's BitHacks
pub max: ; SHOULD BE INLINED pub max: ; SHOULD BE INLINED
; x < y ; x < y
cmp r1, r2 cmp r1, r2
+6
View File
@@ -0,0 +1,6 @@
pub include bit
pub include imath
pub include console
; Needs to be last!
pub include LUTs
-25
View File
@@ -1,25 +0,0 @@
; ===== INTRODUCTION =====
; This is supposed to provide some standard library functionality for stock symphony.
; In particular its supposed to work with an unmodified ISA, that means some choices are not
; optimal (RA being stored in flags for example)
; ===== ABI =====
; ----- CALLING CONVENTION -----
; n.a. zr
; preserved: sp, r8 - r12
; scratch: flags, r1 - r7
; arguments: r1 - r7 (r1 = 1st argument, r6 = 6th arg/stack args, r7 = 7th arg/stack res)
; result: r1, r2 (r1 = low word, r2 = high word)
; return address: r13
; ----- STACK -----
; grows downwards from top of memory
; arguments are passed in reverse order with the stack so:
; lowest address = 1st stack arg
; highest address = last stack arg
; ===== TYPES =====
pub include bit
pub include imath
+5
View File
@@ -0,0 +1,5 @@
# Teaching
This is a collection of teaching advice regarding the stdlib.
**WIP**
+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.