Set
Rationale
The set
macro allows you to ergonomically store values directly into registers.
In simple programs you will just store your values in the constant registers and use them as normal, an example of this might be:
.memory
0000 1111 2222 3333
4444 5555 6666 7777
.code
mov r0, c0 ; Set r0 to [0000, 1111, 2222, 3333]
wmov r1, c1.z ; Set r1.x to 6666
However there are situations where you may need more values than can fit into the constant registers.
One way to solve this problem might be to store a larger set of values in other memory regions, or constructing a series of programs to load chunks of data elsewhere, and then load the actual program afterwards.
Alternatively, the CPU has a convenient trick we can use for loading values into registers directly from the program memory.
The Program Counter
Since the program counter contains the value of the proceeding instruction to be executed, we can take advantage of this and use it as a pointer for the next instruction as a literal value to load from. For example:
mov r0.x, [ri.x]
nop
Since the nop
instruction has the literal value 0x0010
, this will load the value 0x0010
into r0.x
and then execute the nop
instruction. However this limits us to values that are valid instructions, and also has the side effect of executing instructions we may not want executed.
Luckily we have a trick we can leverage to avoid this problem, and that is the increment load/store instructions. When using a value as a pointer, we can increment that value after using it. In this example we can avoid executing the nop
by incrementing the program counter after we use it as a pointer:
mov r0.x, [ri.x+] ; The + here means ri.x will be incremented after reading
nop ; This will now be skipped
This now means that the value in this location does not need to be a valid instruction.
We can use the immediate value syntax to store arbitrary data in program memory:
mov r0.x, [ri.x+]
!b0fa
; Now r0.x = 0xb0fa
The set macro can make this more ergonomic. The following compiles to the same bytecode:
set r0, $b0fa
Additionally we can take advantage of the SIMD gather instructions to similar effect:
mov r0.xyz [ri.x+]
!1111
!2222
!3333
; Equivalent to:
set3 r0, $1111, $2222, $3333
Labels as Data
In addition to setting immediate values, we can also take advantage of the assembler's preprocessor to use the addresses of labels as values at runtime.
set r4, :some_label ; r4.x now contains the address of :some_label
This can be useful for adding offsets to jump locations, or even rewriting jumps when copying code into a new memory location (such as when copying a program into shared memory) among other uses.