Compare commits

...
73 Commits
Author SHA1 Message Date
ShatteredMINT 590732cd48 Merge pull request 'Initial version of atoi' (#15) from PleegWat/symphony_stdlib:str_to_int into main
Reviewed-on: #15
2026-09-06 16:14:57 +02:00
PleegWat 304fe54833 Apply agreed naming conventions 2026-09-06 16:13:40 +02:00
PleegWat 6f7ad3aa4b Implement atoi
Parse strings to integer (binary, octal, decimal, or hexadecimal)
2026-09-06 16:12:50 +02:00
ShatteredMINT 9b7a8685e6 Merge pull request 'Add ISA definitions for symphony' (#18) from PleegWat/symphony_stdlib:add_isa into main
Reviewed-on: #18
2026-09-06 16:08:12 +02:00
PleegWat dd20822ee1 Use all unsigned compares 2026-09-05 16:41:52 +02:00
PleegWat 4c0cd79fdd Fix several bugs 2026-09-05 16:37:51 +02:00
PleegWat 2f09320fa5 Add optimization for arbitrary aligned values
Any value with 16 or less significant bits but larger than 64k is loaded
in one fewer cycle
2026-09-05 15:39:55 +02:00
PleegWat 8e7441fdb3 Add ISA definitions for symphony
Adds both a 'stock' ISA (copied from the game) and one modified for test
code
2026-09-05 15:36:54 +02:00
ShatteredMINT 5c145dc10b Merge pull request 'Changed mul_low algorithm to radix-16-1 algorithm with swap' (#17) from Micha_i/symphony_stdlib:fast-math into main
Reviewed-on: #17
2026-09-04 23:20:14 +02:00
Micha_i ff5e1be8fd Merge branch 'main' into fast-math 2026-09-04 23:11:37 +02:00
Michał Isalski b97e17fa84 Changed mul_low algorithm to radix-16-1 algorithm with swap 2026-09-04 23:08:06 +02:00
ShatteredMINT 50f7040f59 Merge pull request 'Add mem.compare, mem.copy, mem.fill32, unsafe_mem.compare' (#13) from Mutex/symphony_stdlib:mem_ops into main
Reviewed-on: #13
2026-09-04 21:55:22 +02:00
ShatteredMINT 57934309c2 Merge branch 'main' into mem_ops 2026-09-04 21:53:34 +02:00
ShatteredMINT 32a157e87d Merge pull request 'Changed mul_low algorithm to radix-4 multiplication' (#16) from Micha_i/symphony_stdlib:fast-math into main
Reviewed-on: #16
2026-09-04 08:21:49 +02:00
Michał Isalski a00c7f2a84 Changed mul_low algorithm to radix-4 multiplication 2026-09-04 00:11:33 +02:00
MutexRaceCondition ae0684f98a Merged main into mem_ops 2026-09-03 21:15:08 +02:00
MutexRaceCondition 257735d776 Remove leftover files 2026-09-03 21:01:07 +02:00
MutexRaceCondition 809f6e35d5 Merged with main 2026-09-03 20:58:17 +02:00
ShatteredMINT c83269fcf7 fix path to stdlib.asm 2026-09-03 20:35:41 +02:00
ShatteredMINT 6bf0c0a4fc add examples directory 2026-09-03 20:34:56 +02:00
ShatteredMINT 697445feec add tests folder 2026-09-03 20:34:16 +02:00
ShatteredMINT 7c49c5212e move assembly files into src directory 2026-09-03 20:32:40 +02:00
ShatteredMINT 6ff1a43c32 Merge pull request 'Added find_index array function' (#9) from Micha_i/symphony_stdlib:array-functions into main
Reviewed-on: #9
2026-09-03 20:30:09 +02:00
Michał Isalski b5249a43b9 Merge branch 'main' into array-functions 2026-09-03 20:29:24 +02:00
MutexRaceCondition 655ce0237e Fixed unsafe_mem.compare not working correctly when both input pointers are the same 2026-09-03 18:48:57 +02:00
MutexRaceCondition a1e19112c5 Specialised unsafe.asm to unsafe_mem.asm, and renamed unsafe.memcmp to unsafe.compare 2026-09-03 15:22:40 +02:00
MutexRaceCondition 3d874cebbd Merged with main 2026-09-03 15:17:00 +02:00
MutexRaceCondition 4aca17aa40 Moved memory operations into mem.asm (and renamed them) 2026-09-03 15:15:54 +02:00
ShatteredMINT 0188969dab Merge pull request 'Added read_line console function' (#10) from Micha_i/symphony_stdlib:console-functions into main
Reviewed-on: #10
2026-09-03 14:56:12 +02:00
Micha_i 5c9a33c24d Merge branch 'main' into array-functions 2026-09-03 09:04:11 +02:00
Micha_i e4edb56d08 Merge branch 'main' into console-functions 2026-09-03 09:04:03 +02:00
Michał Isalski aa8eef7b1f Fixed at-address 2026-09-02 22:56:24 +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
Michał Isalski 870b6ebc39 Merge branch 'main' into console-functions 2026-09-02 20:41:38 +02:00
Michał Isalski 1e4ad8f64c Merge branch 'main' into array-functions 2026-09-02 20:40:49 +02:00
MutexRaceCondition 7b43e75a9d Added memcmp, memcpy, memset32, unsafe.memcmp 2026-09-02 19:41:56 +02:00
Michal Isalski ca1ba67fd8 Added a shift conversion LUT and finished read_line (except Writeback) 2026-09-02 13:20:55 +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
Michał Isalski f9bb69f1f5 Added storing shift status 2026-09-02 02:03:00 +02:00
ShatteredMINT 9816111f38 clarify stack arguments 2026-09-01 14:08:20 +02:00
Michal Isalski 20e1f17181 Fixed swapped pop instructions 2026-09-01 13:52:45 +02:00
Michal Isalski 625f01166a Made find_index conform to new doc guidelines 2026-09-01 13:52:26 +02:00
ShatteredMINT f2e5c830b3 explain inline comment 2026-09-01 13:48:12 +02:00
Michal Isalski 65caf8aff9 Made read_line compliant to new doc guidelines 2026-09-01 13:47:09 +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
Michal Isalski 1dca814388 One instruction less by using another register 2026-09-01 10:54:29 +02:00
Michal Isalski 9dfb8248a6 Added handling for skipping non-renderable characters
Added handling for backspace character
2026-09-01 10:51:31 +02:00
Michał Isalski 4b962a1f87 Added read_line function 2026-09-01 00:38:37 +02:00
Michał Isalski e6a0739e95 pleegwat's code review fixes 2026-08-31 23:57:55 +02:00
Michał Isalski 1885f304ad Merge branch 'array-functions' of https://gitea.shatteredmint.net/Micha_i/symphony_stdlib into array-functions 2026-08-31 23:55:16 +02:00
Michał Isalski 63b37ab094 Added comment about predicate context 2026-08-31 23:06:59 +02:00
Michał Isalski 6981ff027d Tested and fixed stride->shift conversion missing 2026-08-31 23:06:59 +02:00
Michal Isalski 88c91a8eec Reduced operations to get -1 in register 2026-08-31 23:06:59 +02:00
Michal Isalski a02f0646b4 Fixed the predicate return address 2026-08-31 23:06:59 +02:00
Michal Isalski 7abfcabd73 Added find_index array function 2026-08-31 23:06:59 +02:00
Michał Isalski b476b8aaa3 Added comment about predicate context 2026-08-31 23:06:40 +02:00
Michał Isalski bddb153d9b Tested and fixed stride->shift conversion missing 2026-08-31 23:03:23 +02:00
Michal Isalski 29103d4335 Reduced operations to get -1 in register 2026-08-31 15:30:11 +02:00
Michal Isalski 837e8ba0a6 Fixed the predicate return address 2026-08-31 13:52:27 +02:00
Michal Isalski 6ca77970b8 Added find_index array function 2026-08-31 13:44:07 +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
20 changed files with 1666 additions and 89 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 [[src/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
+3
View File
@@ -0,0 +1,3 @@
# Examples
Examples of how to use the standard library to accomplish a task.
-62
View File
@@ -1,62 +0,0 @@
pub mul_low:
mov r3, 0 ; result
mov r4, 31 ; loop counter
mull_loop:
asr r5, r2, 31
and r5, r5, r1
lsl r5, r5, r4
add r3, r3, r5
lsl r2, r2, 1
sub r4, r4, 1
cmp r4, 0
jge mull_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
pub abs: ; SHOULD BE INLINED
; mask = v >> 31
asr r2, r1, 31
; v + mask
add r1, r1, r2
; return (v + mask) ^ mask
xor r1, r1, r2
jmp r13
; Calculates the minimum value of the two values provided in the r1 and r2 registers
; Based on Stanford's BitHacks
; Clobbers flags
pub min: ; SHOULD BE INLINED
; x < y
cmp r1, r2
lsr flags, flags, 2
; -(x < y)
neg flags, flags
; x ^ y
xor r1, r1, r2
; (x ^ y) & -(x < y)
and r1, r1, flags
; return y ^ ((x ^ y) & -(x < y))
xor r1, r2, r1
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
pub max: ; SHOULD BE INLINED
; x < y
cmp r1, r2
lsr flags, flags, 2
; -(x < y)
neg flags, flags
; x ^ y
xor r2, r1, r2
; (x ^ y) & -(x < y)
and r2, r2, flags
; return x ^ ((x ^ y) & -(x < y))
xor r1, r1, r2
jmp r13
+43
View File
@@ -0,0 +1,43 @@
Symphony ISA specs for use in stdlib
# `default.isa`
The stock game ISA. Production code must comply to this. Taken from `symphony_11_functions` in game version 2.1.330A
# `test.isa`
ISA with additional instructions. These additional instructions may be used in
the tests directory only. Some of these instructions require hardware changes.
## Labels in more instructions
`cmp`, `load`, and `store` instructions (but not `pload` and `pstore`) now also accept labels.
## `qcall %a(label)`
Performs a jump-and-link function call, with label size up to 32 bits.
_HARDWARE:_ Store PC+4 in the result register when in jump mode.
## `li %a(register) %b:U32(immediate | label)`
Loads an immediate up to 32 bits long
_HARDWARE:_ No requirements
## `halt%c(condition)`
Halts if `flags` matches the condition.
_HARDWARE:_ Halt if instruction bit 31 is set AND the condition block is true.
```
condition
"" 1000
".all" 1000
".e" 0001
".ne" 1001
".b" 0010
".ae" 1010
".be" 0011
".a" 1011
".l" 0100
".ge" 1100
".le" 0101
".g" 1101
```
+316
View File
@@ -0,0 +1,316 @@
[settings]
name = "Symphony"
[fields]
register
zr 0000
r1 0001
r2 0010
r3 0011
r4 0100
r5 0101
r6 0110
r7 0111
r8 1000
r9 1001
r10 1010
r11 1011
r12 1100
r13 1101
sp 1110
flags 1111
[instructions]
nop
00000000 00000000 00000000 00000000
# (31337_88272577071256, `Does nothing.`)
in %a(register)
00000001 aaaa0000 00000000 00000000
# (31337_44524771082050, `Loads an input and stores it in %a.`)
out %b(register)
00000010 00000000 0000bbbb 00000000
# (31337_18254089726214, `Sends %b to output.`)
out %a:U16(immediate)
00010010 00000000 aaaaaaaa aaaaaaaa
# (31337_15915531259935, `Sends %a to output.`)
keyboard %a(register)
00000011 aaaa0000 00000000 00000000
# (31337_27868418224252, `Loads keyboard input and stores it to %a.`)
screen %a(register), %b(register)
00000100 0000aaaa 0000bbbb 00000000
# (31337_39857449355186, `Stores the value of %b in screen setting %a.`)
screen %a(register), %b:U16(immediate)
00010100 0000aaaa bbbbbbbb bbbbbbbb
# (31337_12835125621741, `Stores the value of %b in screen setting %a.`)
time_0 %a(register)
00000101 aaaa0000 00000000 00000000
# (31337_72551400437977, `Loads the lower four bytes of the time value and stores it to %a.`)
time_1 %a(register)
00000110 aaaa0000 00000000 00000000
# (31337_68219187873304, `Loads the upper four bytes of the time value and stores it to %a.`)
counter %a(register)
00000111 aaaa0000 00000000 00000000
# (31337_67376584796076, `Loads the counter value and stores it to %a.`)
nand %a(register), %b(register), %c(register)
00100000 aaaabbbb 0000cccc 00000000
# (31337_86705346481666, `NAND %b and %c and stores the result in %a.`)
or %a(register), %b(register), %c(register)
00100001 aaaabbbb 0000cccc 00000000
# (31337_40732211165530, `OR %b and %c and stores the result in %a.`)
and %a(register), %b(register), %c(register)
00100010 aaaabbbb 0000cccc 00000000
# (31337_85679146831122, `AND %b and %c and stores the result in %a.`)
nor %a(register), %b(register), %c(register)
00100011 aaaabbbb 0000cccc 00000000
# (31337_37333076617857, `NOR %b and %c and stores the result in %a.`)
add %a(register), %b(register), %c(register)
00100100 aaaabbbb 0000cccc 00000000
# (31337_51030225266712, `ADD %b and %c and stores the result in %a.`)
sub %a(register), %b(register), %c(register)
00100101 aaaabbbb 0000cccc 00000000
# (31337_28652904157569, `SUB %b and %c and stores the result in %a.`)
xor %a(register), %b(register), %c(register)
00100110 aaaabbbb 0000cccc 00000000
# (31337_46967848572964, `XOR %b and %c and stores the result in %a.`)
lsl %a(register), %b(register), %c(register)
00100111 aaaabbbb 0000cccc 00000000
# (31337_13576771671264, `Logical shifts left %b by %c and stores the result in %a.`)
lsr %a(register), %b(register), %c(register)
00101000 aaaabbbb 0000cccc 00000000
# (31337_67019902294705, `Logical shifts right %b by %c and stores the result in %a.`)
asr %a(register), %b(register), %c(register)
00101001 aaaabbbb 0000cccc 00000000
# (31337_42626144645228, `Arithmetic shift right %b by %c and stores the result in %a.`)
cmp %a(register), %b(register)
00101010 1111aaaa 0000bbbb 00000000
# (31337_54565380946620, `Compares %a and %b and stores the result in the 'flags' register.`)
nand %a(register), %b(register), %c:U16(immediate | label)
00110000 aaaabbbb cccccccc cccccccc
# (31337_75302656449127, `NAND %b and %c and stores the result in %a.`)
or %a(register), %b(register), %c:U16(immediate | label)
00110001 aaaabbbb cccccccc cccccccc
# (31337_30574433901064, `OR %b and %c and stores the result in %a.`)
nor %a(register), %b(register), %c:U16(immediate | label)
00110011 aaaabbbb cccccccc cccccccc
# (31337_46087142860391, `NOR %b and %c and stores the result in %a.`)
and %a(register), %b(register), %c:U16(immediate | label)
00110010 aaaabbbb cccccccc cccccccc
# (31337_86713121915282, `AND %b and %c and stores the result in %a.`)
add %a(register), %b(register), %c:U16(immediate | label)
00110100 aaaabbbb cccccccc cccccccc
# (31337_23349431010010, `ADD %b and %c and stores the result in %a.`)
sub %a(register), %b(register), %c:U16(immediate | label)
00110101 aaaabbbb cccccccc cccccccc
# (31337_67495552371225, `SUB %b and %c and stores the result in %a.`)
xor %a(register), %b(register), %c:U16(immediate | label)
00110110 aaaabbbb cccccccc cccccccc
# (31337_66785854415645, `XOR %b and %c and stores the result in %a.`)
lsl %a(register), %b(register), %c:U16(immediate | label)
00110111 aaaabbbb cccccccc cccccccc
# (31337_54577249258785, `Logical shifts left %b by %c and stores the result in %a.`)
lsr %a(register), %b(register), %c:U16(immediate | label)
00111000 aaaabbbb cccccccc cccccccc
# (31337_16685334571248, `Logical shifts right %b by %c and stores the result in %a.`)
asr %a(register), %b(register), %c:U16(immediate | label)
00111001 aaaabbbb cccccccc cccccccc
# (31337_55551510150818, `Arithmetic shifts right %b and %c and stores the result in %a.`)
cmp %a(register), %b:U16(immediate)
00111010 1111aaaa bbbbbbbb bbbbbbbb
# (31337_37102964808364, `Compares %a and %b and stores the result in the 'flags' register.`)
jmp %a(register)
01001000 00001111 0000aaaa 00000000
# (31337_74148826866592, `Jumps to %a.`)
jmp %a:U16(immediate | label)
01011000 00001111 aaaaaaaa aaaaaaaa
# (31337_90876214388280, `Jumps to %a.`)
je %a:U16(immediate | label)
01010001 00001111 aaaaaaaa aaaaaaaa
# (31337_88180305667389, `Jumps to %a if the values were equal (reading results from 'flags').`)
jne %a:U16(immediate | label)
01011001 00001111 aaaaaaaa aaaaaaaa
# (31337_67178948469318, `Jumps to %a if the values were not equal (reading results from 'flags').`)
jb %a:U16(immediate | label)
01010010 00001111 aaaaaaaa aaaaaaaa
# (31337_31763492459833, `Jumps to %a if the first value was below (unsigned) the second (reading results from 'flags').`)
jae %a:U16(immediate | label)
01011010 00001111 aaaaaaaa aaaaaaaa
# (31337_82087388048746, `Jumps to %a if the first value was above (unsigned) or equal to the second (reading results from 'flags').`)
jbe %a:U16(immediate | label)
01010011 00001111 aaaaaaaa aaaaaaaa
# (31337_26183125927881, `Jumps to %a if the First value was below (unsigned) or equal to the second (reading results from 'flags').`)
ja %a:U16(immediate | label)
01011011 00001111 aaaaaaaa aaaaaaaa
# (31337_11626177279992, `Jumps to %a if the first value was above (unsigned) the second (reading results from 'flags').`)
jl %a:U16(immediate | label)
01010100 00001111 aaaaaaaa aaaaaaaa
# (31337_71637103729391, `Jumps to %a if the first value was less (signed) than the second (reading results from 'flags').`)
jge %a:U16(immediate | label)
01011100 00001111 aaaaaaaa aaaaaaaa
# (31337_53499765587663, `Jumps to %a if the first value was greater (signed) or equal to the second (reading results from 'flags').`)
jle %a:U16(immediate | label)
01010101 00001111 aaaaaaaa aaaaaaaa
# (31337_77040234358389, `Jumps to %a if the first value was less (signed) or equal to the second (reading results from 'flags').`)
jg %a:U16(immediate | label)
01011101 00001111 aaaaaaaa aaaaaaaa
# (31337_71317192057126, `Jumps to %a if the first value was greater (signed) than the second (reading results from 'flags').`)
load_8 %dest(register), [%adr(register)]
01100000 dddd0000 0000aaaa 00000000
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_8 %dest(register), [%adr:U16(immediate)]
01110000 dddd0000 aaaaaaaa aaaaaaaa
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_16 %dest(register), [%adr(register)]
01100001 dddd0000 0000aaaa 00000000
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_16 %dest(register), [%adr:U16(immediate)]
01110001 dddd0000 aaaaaaaa aaaaaaaa
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_32 %dest(register), [%adr(register)]
01100010 dddd0000 0000aaaa 00000000
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_32 %dest(register), [%adr:U16(immediate)]
01110010 dddd0000 aaaaaaaa aaaaaaaa
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
pload %dest(register), [%adr(register)]
01100011 dddd0000 0000aaaa 00000000
# (31337_89257021109854, `Loads address %adr from persistent memory and stores it to %dest.`)
pload %dest(register), [%adr:U16(immediate)]
01110011 dddd0000 aaaaaaaa aaaaaaaa
# (31337_89257021109854, `Loads address %adr from persistent memory and stores it to %dest.`)
store_8 [%adr(register)], %value(register)
01100100 0000vvvv 0000aaaa 00000000
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_8 [%adr:U16(immediate)], %value(register)
01110100 0000vvvv aaaaaaaa aaaaaaaa
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_16 [%adr(register)], %value(register)
01100101 0000vvvv 0000aaaa 00000000
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_16 [%adr:U16(immediate)], %value(register)
01110101 0000vvvv aaaaaaaa aaaaaaaa
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_32 [%adr(register)], %value(register)
01100110 0000vvvv 0000aaaa 00000000
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_32 [%adr:U16(immediate)], %value(register)
01110110 0000vvvv aaaaaaaa aaaaaaaa
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
pstore [%adr(register)], %value(register)
01100111 0000vvvv 0000aaaa 00000000
# (31337_13110303123442, `Stores %value to address %adr in persistent memory.`)
pstore [%adr:U16(immediate)], %value(register)
01110111 0000vvvv aaaaaaaa aaaaaaaa
# (31337_13110303123442, `Stores %value to address %adr in persistent memory.`)
mov %a(register), %b(register)
00100001 aaaa0000 0000bbbb 00000000
# (31337_53941960758392, `Moves a value to %a from %b.`)
mov %a(register), %b:U16(immediate | label)
00110001 aaaa0000 bbbbbbbb bbbbbbbb
# (31337_34859363394334, `Moves a value to %a.`)
neg %a(register), %b(register)
00100101 aaaa0000 0000bbbb 00000000
# (31337_72894099288447, `Negates a %b and stores it in %a.`)
neg %a(register), %b:U16(immediate | label)
00110101 aaaa0000 bbbbbbbb bbbbbbbb
# (31337_57504570243305, `Negates a value and stores it in %a.`)
not %a(register), %b(register)
00100011 aaaa0000 0000bbbb 00000000
# (31337_55357258069734, `Toggles the bits of %b and stores it in %a.`)
not %a(register), %b:U16(immediate | label)
00110011 aaaa0000 bbbbbbbb bbbbbbbb
# (31337_40508048121543, `Toggles the bits of a value and stores it in %a.`)
push %a(register)
00110101 11101110 00000000 00000100 01100110 0000aaaa 00001110 00000000
# (31337_28545157055801, `Pushes %a onto the stack. This instruction is a synonym for: sub sp, sp, 4 store_32 [sp], %a`)
pop %a(register)
01100010 aaaa0000 00001110 00000000 00110100 11101110 00000000 00000100
# (31337_54469754973269, `Pops %a off the stack. This instruction is a synonym for: load_32 %a, [sp] add sp, sp, 4`)
call %a(label)
00000111 11110000 00000000 00000000 00110100 11111111 00000000 00010100 00110101 11101110 00000000 00000100 01100110 00001111 00001110 00000000 01011000 00001111 aaaaaaaa aaaaaaaa
# (31337_57788320241950, `Calls %a. Overwrites the flag register. This instruction is a synonym for: counter flags add flags, flags, 20 sub sp, sp, 4 store_32 [sp], flags jmp %a`)
ret
01100010 11110000 00001110 00000000 00110100 11101110 00000000 00000100 01001000 00001111 00001111 00000000
# (31337_48332460366102, `Returns from the last function call. Overwrites the flag register. This instruction is a synonym for: load_32 flags, [sp] add sp, sp, 4 jmp flags`)
+361
View File
@@ -0,0 +1,361 @@
[settings]
name = "Symphony"
[fields]
register
zr 0000
r1 0001
r2 0010
r3 0011
r4 0100
r5 0101
r6 0110
r7 0111
r8 1000
r9 1001
r10 1010
r11 1011
r12 1100
r13 1101
sp 1110
flags 1111
condition
"" 1000
".all" 1000
".e" 0001
".ne" 1001
".b" 0010
".ae" 1010
".be" 0011
".a" 1011
".l" 0100
".ge" 1100
".le" 0101
".g" 1101
[instructions]
nop
00000000 00000000 00000000 00000000
# (31337_88272577071256, `Does nothing.`)
in %a(register)
00000001 aaaa0000 00000000 00000000
# (31337_44524771082050, `Loads an input and stores it in %a.`)
out %b(register)
00000010 00000000 0000bbbb 00000000
# (31337_18254089726214, `Sends %b to output.`)
out %a:U16(immediate)
00010010 00000000 aaaaaaaa aaaaaaaa
# (31337_15915531259935, `Sends %a to output.`)
keyboard %a(register)
00000011 aaaa0000 00000000 00000000
# (31337_27868418224252, `Loads keyboard input and stores it to %a.`)
screen %a(register), %b(register)
00000100 0000aaaa 0000bbbb 00000000
# (31337_39857449355186, `Stores the value of %b in screen setting %a.`)
screen %a(register), %b:U16(immediate)
00010100 0000aaaa bbbbbbbb bbbbbbbb
# (31337_12835125621741, `Stores the value of %b in screen setting %a.`)
time_0 %a(register)
00000101 aaaa0000 00000000 00000000
# (31337_72551400437977, `Loads the lower four bytes of the time value and stores it to %a.`)
time_1 %a(register)
00000110 aaaa0000 00000000 00000000
# (31337_68219187873304, `Loads the upper four bytes of the time value and stores it to %a.`)
counter %a(register)
00000111 aaaa0000 00000000 00000000
# (31337_67376584796076, `Loads the counter value and stores it to %a.`)
nand %a(register), %b(register), %c(register)
00100000 aaaabbbb 0000cccc 00000000
# (31337_86705346481666, `NAND %b and %c and stores the result in %a.`)
or %a(register), %b(register), %c(register)
00100001 aaaabbbb 0000cccc 00000000
# (31337_40732211165530, `OR %b and %c and stores the result in %a.`)
and %a(register), %b(register), %c(register)
00100010 aaaabbbb 0000cccc 00000000
# (31337_85679146831122, `AND %b and %c and stores the result in %a.`)
nor %a(register), %b(register), %c(register)
00100011 aaaabbbb 0000cccc 00000000
# (31337_37333076617857, `NOR %b and %c and stores the result in %a.`)
add %a(register), %b(register), %c(register)
00100100 aaaabbbb 0000cccc 00000000
# (31337_51030225266712, `ADD %b and %c and stores the result in %a.`)
sub %a(register), %b(register), %c(register)
00100101 aaaabbbb 0000cccc 00000000
# (31337_28652904157569, `SUB %b and %c and stores the result in %a.`)
xor %a(register), %b(register), %c(register)
00100110 aaaabbbb 0000cccc 00000000
# (31337_46967848572964, `XOR %b and %c and stores the result in %a.`)
lsl %a(register), %b(register), %c(register)
00100111 aaaabbbb 0000cccc 00000000
# (31337_13576771671264, `Logical shifts left %b by %c and stores the result in %a.`)
lsr %a(register), %b(register), %c(register)
00101000 aaaabbbb 0000cccc 00000000
# (31337_67019902294705, `Logical shifts right %b by %c and stores the result in %a.`)
asr %a(register), %b(register), %c(register)
00101001 aaaabbbb 0000cccc 00000000
# (31337_42626144645228, `Arithmetic shift right %b by %c and stores the result in %a.`)
cmp %a(register), %b(register)
00101010 1111aaaa 0000bbbb 00000000
# (31337_54565380946620, `Compares %a and %b and stores the result in the 'flags' register.`)
nand %a(register), %b(register), %c:U16(immediate | label)
00110000 aaaabbbb cccccccc cccccccc
# (31337_75302656449127, `NAND %b and %c and stores the result in %a.`)
or %a(register), %b(register), %c:U16(immediate | label)
00110001 aaaabbbb cccccccc cccccccc
# (31337_30574433901064, `OR %b and %c and stores the result in %a.`)
nor %a(register), %b(register), %c:U16(immediate | label)
00110011 aaaabbbb cccccccc cccccccc
# (31337_46087142860391, `NOR %b and %c and stores the result in %a.`)
and %a(register), %b(register), %c:U16(immediate | label)
00110010 aaaabbbb cccccccc cccccccc
# (31337_86713121915282, `AND %b and %c and stores the result in %a.`)
add %a(register), %b(register), %c:U16(immediate | label)
00110100 aaaabbbb cccccccc cccccccc
# (31337_23349431010010, `ADD %b and %c and stores the result in %a.`)
sub %a(register), %b(register), %c:U16(immediate | label)
00110101 aaaabbbb cccccccc cccccccc
# (31337_67495552371225, `SUB %b and %c and stores the result in %a.`)
xor %a(register), %b(register), %c:U16(immediate | label)
00110110 aaaabbbb cccccccc cccccccc
# (31337_66785854415645, `XOR %b and %c and stores the result in %a.`)
lsl %a(register), %b(register), %c:U16(immediate | label)
00110111 aaaabbbb cccccccc cccccccc
# (31337_54577249258785, `Logical shifts left %b by %c and stores the result in %a.`)
lsr %a(register), %b(register), %c:U16(immediate | label)
00111000 aaaabbbb cccccccc cccccccc
# (31337_16685334571248, `Logical shifts right %b by %c and stores the result in %a.`)
asr %a(register), %b(register), %c:U16(immediate | label)
00111001 aaaabbbb cccccccc cccccccc
# (31337_55551510150818, `Arithmetic shifts right %b and %c and stores the result in %a.`)
cmp %a(register), %b:U16(immediate | label)
00111010 1111aaaa bbbbbbbb bbbbbbbb
# (31337_37102964808364, `Compares %a and %b and stores the result in the 'flags' register.`)
jmp %a(register)
01001000 00001111 0000aaaa 00000000
# (31337_74148826866592, `Jumps to %a.`)
jmp %a:U16(immediate | label)
01011000 00001111 aaaaaaaa aaaaaaaa
# (31337_90876214388280, `Jumps to %a.`)
je %a:U16(immediate | label)
01010001 00001111 aaaaaaaa aaaaaaaa
# (31337_88180305667389, `Jumps to %a if the values were equal (reading results from 'flags').`)
jne %a:U16(immediate | label)
01011001 00001111 aaaaaaaa aaaaaaaa
# (31337_67178948469318, `Jumps to %a if the values were not equal (reading results from 'flags').`)
jb %a:U16(immediate | label)
01010010 00001111 aaaaaaaa aaaaaaaa
# (31337_31763492459833, `Jumps to %a if the first value was below (unsigned) the second (reading results from 'flags').`)
jae %a:U16(immediate | label)
01011010 00001111 aaaaaaaa aaaaaaaa
# (31337_82087388048746, `Jumps to %a if the first value was above (unsigned) or equal to the second (reading results from 'flags').`)
jbe %a:U16(immediate | label)
01010011 00001111 aaaaaaaa aaaaaaaa
# (31337_26183125927881, `Jumps to %a if the First value was below (unsigned) or equal to the second (reading results from 'flags').`)
ja %a:U16(immediate | label)
01011011 00001111 aaaaaaaa aaaaaaaa
# (31337_11626177279992, `Jumps to %a if the first value was above (unsigned) the second (reading results from 'flags').`)
jl %a:U16(immediate | label)
01010100 00001111 aaaaaaaa aaaaaaaa
# (31337_71637103729391, `Jumps to %a if the first value was less (signed) than the second (reading results from 'flags').`)
jge %a:U16(immediate | label)
01011100 00001111 aaaaaaaa aaaaaaaa
# (31337_53499765587663, `Jumps to %a if the first value was greater (signed) or equal to the second (reading results from 'flags').`)
jle %a:U16(immediate | label)
01010101 00001111 aaaaaaaa aaaaaaaa
# (31337_77040234358389, `Jumps to %a if the first value was less (signed) or equal to the second (reading results from 'flags').`)
jg %a:U16(immediate | label)
01011101 00001111 aaaaaaaa aaaaaaaa
# (31337_71317192057126, `Jumps to %a if the first value was greater (signed) than the second (reading results from 'flags').`)
load_8 %dest(register), [%adr(register)]
01100000 dddd0000 0000aaaa 00000000
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_8 %dest(register), [%adr:U16(immediate | label)]
01110000 dddd0000 aaaaaaaa aaaaaaaa
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_16 %dest(register), [%adr(register)]
01100001 dddd0000 0000aaaa 00000000
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_16 %dest(register), [%adr:U16(immediate | label)]
01110001 dddd0000 aaaaaaaa aaaaaaaa
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_32 %dest(register), [%adr(register)]
01100010 dddd0000 0000aaaa 00000000
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
load_32 %dest(register), [%adr:U16(immediate | label)]
01110010 dddd0000 aaaaaaaa aaaaaaaa
# (31337_57200935026773, `Loads address %adr from main memory and stores it to %dest.`)
pload %dest(register), [%adr(register)]
01100011 dddd0000 0000aaaa 00000000
# (31337_89257021109854, `Loads address %adr from persistent memory and stores it to %dest.`)
pload %dest(register), [%adr:U16(immediate)]
01110011 dddd0000 aaaaaaaa aaaaaaaa
# (31337_89257021109854, `Loads address %adr from persistent memory and stores it to %dest.`)
store_8 [%adr(register)], %value(register)
01100100 0000vvvv 0000aaaa 00000000
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_8 [%adr:U16(immediate | label)], %value(register)
01110100 0000vvvv aaaaaaaa aaaaaaaa
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_16 [%adr(register)], %value(register)
01100101 0000vvvv 0000aaaa 00000000
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_16 [%adr:U16(immediate | label)], %value(register)
01110101 0000vvvv aaaaaaaa aaaaaaaa
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_32 [%adr(register)], %value(register)
01100110 0000vvvv 0000aaaa 00000000
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
store_32 [%adr:U16(immediate | label)], %value(register)
01110110 0000vvvv aaaaaaaa aaaaaaaa
# (31337_61040780958703, `Stores %value to address %adr in main memory.`)
pstore [%adr(register)], %value(register)
01100111 0000vvvv 0000aaaa 00000000
# (31337_13110303123442, `Stores %value to address %adr in persistent memory.`)
pstore [%adr:U16(immediate)], %value(register)
01110111 0000vvvv aaaaaaaa aaaaaaaa
# (31337_13110303123442, `Stores %value to address %adr in persistent memory.`)
mov %a(register), %b(register)
00100001 aaaa0000 0000bbbb 00000000
# (31337_53941960758392, `Moves a value to %a from %b.`)
mov %a(register), %b:U16(immediate | label)
00110001 aaaa0000 bbbbbbbb bbbbbbbb
# (31337_34859363394334, `Moves a value to %a.`)
neg %a(register), %b(register)
00100101 aaaa0000 0000bbbb 00000000
# (31337_72894099288447, `Negates a %b and stores it in %a.`)
neg %a(register), %b:U16(immediate | label)
00110101 aaaa0000 bbbbbbbb bbbbbbbb
# (31337_57504570243305, `Negates a value and stores it in %a.`)
not %a(register), %b(register)
00100011 aaaa0000 0000bbbb 00000000
# (31337_55357258069734, `Toggles the bits of %b and stores it in %a.`)
not %a(register), %b:U16(immediate | label)
00110011 aaaa0000 bbbbbbbb bbbbbbbb
# (31337_40508048121543, `Toggles the bits of a value and stores it in %a.`)
push %a(register)
00110101 11101110 00000000 00000100 01100110 0000aaaa 00001110 00000000
# (31337_28545157055801, `Pushes %a onto the stack. This instruction is a synonym for: sub sp, sp, 4 store_32 [sp], %a`)
pop %a(register)
01100010 aaaa0000 00001110 00000000 00110100 11101110 00000000 00000100
# (31337_54469754973269, `Pops %a off the stack. This instruction is a synonym for: load_32 %a, [sp] add sp, sp, 4`)
call %a(label)
00000111 11110000 00000000 00000000 00110100 11111111 00000000 00010100 00110101 11101110 00000000 00000100 01100110 00001111 00001110 00000000 01011000 00001111 aaaaaaaa aaaaaaaa
# (31337_57788320241950, `Calls %a. Overwrites the flag register. This instruction is a synonym for: counter flags add flags, flags, 20 sub sp, sp, 4 store_32 [sp], flags jmp %a`)
ret
01100010 11110000 00001110 00000000 00110100 11101110 00000000 00000100 01001000 00001111 00001111 00000000
# (31337_48332460366102, `Returns from the last function call. Overwrites the flag register. This instruction is a synonym for: load_32 flags, [sp] add sp, sp, 4 jmp flags`)
li %r(register), %a:S33(immediate | label)
; assert(0 - 0x80000000 <= %a, "%a is too low")
; assert(%a <= 0xffffffff, "%a is too high")
%s = trailing_zeros(%a)
%sa = %a >> %s
%nota = %a ^ 0xffffffff
when %a <=u 0xffff : 00110001 %r[3:0]0000 %a[15:0] ; First 64k
when %nota <=u 0xffff: 00110001 %r[3:0]0000 %nota[15:0] 00100011 %r[3:0]0000 0000%r[3:0] 00000000 ; Last 64k
when %sa <=u 0xffff : 00110001 %r[3:0]0000 %sa[15:0] 00110111 %r[3:0]%r[3:0] 00000000 %s[7:0] ; Aligned
00110001 %r[3:0]0000 %a[31:16] 00110111 %r[3:0]%r[3:0] 00000000 00010000 00110001 %r[3:0]%r[3:0] %a[15:0] ; Any 32-bit
# Load 32-bit immediate %a into %r
qcall %a:U32(label)
; assert(0 - 0x80000000 <= %a, "%a is too low")
; assert(%a <= 0xffffffff, "%a is too high")
%r = 13
%f = 15
%s = trailing_zeros(%a)
%sa = %a >> %s
%nota = %a ^ 0xffffffff
when %a <=u 0xffff : 01011000 %r[3:0]ffff %a[15:0] ; First 64k
when %nota <=u 0xffff: 00110001 %r[3:0]0000 %nota[15:0] 00100011 %r[3:0]0000 0000%r[3:0] 00000000 01001000 %r[3:0]ffff 0000%r[3:0] 00000000 ; Last 64k
when %sa <=u 0xffff : 00110001 %r[3:0]0000 %sa[15:0] 00110111 %r[3:0]%r[3:0] 00000000 %s[7:0] 01001000 %r[3:0]ffff 0000%r[3:0] 00000000 ; Aligned
00110001 %r[3:0]0000 %a[31:16] 00110111 %r[3:0]%r[3:0] 00000000 00010000 00110001 %r[3:0]%r[3:0] %a[15:0] 01001000 %r[3:0]ffff 0000%r[3:0] 00000000 ; Any 32-bit
# Jump and link to 32-bit label
halt%c(condition)
1000cccc 00001111 00000000 00000000
# Halt on condition %c
+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
+114
View File
@@ -0,0 +1,114 @@
; 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, r6
pub mul_low:
cmp r1, r2
jbe mul_low_noswap
xor r1, r2, r1
xor r2, r1, r2
xor r1, r2, r1
; Passthrough
; Multiplies r1 and r2, returning the lower part of the result
; This method assumes r1 is smaller than r2, which results in faster execution
; Arguments:
; r1 - The first value
; r2 - The second value
; Result:
; r1 - The lower 32 bits of the result
; Clobbers: r2, r3, r4, r5, r6
pub mul_low_noswap:
mov r4, 0 ; r4 has the result
mov r6, mul_loop_end
add r3, r2, r2
mul_loop:
and r5, r1, 14 ; Taking the first four bits, discarding the odd
lsl r5, r5, 1 ; 0000 - 0, 0001 - 2, 0010 - 4, 0011 - 8, etc.
sub r5, r6, r5
jmp r5
add r4, r4, r3
add r4, r4, r3
add r4, r4, r3
add r4, r4, r3
add r4, r4, r3
add r4, r4, r3
add r4, r4, r3
mul_loop_end:
mov flags, r1
jne mul_skip_one
add r4, r4, r2
mul_skip_one:
lsl r2, r2, 4
lsl r3, r3, 4
lsr r1, r1, 4
cmp r1, zr
jne mul_loop
mov r1, r4
jmp r13
; Calculates the absolute value of the value provided in the r1 register
; 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
; v + mask
add r1, r1, r2
; return (v + mask) ^ mask
xor r1, r1, r2
jmp r13
; Calculates the minimum value of the two values provided in the r1 and r2 registers
; 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
lsr flags, flags, 2
; -(x < y)
neg flags, flags
; x ^ y
xor r1, r1, r2
; (x ^ y) & -(x < y)
and r1, r1, flags
; return y ^ ((x ^ y) & -(x < y))
xor r1, r2, r1
jmp r13
; Calculates the maximum value of the two values provided in the r1 and r2 registers
; 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
lsr flags, flags, 2
; -(x < y)
neg flags, flags
; x ^ y
xor r2, r1, r2
; (x ^ y) & -(x < y)
and r2, r2, flags
; return x ^ ((x ^ y) & -(x < y))
xor r1, r1, r2
jmp r13
+174
View File
@@ -0,0 +1,174 @@
; 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
+9
View File
@@ -0,0 +1,9 @@
pub include bit
pub include imath
pub include array
pub include console
pub include mem
pub include string_to_int
; Needs to be last!
pub include LUTs
+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 (akin to libc atoi(), strotoi())
; 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 auto:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne auto_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
auto_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 bin_loop
cmp r3, 0x306F ; "0o"
je oct_loop
cmp r3, 0x3078 ; "0x"
je hex_loop
sub r2, r2, 2 ; no matching prefix, move pointer back
jmp dec_loop
; Epilogue
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 dec:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne dec_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
dec_loop:
load_8 r3, [r2]
dec_positive:
sub r3, r3, 0x30 ; '0'
cmp r3, 9
ja 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 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 bin:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne bin_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
bin_positive:
load_16 r3, [r2] ; check for prefix
or r3, r3, 0x20 ; 2nd char to lower case
cmp r3, 0x3062 ; "0b"
jne bin_loop
add r2, r2, 2
bin_loop:
load_8 r3, [r2]
sub r3, r3, 0x30 ; '0'
cmp r3, 1
ja done
add r2, r2, 1
lsl r1, r1, 1
add r1, r1, r3
jmp 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 oct:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne oct_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
oct_positive:
load_16 r3, [r2] ; check for prefix
or r3, r3, 0x20 ; 2nd char to lower case
cmp r3, 0x306F ; "0o"
jne oct_loop
add r2, r2, 2
oct_loop:
load_8 r3, [r2]
sub r3, r3, 0x30 ; '0'
cmp r3, 7
ja done
add r2, r2, 1
lsl r1, r1, 3
add r1, r1, r3
jmp 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 hex:
mov r2, r1
mov r1, 0
mov r4, 0
load_8 r3, [r2]
cmp r3, 0x2D ; '-'
jne hex_positive
sub r4, zr, 1 ; Set r4 to -1
add r2, r2, 1
hex_positive:
load_16 r3, [r2] ; check for prefix
or r3, r3, 0x20 ; 2nd char to lower case
cmp r3, 0x3078 ; "0x"
jne hex_loop
add r2, r2, 2
hex_loop:
load_8 r3, [r2]
sub r3, r3, 0x30 ; '0'
cmp r3, 10
jb hex_add
sub r3, r3, 0x11 ; 'A' - '0'
and r3, r3, 0xdf ; to lower case
cmp r3, 5 ; 0-5: 6 letters
ja done
add r3, r3, 10 ; Adjust for digits below
hex_add:
add r2, r2, 1
lsl r1, r1, 4
add r1, r1, r3
jmp hex_loop
+77
View File
@@ -0,0 +1,77 @@
; 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
-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.
+57
View File
@@ -0,0 +1,57 @@
; test harness for string_to_int
; Registers:
; r8 - test address
; r9 - expected result
; r10 - actual length
; r11 - expected length
li r8, tests
li r12, end_of_tests
next_test:
; Set up arguments and run test
add r1, r8, 5 ; Start of test string
mov r13, atoi_test_ret
jmp stdlib.string_to_int.auto
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, r12
jl next_test
; Done
success: jmp success
include ../src/stdlib
@0x20000
tests:
; inlen is the number of input bytes the function is expected to consume
; addr result inlen instr
@0x20000 U32 0 U8 0 "\0"
@0x20010 U32 0 U8 1 "0\0"
@0x20020 U32 1 U8 1 "1\0"
@0x20030 U32 2 U8 1 "2:\0"
@0x20040 U32 42 U8 2 "42\0"
@0x20050 U32 67 U8 2 "67lol\0"
@0x20060 U32 0x69a U8 5 "0x69a@\0"
@0x20070 U32 0x4B4 U8 5 "0x4B4g\0"
@0x20080 U32 0o23 U8 4 "0o239\0"
@0x20090 U32 0b1011 U8 6 "0b1011\0"
@0x200a0 U32 0b10001 U8 7 "0b100012\0"
@0x200b0 U32 0xFFFFFFEB U8 3 "-21\0" ; Yuk
@0x200c0
end_of_tests: