Compare commits

..
8 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
12 changed files with 21 additions and 280 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
This is a 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. 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 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 using it as a learning resource have a look at the [teaching folder](teaching).
View File
-3
View File
@@ -1,3 +0,0 @@
# Examples
Examples of how to use the standard library to accomplish a task.
View File
-100
View File
@@ -1,100 +0,0 @@
@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
@@ -1,77 +0,0 @@
; 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
-69
View File
@@ -1,69 +0,0 @@
; 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
-8
View File
@@ -1,8 +0,0 @@
pub include bit
pub include imath
pub include array
pub include console
pub include strint
; Needs to be last!
pub include LUTs
+3
View File
@@ -0,0 +1,3 @@
pub include bit
pub include imath
pub include strint
View File
+17 -19
View File
@@ -4,8 +4,7 @@
; r9 - expected result ; r9 - expected result
; r10 - actual length ; r10 - actual length
; r11 - expected length ; r11 - expected length
li r8, tests mov r8, tests
li r12, end_of_tests
next_test: next_test:
; Set up arguments and run test ; Set up arguments and run test
add r1, r8, 5 ; Start of test string add r1, r8, 5 ; Start of test string
@@ -28,30 +27,29 @@ incorrect_length: jne incorrect_length
; Next test ; Next test
add r8, r8, 0x10 add r8, r8, 0x10
cmp r8, r12 cmp r8, end_of_tests
jl next_test jl next_test
; Done ; Done
success: jmp success success: jmp success
include ../src/stdlib include stdlib
@0x20000 @0x1000
tests: tests:
; inlen is the number of input bytes the function is expected to consume ; inlen is the number of input bytes the function is expected to consume
; addr result inlen instr ; addr result inlen instr
@0x20000 U32 0 U8 0 "\0" @0x1000 U32 0 U8 0 "\0"
@0x20010 U32 0 U8 1 "0\0" @0x1010 U32 0 U8 1 "0\0"
@0x20020 U32 1 U8 1 "1\0" @0x1020 U32 1 U8 1 "1\0"
@0x20030 U32 2 U8 1 "2:\0" @0x1030 U32 2 U8 1 "2:\0"
@0x20040 U32 42 U8 2 "42\0" @0x1040 U32 42 U8 2 "42\0"
@0x20050 U32 67 U8 2 "67lol\0" @0x1050 U32 67 U8 2 "67lol\0"
@0x20060 U32 0x69a U8 5 "0x69a@\0" @0x1060 U32 0x69a U8 5 "0x69a@\0"
@0x20070 U32 0x4B4 U8 5 "0x4B4g\0" @0x1070 U32 0x4B4 U8 5 "0x4B4g\0"
@0x20080 U32 0o23 U8 4 "0o239\0" @0x1080 U32 0o23 U8 4 "0o239\0"
@0x20090 U32 0b1011 U8 6 "0b1011\0" @0x1090 U32 0b1011 U8 6 "0b1011\0"
@0x200a0 U32 0b10001 U8 7 "0b100012\0" @0x10a0 U32 0b10001 U8 7 "0b100012\0"
@0x200b0 U32 0xFFFFFFEB U8 3 "-21\0" ; Yuk @0x10b0 U32 0xFFFFFFEB U8 3 "-21\0" ; Yuk
@0x200c0 @0x10c0
end_of_tests: end_of_tests:
-3
View File
@@ -1,3 +0,0 @@
# Tests
Tests for the standard library go here, tests are allowed to depend on the recommended spec.isa changes.