26 Commits
Author SHA1 Message Date
PleegWat 3968179026 Optimize digit parse loops 2026-09-04 23:07:21 +02:00
PleegWat f18199c5d0 Only allow 0o prefix for octal, not just 0 2026-09-03 16:45:04 +02:00
PleegWat b4af75a18e Handle negative in radix-specific preludes 2026-09-03 14:31:54 +02:00
PleegWat 836b977fc9 Implement negative number parsing 2026-09-03 13:42:11 +02:00
PleegWat 2c6e025dff Refactor tests to use data structs
Rather than a pointer to the end of the parseable string, we now store
the number of bytes we expect to be parsed. Current code allows for 11
bytes of string data which should suffice for most usecases.
2026-09-03 11:34:01 +02:00
PleegWat 9ff0a3192c Fix binary parsing and more tests 2026-09-02 21:33:58 +02:00
PleegWat fd5469aafc Add some cases, fix an octal bug 2026-09-02 21:28:29 +02:00
PleegWat e8cf5ae35f Initial version of atoi
Functions to convert strings to integers in various bases
2026-09-02 21:22:04 +02:00
ShatteredMINT 37d0e556d2 Merge pull request 'clean up confusion about function template' (#14) from meta-documentation into main
Reviewed-on: #14
2026-09-02 20:49:38 +02:00
ShatteredMINT 26d3a4d4f4 clean up confusion about function template 2026-09-02 20:49:22 +02:00
ShatteredMINT 786adfdb1c Merge pull request 'Changed docs of math functions to conform to new guidelines' (#12) from Micha_i/symphony_stdlib:math-functions-docs into main
Reviewed-on: #12
2026-09-02 11:46:14 +02:00
Michał Isalski 84636799b6 Changed docs of math functions to conform to new guidelines 2026-09-02 11:44:30 +02:00
ShatteredMINT aa2cbe4ad8 Merge pull request 'Add basic meta documentation' (#11) from meta-documentation into main
Documentation efforts are ongoing but this should be good enough for now
2026-09-02 11:39:25 +02:00
ShatteredMINT 8301d5988a create teaching directory 2026-09-02 11:32:46 +02:00
ShatteredMINT dbd4865f14 change stack start 2026-09-02 11:31:52 +02:00
ShatteredMINT 8f1b970a2f remove mention of non existent file 2026-09-02 11:31:33 +02:00
ShatteredMINT ddca02d8a8 relax r7 requirement for result stack 2026-09-02 11:21:03 +02:00
ShatteredMINT a3d6cb64b5 remove duplicate documentation from stdlib.asm 2026-09-02 11:17:18 +02:00
ShatteredMINT 9816111f38 clarify stack arguments 2026-09-01 14:08:20 +02:00
ShatteredMINT f2e5c830b3 explain inline comment 2026-09-01 13:48:12 +02:00
ShatteredMINT 60e7b6209f basic contribution guidelines 2026-09-01 13:21:17 +02:00
ShatteredMINT 33b7886e70 add arrays to readme 2026-09-01 13:07:13 +02:00
ShatteredMINT daf4168129 format calling convention 2026-08-31 10:17:25 +02:00
ShatteredMINT 4bce47209a fix remaining links 2026-08-31 10:12:57 +02:00
ShatteredMINT af5a750031 link test 2026-08-31 10:10:10 +02:00
ShatteredMINT 8e0fdf0d28 start readme 2026-08-31 10:08:44 +02:00
7 changed files with 349 additions and 34 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
+45 -2
View File
@@ -1,3 +1,46 @@
# 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.
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
+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:
mov r3, 0 ; result
mov r4, 31 ; loop counter
mull_loop:
mul_low_loop:
asr r5, r2, 31
and r5, r5, r1
lsl r5, r5, r4
@@ -10,14 +17,18 @@ pub mul_low:
lsl r2, r2, 1
sub r4, r4, 1
cmp r4, 0
jge mull_loop
jge mul_low_loop
mov r1, r3
jmp r13
; Calculates the absolute value of the value provided in the r1 register
; Based on Stanford's BitHacks
; Clobbers r2
; Arguments:
; 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
; mask = v >> 31
asr r2, r1, 31
@@ -28,8 +39,13 @@ pub abs: ; SHOULD BE INLINED
jmp r13
; Calculates the minimum value of the two values provided in the r1 and r2 registers
; Based on Stanford's BitHacks
; Clobbers flags
; Arguments:
; 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
; x < y
cmp r1, r2
@@ -45,8 +61,13 @@ pub min: ; SHOULD BE INLINED
jmp r13
; Calculates the maximum value of the two values provided in the r1 and r2 registers
; Based on Stanford's BitHacks
; Clobbers r2 and flags
; Arguments:
; 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
; x < y
cmp r1, r2
+1 -23
View File
@@ -1,25 +1,3 @@
; ===== 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
pub include strint
+188
View File
@@ -0,0 +1,188 @@
; Internal register assignments:
; r1 - Partially parsed output integer
; r2 - Parsing position
; r3 - Character being parsed
; r4 - Set to -1 if the input is negative
; Convert string to integer
; Arguments:
; r1 - Pointer to string
; Result:
; r1 - Parsed integer
; r2 - Pointer to first rejected input byte
; Clobbers:
; flags
; r3 - last character read
; r4 - Negative marker
; Note: Unless the input is "0", tail-calls into a base-specific specialization.
pub atoi:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne atoi_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
atoi_positive:
load_16 r3, [r2] ; 2-byte prefix "0b", "0o", "0x", etc.
or r3, r3, 0x20 ; 2nd char to lower case
add r2, r2, 2
cmp r3, 0x3062 ; "0b"
je atoi_bin_loop
cmp r3, 0x306F ; "0o"
je atoi_oct_loop
cmp r3, 0x3078 ; "0x"
je atoi_hex_loop
sub r2, r2, 2 ; no matching prefix, move pointer back
jmp atoi_dec_loop
; Epilogue
atoi_done:
add r1, r1, r4 ; If r4 is -1, negate r1. Else it's 0 and no effect.
xor r1, r1, r4
mov flags, 0 ; No error
jmp r13
; Convert decimal string to integer
; Arguments:
; r1 - Pointer to string
; Result:
; r1 - Parsed integer
; r2 - Pointer to first rejected input byte
; Clobbers:
; flags
; r3 - last character read
; r4 - Negative marker
pub atoi_dec:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne atoi_dec_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
atoi_dec_loop:
load_8 r3, [r2]
atoi_dec_positive:
sub r3, r3, 0x30 ; '0'
cmp r3, 9
ja atoi_done
add r2, r2, 1
lsl flags, r1, 2 ; Use flags to help multiply by 10
add r1, r1, flags
lsl r1, r1, 1
add r1, r1, r3
jmp atoi_dec_loop
; Convert binary string to integer
; Arguments:
; r1 - Pointer to string
; Result:
; r1 - Parsed integer
; r2 - Pointer to first rejected input byte
; Clobbers:
; flags
; r3 - last character read
; r4 - Negative marker
pub atoi_bin:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne atoi_bin_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
atoi_bin_positive:
load_16 r3, [r2] ; check for prefix
or r3, r3, 0x20 ; 2nd char to lower case
cmp r3, 0x3062 ; "0b"
jne atoi_bin_loop
add r2, r2, 2
atoi_bin_loop:
load_8 r3, [r2]
sub r3, r3, 0x30 ; '0'
cmp r3, 1
ja atoi_done
add r2, r2, 1
lsl r1, r1, 1
add r1, r1, r3
jmp atoi_bin_loop
; Convert octal string to integer
; Arguments:
; r1 - Pointer to string
; Result:
; r1 - Parsed integer
; r2 - Pointer to first rejected input byte
; Clobbers:
; flags
; r3 - last character read
; r4 - Negative marker
pub atoi_oct:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne atoi_oct_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
atoi_oct_positive:
load_16 r3, [r2] ; check for prefix
or r3, r3, 0x20 ; 2nd char to lower case
cmp r3, 0x306F ; "0o"
jne atoi_oct_loop
add r2, r2, 2
atoi_oct_loop:
load_8 r3, [r2]
sub r3, r3, 0x30 ; '0'
cmp r3, 7
ja atoi_done
add r2, r2, 1
lsl r1, r1, 3
add r1, r1, r3
jmp atoi_oct_loop
; Convert hexadecimal string to integer
; Arguments:
; r1 - Pointer to string
; Result:
; r1 - Parsed integer
; r2 - Pointer to first rejected input byte
; Clobbers:
; flags
; r3 - last character read
; r4 - Negative marker
pub atoi_hex:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne atoi_hex_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
atoi_hex_positive:
load_16 r3, [r2] ; check for prefix
or r3, r3, 0x20 ; 2nd char to lower case
cmp r3, 0x3078 ; "0x"
jne atoi_hex_loop
add r2, r2, 2
atoi_hex_loop:
load_8 r3, [r2]
sub r3, r3, 0x30 ; '0'
cmp r3, 10
jb atoi_hex_add
sub r3, r3, 0x11 ; 'A' - '0'
and r3, r3, 0xdf ; to lower case
cmp r3, 5 ; 0-5: 6 letters
ja atoi_done
add r3, r3, 10 ; Adjust for digits below
atoi_hex_add:
add r2, r2, 1
lsl r1, r1, 4
add r1, r1, r3
jmp atoi_hex_loop
+55
View File
@@ -0,0 +1,55 @@
; test harness for strint.atoi
; Registers:
; r8 - test address
; r9 - expected result
; r10 - actual length
; r11 - expected length
mov r8, tests
next_test:
; Set up arguments and run test
add r1, r8, 5 ; Start of test string
mov r13, atoi_test_ret
jmp stdlib.strint.atoi
atoi_test_ret:
; Load reference data
load_32 r9, [r8] ; Expected result
sub r10, r2, r8
sub r10, r10, 5 ; Actual string length
add r11, r8, 4 ; Address of expected length
load_8 r11, [r11] ; Expected length
; Verify results
cmp r1, r9
incorrect_result: jne incorrect_result
cmp r10, r11
incorrect_length: jne incorrect_length
; Next test
add r8, r8, 0x10
cmp r8, end_of_tests
jl next_test
; Done
success: jmp success
include stdlib
@0x1000
tests:
; inlen is the number of input bytes the function is expected to consume
; addr result inlen instr
@0x1000 U32 0 U8 0 "\0"
@0x1010 U32 0 U8 1 "0\0"
@0x1020 U32 1 U8 1 "1\0"
@0x1030 U32 2 U8 1 "2:\0"
@0x1040 U32 42 U8 2 "42\0"
@0x1050 U32 67 U8 2 "67lol\0"
@0x1060 U32 0x69a U8 5 "0x69a@\0"
@0x1070 U32 0x4B4 U8 5 "0x4B4g\0"
@0x1080 U32 0o23 U8 4 "0o239\0"
@0x1090 U32 0b1011 U8 6 "0b1011\0"
@0x10a0 U32 0b10001 U8 7 "0b100012\0"
@0x10b0 U32 0xFFFFFFEB U8 3 "-21\0" ; Yuk
@0x10c0
end_of_tests:
+5
View File
@@ -0,0 +1,5 @@
# Teaching
This is a collection of teaching advice regarding the stdlib.
**WIP**