From 8e7441fdb353fb79a2b779464afbd61441b4e9af Mon Sep 17 00:00:00 2001 From: PleegWat Date: Sat, 5 Sep 2026 15:30:06 +0200 Subject: [PATCH 1/6] Add ISA definitions for symphony Adds both a 'stock' ISA (copied from the game) and one modified for test code --- isa/README.md | 43 ++++++ isa/default.isa | 316 ++++++++++++++++++++++++++++++++++++++++++ isa/test.isa | 358 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 717 insertions(+) create mode 100644 isa/README.md create mode 100644 isa/default.isa create mode 100644 isa/test.isa diff --git a/isa/README.md b/isa/README.md new file mode 100644 index 0000000..592b680 --- /dev/null +++ b/isa/README.md @@ -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 +``` diff --git a/isa/default.isa b/isa/default.isa new file mode 100644 index 0000000..f9514c8 --- /dev/null +++ b/isa/default.isa @@ -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`) + diff --git a/isa/test.isa b/isa/test.isa new file mode 100644 index 0000000..5aca729 --- /dev/null +++ b/isa/test.isa @@ -0,0 +1,358 @@ +[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") +%nota = ~%a +when 0x0000 <= %a && %a <= 0xffff: 00110001 rrrr0000 %a[15:0] ; First 64k +when %nota <= 0xffff : 00110001 rrrr0000 %nota[15:0] 00100011 rrrr0000 0000rrrr 00000000 ; Last 64k +when !(%a & 0x0f) && %a < 0xffff0: 00110001 rrrr0000 %a[19:4] 00100111 rrrrrrrr 00000000 00000100 ; 16-byte aligned +when !(%a & 0x03) && %a < 0x3fffc: 00110001 rrrr0000 %a[17:2] 00100111 rrrrrrrr 00000000 00000010 ; 4-byte aligned +00110001 rrrr0000 %a[31:16] 00100111 rrrrrrrr 00000000 00010000 00110001 rrrrrrrr %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 +%nota = ~%a +when 0x0000 <= %a && %a <= 0xffff: 01011000 rrrrffff %a[15:0] ; First 64k +when %nota <= 0xffff : 00110001 rrrr0000 %nota[15:0] 00100011 rrrr0000 0000rrrr 00000000 01001000 rrrrffff 0000rrrr 00000000 ; Last 64k +when !(%a & 0x0f) && %a < 0xffff0: 00110001 rrrr0000 %a[19:4] 00100111 rrrrrrrr 00000000 00000100 01001000 rrrrffff 0000rrrr 00000000 ; 16-byte aligned +when !(%a & 0x03) && %a < 0x3fffc: 00110001 rrrr0000 %a[17:2] 00100111 rrrrrrrr 00000000 00000010 01001000 rrrrffff 0000rrrr 00000000 ; 4-byte aligned +00110001 rrrr0000 %a[31:16] 00100111 rrrrrrrr 00000000 00010000 00110001 rrrrrrrr %a[15:0] 01001000 rrrrffff 0000rrrr 00000000 ; Any 32-bit +# Jump and link to 32-bit label + +halt%c(condition) +1000cccc 00001111 00000000 00000000 +# Halt on condition %c From 2f09320fa56c3eca48810371c39e66f925f14c9b Mon Sep 17 00:00:00 2001 From: PleegWat Date: Sat, 5 Sep 2026 15:39:55 +0200 Subject: [PATCH 2/6] Add optimization for arbitrary aligned values Any value with 16 or less significant bits but larger than 64k is loaded in one fewer cycle --- isa/test.isa | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/isa/test.isa b/isa/test.isa index 5aca729..f030f08 100644 --- a/isa/test.isa +++ b/isa/test.isa @@ -332,11 +332,12 @@ ret 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 when 0x0000 <= %a && %a <= 0xffff: 00110001 rrrr0000 %a[15:0] ; First 64k when %nota <= 0xffff : 00110001 rrrr0000 %nota[15:0] 00100011 rrrr0000 0000rrrr 00000000 ; Last 64k -when !(%a & 0x0f) && %a < 0xffff0: 00110001 rrrr0000 %a[19:4] 00100111 rrrrrrrr 00000000 00000100 ; 16-byte aligned -when !(%a & 0x03) && %a < 0x3fffc: 00110001 rrrr0000 %a[17:2] 00100111 rrrrrrrr 00000000 00000010 ; 4-byte aligned +when %sa < 0xffff : 00110001 rrrr0000 %sa[15:0] 00100011 rrrrrrrr 00000000 %s[7:0] ; Aligned 00110001 rrrr0000 %a[31:16] 00100111 rrrrrrrr 00000000 00010000 00110001 rrrrrrrr %a[15:0] ; Any 32-bit # Load 32-bit immediate %a into %r @@ -345,14 +346,16 @@ qcall %a:U32(label) ; assert(%a <= 0xffffffff, "%a is too high") %r = 13 %f = 15 +%s = trailing_zeros(%a) +%sa = %a >> %s %nota = ~%a when 0x0000 <= %a && %a <= 0xffff: 01011000 rrrrffff %a[15:0] ; First 64k when %nota <= 0xffff : 00110001 rrrr0000 %nota[15:0] 00100011 rrrr0000 0000rrrr 00000000 01001000 rrrrffff 0000rrrr 00000000 ; Last 64k -when !(%a & 0x0f) && %a < 0xffff0: 00110001 rrrr0000 %a[19:4] 00100111 rrrrrrrr 00000000 00000100 01001000 rrrrffff 0000rrrr 00000000 ; 16-byte aligned -when !(%a & 0x03) && %a < 0x3fffc: 00110001 rrrr0000 %a[17:2] 00100111 rrrrrrrr 00000000 00000010 01001000 rrrrffff 0000rrrr 00000000 ; 4-byte aligned +when %sa < 0xffff : 00110001 rrrr0000 %sa[15:0] 00100011 rrrrrrrr 00000000 %s[7:0] 01001000 rrrrffff 0000rrrr 00000000 ; Aligned 00110001 rrrr0000 %a[31:16] 00100111 rrrrrrrr 00000000 00010000 00110001 rrrrrrrr %a[15:0] 01001000 rrrrffff 0000rrrr 00000000 ; Any 32-bit # Jump and link to 32-bit label halt%c(condition) 1000cccc 00001111 00000000 00000000 # Halt on condition %c + From 4c0cd79fdd1726bfdcf1b080178224306ae90dc5 Mon Sep 17 00:00:00 2001 From: PleegWat Date: Sat, 5 Sep 2026 16:37:51 +0200 Subject: [PATCH 3/6] Fix several bugs --- isa/test.isa | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/isa/test.isa b/isa/test.isa index f030f08..a994441 100644 --- a/isa/test.isa +++ b/isa/test.isa @@ -334,11 +334,11 @@ li %r(register), %a:S33(immediate | label) ; assert(%a <= 0xffffffff, "%a is too high") %s = trailing_zeros(%a) %sa = %a >> %s -%nota = ~%a -when 0x0000 <= %a && %a <= 0xffff: 00110001 rrrr0000 %a[15:0] ; First 64k -when %nota <= 0xffff : 00110001 rrrr0000 %nota[15:0] 00100011 rrrr0000 0000rrrr 00000000 ; Last 64k -when %sa < 0xffff : 00110001 rrrr0000 %sa[15:0] 00100011 rrrrrrrr 00000000 %s[7:0] ; Aligned -00110001 rrrr0000 %a[31:16] 00100111 rrrrrrrr 00000000 00010000 00110001 rrrrrrrr %a[15:0] ; Any 32-bit +%nota = %a ^ 0xffffffff ; Needs xor; ~ operator does not generate leading high bits +when %a <=u 0xffff : 00110001 %r[3:0]0000 %a[15:0] ; First 64k +when %nota <= 0xffff: 00110001 %r[3:0]0000 %nota[15:0] 00100011 %r[3:0]0000 0000%r[3:0] 00000000 ; Last 64k +when %sa <= 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) @@ -348,14 +348,13 @@ qcall %a:U32(label) %f = 15 %s = trailing_zeros(%a) %sa = %a >> %s -%nota = ~%a -when 0x0000 <= %a && %a <= 0xffff: 01011000 rrrrffff %a[15:0] ; First 64k -when %nota <= 0xffff : 00110001 rrrr0000 %nota[15:0] 00100011 rrrr0000 0000rrrr 00000000 01001000 rrrrffff 0000rrrr 00000000 ; Last 64k -when %sa < 0xffff : 00110001 rrrr0000 %sa[15:0] 00100011 rrrrrrrr 00000000 %s[7:0] 01001000 rrrrffff 0000rrrr 00000000 ; Aligned -00110001 rrrr0000 %a[31:16] 00100111 rrrrrrrr 00000000 00010000 00110001 rrrrrrrr %a[15:0] 01001000 rrrrffff 0000rrrr 00000000 ; Any 32-bit +%nota = %a ^ 0xffffffff ; Needs xor; ~ operator does not generate leading high bits +when %a <=u 0xffff : 01011000 %r[3:0]ffff %a[15:0] ; First 64k +when %nota <= 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 <= 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 - From dd20822ee11e38f77f1075122dfb73a3c543a0cc Mon Sep 17 00:00:00 2001 From: PleegWat Date: Sat, 5 Sep 2026 16:41:52 +0200 Subject: [PATCH 4/6] Use all unsigned compares --- isa/test.isa | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/isa/test.isa b/isa/test.isa index a994441..45b78ec 100644 --- a/isa/test.isa +++ b/isa/test.isa @@ -334,11 +334,11 @@ li %r(register), %a:S33(immediate | label) ; assert(%a <= 0xffffffff, "%a is too high") %s = trailing_zeros(%a) %sa = %a >> %s -%nota = %a ^ 0xffffffff ; Needs xor; ~ operator does not generate leading high bits -when %a <=u 0xffff : 00110001 %r[3:0]0000 %a[15:0] ; First 64k -when %nota <= 0xffff: 00110001 %r[3:0]0000 %nota[15:0] 00100011 %r[3:0]0000 0000%r[3:0] 00000000 ; Last 64k -when %sa <= 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 +%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) @@ -348,13 +348,14 @@ qcall %a:U32(label) %f = 15 %s = trailing_zeros(%a) %sa = %a >> %s -%nota = %a ^ 0xffffffff ; Needs xor; ~ operator does not generate leading high bits -when %a <=u 0xffff : 01011000 %r[3:0]ffff %a[15:0] ; First 64k -when %nota <= 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 <= 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 +%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 + From 6f7ad3aa4bd9bc4d69d05162ab823ddc24ae3178 Mon Sep 17 00:00:00 2001 From: PleegWat Date: Wed, 2 Sep 2026 21:11:45 +0200 Subject: [PATCH 5/6] Implement atoi Parse strings to integer (binary, octal, decimal, or hexadecimal) --- src/stdlib.asm | 1 + src/strint.asm | 188 ++++++++++++++++++++++++++++++++++++++++++ tests/strint_test.asm | 57 +++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 src/strint.asm create mode 100644 tests/strint_test.asm diff --git a/src/stdlib.asm b/src/stdlib.asm index 512c6c3..31fb521 100644 --- a/src/stdlib.asm +++ b/src/stdlib.asm @@ -3,6 +3,7 @@ pub include imath pub include array pub include console pub include mem +pub include strint ; Needs to be last! pub include LUTs diff --git a/src/strint.asm b/src/strint.asm new file mode 100644 index 0000000..752b74f --- /dev/null +++ b/src/strint.asm @@ -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 diff --git a/tests/strint_test.asm b/tests/strint_test.asm new file mode 100644 index 0000000..3e7ccb2 --- /dev/null +++ b/tests/strint_test.asm @@ -0,0 +1,57 @@ +; test harness for strint.atoi +; 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.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, 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: + From 304fe5483390b861fcd860598722fc0e2a249cf6 Mon Sep 17 00:00:00 2001 From: PleegWat Date: Fri, 4 Sep 2026 23:53:46 +0200 Subject: [PATCH 6/6] Apply agreed naming conventions --- src/stdlib.asm | 2 +- src/{strint.asm => string_to_int.asm} | 76 +++++++++---------- ...strint_test.asm => string_to_int_test.asm} | 4 +- 3 files changed, 41 insertions(+), 41 deletions(-) rename src/{strint.asm => string_to_int.asm} (83%) rename tests/{strint_test.asm => string_to_int_test.asm} (95%) diff --git a/src/stdlib.asm b/src/stdlib.asm index 31fb521..ee6477d 100644 --- a/src/stdlib.asm +++ b/src/stdlib.asm @@ -3,7 +3,7 @@ pub include imath pub include array pub include console pub include mem -pub include strint +pub include string_to_int ; Needs to be last! pub include LUTs diff --git a/src/strint.asm b/src/string_to_int.asm similarity index 83% rename from src/strint.asm rename to src/string_to_int.asm index 752b74f..cd961c2 100644 --- a/src/strint.asm +++ b/src/string_to_int.asm @@ -4,7 +4,7 @@ ; r3 - Character being parsed ; r4 - Set to -1 if the input is negative -; Convert string to integer +; Convert string to integer (akin to libc atoi(), strotoi()) ; Arguments: ; r1 - Pointer to string ; Result: @@ -15,30 +15,30 @@ ; r3 - last character read ; r4 - Negative marker ; Note: Unless the input is "0", tail-calls into a base-specific specialization. -pub atoi: +pub auto: mov r2, r1 mov r1, 0 mov r4, 0 load_8 r3, [r2] cmp r3, 0x2D ; '-' -jne atoi_positive +jne auto_positive sub r4, zr, 1 ; Set r4 to -1 add r2, r2, 1 -atoi_positive: +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 atoi_bin_loop +je bin_loop cmp r3, 0x306F ; "0o" -je atoi_oct_loop +je oct_loop cmp r3, 0x3078 ; "0x" -je atoi_hex_loop +je hex_loop sub r2, r2, 2 ; no matching prefix, move pointer back -jmp atoi_dec_loop +jmp dec_loop ; Epilogue -atoi_done: +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 @@ -54,27 +54,27 @@ jmp r13 ; flags ; r3 - last character read ; r4 - Negative marker -pub atoi_dec: +pub dec: mov r2, r1 mov r1, 0 mov r4, 0 load_8 r3, [r2] cmp r3, 0x2D ; '-' -jne atoi_dec_positive +jne dec_positive sub r4, zr, 1 ; Set r4 to -1 add r2, r2, 1 -atoi_dec_loop: +dec_loop: load_8 r3, [r2] -atoi_dec_positive: +dec_positive: sub r3, r3, 0x30 ; '0' cmp r3, 9 -ja atoi_done +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 atoi_dec_loop +jmp dec_loop ; Convert binary string to integer ; Arguments: @@ -86,30 +86,30 @@ jmp atoi_dec_loop ; flags ; r3 - last character read ; r4 - Negative marker -pub atoi_bin: +pub bin: mov r2, r1 mov r1, 0 mov r4, 0 load_8 r3, [r2] cmp r3, 0x2D ; '-' -jne atoi_bin_positive +jne bin_positive sub r4, zr, 1 ; Set r4 to -1 add r2, r2, 1 -atoi_bin_positive: +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 +jne bin_loop add r2, r2, 2 -atoi_bin_loop: +bin_loop: load_8 r3, [r2] sub r3, r3, 0x30 ; '0' cmp r3, 1 -ja atoi_done +ja done add r2, r2, 1 lsl r1, r1, 1 add r1, r1, r3 -jmp atoi_bin_loop +jmp bin_loop ; Convert octal string to integer ; Arguments: @@ -121,30 +121,30 @@ jmp atoi_bin_loop ; flags ; r3 - last character read ; r4 - Negative marker -pub atoi_oct: +pub oct: mov r2, r1 mov r1, 0 mov r4, 0 load_8 r3, [r2] cmp r3, 0x2D ; '-' -jne atoi_oct_positive +jne oct_positive sub r4, zr, 1 ; Set r4 to -1 add r2, r2, 1 -atoi_oct_positive: +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 +jne oct_loop add r2, r2, 2 -atoi_oct_loop: +oct_loop: load_8 r3, [r2] sub r3, r3, 0x30 ; '0' cmp r3, 7 -ja atoi_done +ja done add r2, r2, 1 lsl r1, r1, 3 add r1, r1, r3 -jmp atoi_oct_loop +jmp oct_loop ; Convert hexadecimal string to integer ; Arguments: @@ -156,33 +156,33 @@ jmp atoi_oct_loop ; flags ; r3 - last character read ; r4 - Negative marker -pub atoi_hex: +pub hex: mov r2, r1 mov r1, 0 mov r4, 0 load_8 r3, [r2] cmp r3, 0x2D ; '-' -jne atoi_hex_positive +jne hex_positive sub r4, zr, 1 ; Set r4 to -1 add r2, r2, 1 -atoi_hex_positive: +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 +jne hex_loop add r2, r2, 2 -atoi_hex_loop: +hex_loop: load_8 r3, [r2] sub r3, r3, 0x30 ; '0' cmp r3, 10 -jb atoi_hex_add +jb 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 +ja done add r3, r3, 10 ; Adjust for digits below -atoi_hex_add: +hex_add: add r2, r2, 1 lsl r1, r1, 4 add r1, r1, r3 -jmp atoi_hex_loop +jmp hex_loop diff --git a/tests/strint_test.asm b/tests/string_to_int_test.asm similarity index 95% rename from tests/strint_test.asm rename to tests/string_to_int_test.asm index 3e7ccb2..c60494b 100644 --- a/tests/strint_test.asm +++ b/tests/string_to_int_test.asm @@ -1,4 +1,4 @@ -; test harness for strint.atoi +; test harness for string_to_int ; Registers: ; r8 - test address ; r9 - expected result @@ -10,7 +10,7 @@ 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 +jmp stdlib.string_to_int.auto atoi_test_ret: ; Load reference data