38 Commits
Author SHA1 Message Date
Michał Isalski cdbbd9e20f Made bitblt work in 32bpp mode 2026-09-11 01:16:53 +02:00
Michał Isalski b30a627d0a Added missing errno 2026-09-07 01:47:15 +02:00
Michał Isalski e8d8eddeb6 Merge branch 'main' into bitblt 2026-09-07 01:46:43 +02:00
Michał Isalski ad8fd7e68e Finished bitblit function - to be tested! 2026-09-07 01:44:51 +02:00
ShatteredMINT e654d451a7 Merge pull request 'Globals and start.asm' (#19) from Gelthor/symphony_stdlib:graphics into main
Reviewed-on: TCShenanigans/symphony_stdlib#19
2026-09-06 20:13:05 +02:00
Gelthor 4801520424 Split framebuffer stride into stride and depth
Stride is now U16, depth is U16, and stores the bits per pixel of the
buffer.
2026-09-06 17:37:49 +01:00
Gelthor 1ba70f10cd Merge remote-tracking branch 'origin/main' into graphics 2026-09-06 16:26:55 +01:00
Gelthor ca5cff0a79 Function premble should list error codes, if any 2026-09-06 16:25:40 +01:00
Gelthor 7368044335 Global addresses now in hex and listed in order 2026-09-06 15:49:23 +01:00
Gelthor 21deca64fd Remove graphics code from this PR 2026-09-06 15:48:37 +01:00
ShatteredMINT 590732cd48 Merge pull request 'Initial version of atoi' (#15) from PleegWat/symphony_stdlib:str_to_int into main
Reviewed-on: TCShenanigans/symphony_stdlib#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: TCShenanigans/symphony_stdlib#18
2026-09-06 16:08:12 +02:00
Gelthor b63af2f15a merge upstream 2026-09-06 12:14:49 +02:00
Gelthor 46abf8d893 Add globals and error codes
Added support for global variables.

This requires including `start.asm` as the first code. This includes
a `jmp` to 0x100 for user code, and a magic number that modules
can check to verify th the user has correctly reserved the space.

The magic number is 0xb301534c:

* This has the high bit set so is not a valid instruction

* On builds that ignore the high bit this decodes to `nor zr, zr,
  0x534c` a no-op.

* The immediate is "SL" for standardl ibrary.

Due to a bug start pads with U... 0, instead of @addr.

`start.asm` also initialises `sp` so it start just below the 64KiB LUT
memory alocation.

Added some error codes for the upcoming graphics module.
2026-09-06 11:02:27 +01: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: TCShenanigans/symphony_stdlib#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: TCShenanigans/symphony_stdlib#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
Gelthor 04f3f03ad5 Moved new *.asm files into src/
v
2026-09-04 16:42:35 +01:00
Gelthor 39ff477f6e Merge remote-tracking branch 'gelthor' into graphics 2026-09-04 16:09:51 +01:00
Gelthor 4d752ef67c Added initial draft of globals and errno 2026-09-04 16:07:24 +01: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: TCShenanigans/symphony_stdlib#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
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
MutexRaceCondition 7b43e75a9d Added memcmp, memcpy, memset32, unsafe.memcmp 2026-09-02 19:41:56 +02:00
15 changed files with 1628 additions and 17 deletions
+13 -1
View File
@@ -16,10 +16,22 @@ All functions in the standard library should follow the following outline:
; Arguments: <which register contains what argument>
; Result: <what is the result, and where is it stored>
; Clobbers: <list of registers that are clobbered>
; Globals: <list of globals are accessed. OPTIONAL>
; Errors: <list of any status codes returned in flags. OPTIONAL>
<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
Functions that are provided for convenience/reference but should be inlined in production code should be marked with `;SHOULD BE INLINED` after their label.
If the function returns a status code in flags, the preamble should list all it might return.
## Globals
Any function that uses global variables should document this in the preamble comment, see above.
A file/module should check on intialisation that the address `globals.MAGIC_ADDRESS` contains the 16 bit value `globals.MAGIC_VALUE`, to ensure that the user has properly included [[src/start.asm]] and reservered the global variable area.
No opinion is offered on whether modules can assume globals variables are initialised to zero.
+7 -2
View File
@@ -3,7 +3,7 @@
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 just want to use the standard library [[src/stdlib.asm]] is your main header, include it after your code. Some modules also require [[src/start.asm]] at the very start of the program.
If you are using it as a learning resource have a look at the [teaching folder](teaching).
@@ -20,7 +20,7 @@ If you are intersted in contributing have a look at [[CONTRIBUTING.md]]
| preserved | sp, r8 - r12 |
| scratch | flags, r1 - r7 |
| arguments | r1 - r7 |
| result | r1-r7 |
| result | flags, 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.
@@ -32,9 +32,14 @@ Should a function return more values than fit into the 7 registers, the caller h
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.
Some functions return a success/failure status code in flags. A set low bit will indicate some error condition, the function may return more than one possible value to report different errors. A flags value of 0 is set on success. No other even values are used. This can be checked with `je` or `jne` immediatly on return to the caller. All other functions clobber flags.
### Stack
Grows downwards from 0xXXFE_0000 (so top of memory -0x1_0000).
### Globals
Global variables are stored near the bottom of RAM in the address range 0x0010..0x0100. Programs should `include src/start` as the first line before their own code and before other includes, or otherwise reserve this space, the first instruction should be a jump to user code.
### Types
#### String
+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
Executable
+28
View File
@@ -0,0 +1,28 @@
; Error numbers should be 16 bit ODD numbers, so they can be loaded as immediates
; and can be checked with:
;
; qcall falible_function
; jne falible_ok ; Jump no error
; ; handle error
; falible_ok:
; ; Happy path
; ; ...
; OR
; qcall falible_function
; je handle_error ; Jump if error
; ; Happy path
; ; ...
; handle_error:
; ; handle error
pub const OK = 0x0000
pub const SCREEN_INVALID_MODE = 0x0001
pub const SCREEN_INVALID_WIDTH = 0x0003
pub const SCREEN_FB_TOO_SMALL = 0x0005
pub const SCREEN_OUTSIDE_FB = 0x0007
pub const BUFFER_DEPTH_MISMATCH= 0x0009
pub const MAGIC_BAD = 0x8001
; Extended error codes, these would require loading a 32 bit value.
pub const EXT_OK = 0x00000000
+30
View File
@@ -0,0 +1,30 @@
; Addresses of global variables
pub const MAGIC_ADDRESS_LONG = 0x0c ; U32 Location of the magic value
pub const MAGIC_VALUE_LONG = 0xb301534c ; U32 Full 32 bits of the magic value
pub const MAGIC_ADDRESS = 0x0e ; U16 Location of the low 16 bits of magic value
pub const MAGIC_VALUE = 0x534c ; U16 Low 16 bits of the magic value
; If not double buffering these point to the same buffer
; If double buffering they must the same size
pub const FB_DISPLAY_PTR = 0x10 ; U32 Currently displayed buffer
pub const FB_DISPLAY_STRIDE = 0x14 ; U16 Bytes in each row of the displayed buffer
pub const FB_DISPLAY_DEPTH = 0x16 ; U16 Bits per pixel of the display buffer
pub const FB_DRAW_PTR = 0x18 ; U32 Draw to this buffer
pub const FB_DRAW_STRIDE = 0x1c ; U16 Bytes in each row of the draw buffer == FB_DISPLAY_STRIDE
pub const FB_DRAW_DEPTH = 0x1e ; U16 Bits per pixel of the draw buffer == FB_DISPLAY_DEPTH
pub const FB_SIZE_BYTE = 0x20 ; U32 In bytes
pub const FB_WIDTH_PX = 0x24 ; U16 Width of screen in pixels
pub const FB_HEIGHT_PX = 0x26 ; U16 Height of screen in pixels
; Log2 of width in pixels, e.g.
; * 10 => 1024 * 768
; * 8 => 256 * 192
pub const FB_LOG_WIDTH = 0x28 ; U8
pub const FB_LOG_STRIDE = 0x29 ; U8 Log2 of FB_xxx_STRIDE in bytes
pub const FB_BYTES_PER_PIXEL = 0x2a ; U8 Specialisations should hardcode this
; Log2 of bytes per pixel
; * 0 => 8 bits per pixel
; * 2 => 32 bits per pixel
pub const FB_LOG_BPP = 0x2b ; U8 Specialisations should hardcode this
+268
View File
@@ -0,0 +1,268 @@
include errno
; Performs a Bitblock transfer, copying a section of the Source to the Destination, applying a given Mode operation
; Arguments:
; r1 - Destination Buffer pointer
; r2 - Source Buffer pointer
; r3 - X destination coordinate
; r4 - Y destination coordinate
; r5 - X source coordinate
; r6 - Y source coordinate
; r7 - Mode:
; 000 - SRCCOPY (copies source over destination)
; 001 - XOR
; 010 - AND
; 011 - NOT
; 100 - OR
; 101 - reserved
; 110 - reserved
; 111 - SRCALPHA (copies source over destination if: source != 0 (for 8bpp), source.alpha != 0 (for 32bpp))
; Stack argument 1: Width
; Stack argument 2: Height
; Result: None
; Clobbers: r1, r2, r3, r4, r5, r6, r7
; Errors: BUFFER_DEPTH_MISMATCH - if the buffers have different bit depths
pub bitblit:
push r8
push r9
; Checking if the bit depths are correct
add r8, r1, 6 ; r8 = pointer to destination depth
load_16 r8, [r8] ; r8 = dest depth
add r9, r2, 6 ; r9 = pointer to source depth
load_16 r9, [r9] ; r9 = src depth
cmp r8, r9
je bitblit_depth_good
; Bit depths differ - error
pop r9
pop r8
add sp, sp, 8 ; Removing the stack Arguments
mov flags, errno.BUFFER_DEPTH_MISMATCH
jmp r13
bitblit_depth_good:
push r8
push r10
push r11
push r12
push r13
push r7
mov r7, r8
lsr r7, r7, 4
add r10, r1, 4 ; r10 = pointer to destination stride
load_16 r10, [r10] ; r10 = destination stride
lsl r10, r10, r7
add r11, r2, 4 ; r11 = pointer to source stride
load_16 r11, [r11] ; r11 = source stride
lsl r11, r11, r7
add sp, sp, 32
load_32 r8, [sp] ; r8 = height
add sp, sp, 4
load_32 r9, [sp] ; r9 = width
lsl r9, r9, r7
sub sp, sp, 36
; Calculating destination index
push r10
mov r12, 0
bitblit_destination_index:
cmp r4, zr
je bitblit_after_destination_index
mov flags, r4
jne bitblit_destination_index_afteradd
add r12, r12, r10
bitblit_destination_index_afteradd:
lsr r4, r4, 1
lsl r10, r10, 1
jmp bitblit_destination_index
bitblit_after_destination_index:
add r4, r4, r12 ; Now r4 is dest_y * dest_stride
load_32 r1, [r1] ; r1 = destination data pointer
add r1, r1, r4 ; Now r1 is dest_pointer + dest_y * dest_stride
lsl r3, r3, r7
add r1, r1, r3 ; Now r1 is dest_pointer + dest_y * dest_stride + dest_x
pop r10
push r11
; Calculating source index
mov r12, 0
bitblit_source_index:
cmp r6, zr
je bitblit_after_source_index
mov flags, r6
jne bitblit_source_index_afteradd
add r12, r12, r11
bitblit_source_index_afteradd:
lsr r6, r6, 1
lsl r11, r11, 1
jmp bitblit_source_index
bitblit_after_source_index:
add r6, r6, r12 ; Now r6 is src_y * src_stride
load_32 r2, [r2] ; r2 = source data pointer
add r2, r2, r6 ; Now r2 is src_pointer + src_y * src_stride
lsl r5, r5, r7
add r2, r2, r5 ; Now r2 is src_pointer + src_y * src_stride + src_x
pop r11
pop r7
; r3, r4, r5, r6 are now free
mov r3, bitblit_mode
mov r12, bitblit_end_mode
lsl r7, r7, 3 ; 2 instructions per mode
add r12, r7, r12
add r7, r7, r3
; r1 is line dest pointer (or bit depth)
; r2 is line src pointer (or temp)
; r3 is width iterator
; r4 is current dest pointer
; r5 is current src pointer
; r6 is current src value
; r7 is 32bit mode jump table destination
; r8 is height iterator
; r9 is width of bitblit
; r10 is dest stride
; r11 is src stride
; r12 is 8bit mode jump table destination
; r13 is current dest value
bitblit_copy_height:
cmp r8, zr
je bitblit_finish
bitblit_copy_line:
mov r3, 0
mov r4, r1 ; r4 = dest
mov r5, r2 ; r5 = src
push r1
add r1, sp, 20 ; Pointing to bit depth saved on stack
load_32 r1, [r1] ; r1 = bit depth
push r2
bitblit_copy_line_loop: ; Main loop that copies 4 bytes at a time
add r3, r3, 4
cmp r9, r3
jb bitblit_copy_line_end
load_32 r6, [r5] ; r6 = [src]
load_32 r13, [r4] ; r13 = [dest]
jmp r7
bitblit_mode:
nop ; SRCCOPY
jmp bitblit_save_bits
xor r6, r6, r13 ; XOR
jmp bitblit_save_bits
and r6, r6, r13 ; AND
jmp bitblit_save_bits
not r6, r6 ; NOT
jmp bitblit_save_bits
or r6, r6, r13 ; OR
jmp bitblit_save_bits
nop ; reserved
jmp bitblit_save_bits
nop ; reserved
jmp bitblit_save_bits
cmp r1, 8 ; SRCALPHA
jne bitblit_srcalpha_32 ; If bit depth is 32, we jump to alpha checking
; If bit depth is 8, we need to repack this value
mov r2, 0 ; our mask
lsr flags, r6, 24
cmp flags, zr
jne bitblit_srcalpha_8_1
or r2, r2, 0xFF
bitblit_srcalpha_8_1:
lsl r2, r2, 8
lsr flags, r6, 16
and flags, flags, 0xFF
cmp flags, zr
jne bitblit_srcalpha_8_2
or r2, r2, 0xFF
bitblit_srcalpha_8_2:
lsl r2, r2, 8
lsr flags, r6, 8
and flags, flags, 0xFF
cmp flags, zr
jne bitblit_srcalpha_8_3
or r2, r2, 0xFF
bitblit_srcalpha_8_3:
lsl r2, r2, 8
and flags, r6, 0xFF
cmp flags, zr
jne bitblit_srcalpha_8_4
or r2, r2, 0xFF
bitblit_srcalpha_8_4:
and r13, r13, r2
not r2, r2
and r6, r6, r2
or r6, r6, r13
jmp bitblit_save_bits
bitblit_srcalpha_32:
and flags, r6, 0xFF
cmp flags, zr
jne bitblit_save_bits ; If alpha channel is not zero, we save the value
mov r6, r13 ; If it was zero, we take [dest], i.e. don't overwrite
bitblit_save_bits:
store_32 [r4], r6
cmp r9, r3
je bitblit_copy_line_finish
add r4, r4, 4
add r5, r5, 4
jmp bitblit_copy_line_loop
bitblit_copy_line_end: ; Now we need to handle up to 3 bytes that were not copied
sub r3, r3, 4
bitblit_copy_line_end_loop:
load_8 r6, [r5] ; r6 = [src]
load_8 r13, [r4] ; r13 = [dest]
jmp r12
bitblit_end_mode:
nop ; SRCCOPY
jmp bitblit_save_bits_end
xor r6, r6, r13 ; XOR
jmp bitblit_save_bits_end
and r6, r6, r13 ; AND
jmp bitblit_save_bits_end
not r6, r6 ; NOT
jmp bitblit_save_bits_end
or r6, r6, r13 ; OR
jmp bitblit_save_bits_end
nop ; reserved
jmp bitblit_save_bits_end
nop ; reserved
jmp bitblit_save_bits_end
cmp r6, zr ; SRCALPHA (will happen only in 8bpp)
jne bitblit_save_bits_end
mov r6, r13 ; [src] = 0, so we take [dest], i.e. don't overwrite
bitblit_save_bits_end:
store_8 [r4], r6
add r3, r3, 1
add r4, r4, 1
add r5, r5, 1
cmp r9, r3
ja bitblit_copy_line_end_loop
bitblit_copy_line_finish:
pop r2
pop r1
add r1, r1, r10
add r2, r2, r11 ; Moving dest & src to next line
sub r8, r8, 1
jmp bitblit_copy_height
bitblit_finish:
pop r13
pop r12
pop r11
pop r10
add sp, sp, 4 ; Bit depth skip
pop r9
pop r8
add sp, sp, 8
mov flags, errno.OK
jmp r13
+45 -14
View File
@@ -4,22 +4,53 @@
; r2 - The second value
; Result:
; r1 - The lower 32 bits of the result
; Clobbers: r2, r3, r4, r5
; Clobbers: r2, r3, r4, r5, r6
pub mul_low:
mov r3, 0 ; result
mov r4, 31 ; loop counter
mul_low_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 mul_low_loop
mov r1, r3
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
+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
Executable
+19
View File
@@ -0,0 +1,19 @@
;@0 ; Reserve space for globals
; First initialise the stack pointer
nor sp, zr, 0xffff
; Jump over the globals to user code
jmp 0x100
U32 0 ; 4 bytes
; MAGIC_VALUE
;@0x0c
U32 0xb301534c
; Pad with zeroes since the @addr feature is currently broken.
; * https://discord.com/channels/828292123936948244/1545010596246847568
;
U1920 0 ; 240 bytes
;@0x100 ; start of user code
+2
View File
@@ -2,6 +2,8 @@ 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
+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: