Author: Ryan Crosby
The single board relay computer does not use a predefined set of encoded instructions. Instead, it uses wide instructions with 16 bits of flags, each of which trigger certain functionality within the CPU and/or memory controller. As a result, an instruction decoder is not required, which saves the need for additional relay logic.
An additional benefit of this design is the flexibility it affords in controlling the CPU behaviour. The instructions listed in the reference card and design documentation are only a few of the instructions that can possibly be run on the relay computer.
This document is an attempt to record some additional useful instructions which are not included in the original documentation.
If an instruction is "bb free", the bb value is not used at all. This allows the instruction to also be used as variable storage without affecting its behaviour.
OP CODE | Mnemonic (unofficial) | Operation | bb free |
---|---|---|---|
C080_aabb | imadd aa bb | aa + [bb] → [aa] | No |
C180_aabb | imand aa bb | aa & [bb] → [aa] | No |
8180_aabb | and aa bb | [aa] & [bb] → [aa] | No |
8100_aa00 | clra aa | 0 → [aa] | Yes |
8020_aa00 | inca aa | [aa] + 1 → [aa] | Yes |
0820_aabb | incto aa bb | [aa] + 1 → [bb] | No |
00C0_aabb | altbtoc aa bb | [aa] < [bb] → C | No |
00E0_aabb | alebtoc aa bb | [aa] <= [bb] → C | No |
80D0_aabb | rsbc aa bb | [bb] - [aa] - ~C → [aa] | No |
81C0_aabb | bic aa bb | ~[aa] & [bb] → [aa] | No |
0808_aabb | stjmp aa bb | [aa] → [bb], bb → PC | No |
9808_aabb | outcjmp aa bb | [aa] → serial console, bb → PC | No |
820A_aabb | lsrje aa bb | Shift right [aa], 0 → [aa].7, [aa].0 → C. If new C == 0 then bb → PC | No |
8202_aabb | lsrjo aa bb | Shift right [aa], 0 → [aa].7, [aa].0 → C. If new C == 1 then bb → PC | No |
821A_aabb | rorje aa bb | Shift right [aa], C → [aa].7, [aa].0 → C. If new C == 0 then bb → PC | No |
8212_aabb | rorj0 aa bb | Shift right [aa], C → [aa].7, [aa].0 → C. If new C == 1 then bb → PC | No |
8028_aabb | incjmp aa bb | [aa] + 1 → [aa], bb → PC | No |
802C_aabb | incjcs aa bb | [aa] + 1 → [aa]. If C == 1, bb → PC | No |
8024_aabb | incjcc aa bb | [aa] + 1 → [aa]. If C == 0, bb → PC | No |
C080_aabb
aa + [bb] → [aa]
Stores the value of #aa + [bb] in the [aa] address.
This can be used with address 0x00 to load from an indirect pointer in a single instruction:
st #1, the_ptr ; Prepare the_ptr pointer
the_ptr imadd #0x00, 0 ; LOAD address 0x01 into address 0x00. No need to pre-clear address 0x00.
; Address 0x00 now contains the loaded value
C180_aabb
aa & [bb] → [aa]
Stores the value of immediate aa & [bb] in the [aa] address.
This can be used with address 0xFF to load from an indirect pointer in a single instruction:
st #1, the_ptr ; Prepare the_ptr pointer
the_ptr imand #0xFF, 0 ; LOAD address 0x01 into address 0xFF. No need to pre-clear address 0xFF.
; Address 0xFF now contains the loaded value
8180_aabb
[aa] & [bb] → [aa]
Computes [aa] & [bb] and stores it in [aa]
Useful for performing indirect reads (similar to add), and computing AND at the same time.
st 0x01, ptr ; Prepare indirect fetch pointer
st 0xF0, tmp ; Prepare destination with bitmask
ptr and tmp, 0 ; AND into tmp
8100_aa00
0 → [aa]
This version of clr
does not use the bb value, allowing the instruction to be also used as variable storage.
A useful way to use this is to create a self-clearing variable:
the_var clra the_var, 0
the_var
can now be used as a variable, and will also zero itself when executed. Since many variables need to be initialized to zero anyway, this saves needing to use separate clr
and halt
(skip
) instructions, saving one instruction overall.
8020_aa00
[aa] + 1 → [aa]
This version of inc
does not use the bb value, allowing it to be used as variable storage.
A useful way to use this is to create a self-incrementing variable:
the_var inca the_var, 0
0820_aabb
[aa] + 1 → [bb]
Store the value of [aa] + 1 to [bb].
Effectively the same as:
st aa bb
inc bb
but saves an instruction.
00C0_aabb
[aa] < [bb] → C
If [aa] is less than [bb], carry is set. Else, carry is cleared.
[aa] <= [bb] → C
If [aa] is less than or equal to [bb], carry is set. Else, carry is cleared.
altbtoc
and alebtoc
are useful for quickly comparing numbers and then jumping with jcs
or jcc
, without the need to store the actual subtraction result in any intermediate variable.
It can be used with incjcs
/incjcc
. This is useful for checking whether a pointer has reached some value in a loop, as an alternative to setting up the typical incjne
loop.
st #array_start, arr_ptr
loop
arr_ptr imadd #0x00, 0
; Use value stored in 0x00 here
alebtoc #array_end, arr_ptr ; arr_ptr >= #array_end -> carry set
incjcc arr_ptr, loop ; Loop if arr_ptr < #array_end
Since altbtoc
and alebtoc
just set carry, they can also be used with instructions like adc #0, bb
to conditionally increment a variable without branching.
80D0_aabb
[bb] - [aa] - ~C → [aa]
The same as rsbcto
, but stores the result in [aa].
80D0_aabb
[bb] - [aa] - ~C → [aa]
The same as rsbto
, but stores the result in [aa].
81C0_aabb
~[aa] & [bb] → [aa]
The same as bicto
, but stores the result in [aa].
0808_aabb
[aa] → [bb], bb → PC
Stores the value of [aa] in [bb], then jumps to address bb.
This is useful for implementing a jump table that looks up a value from an index, for example:
st #jmp_table, jmp_target
addto offset, jmp_target ; Offset the jump address into the jump table
; Do the jump
jmp_target jmp 0 ; Indirect jump into jmp_table
jmp_table ; Begin jump table
stjmp #CONSTANT_A, jmp_output
stjmp #CONSTANT_B, jmp_output
stjmp #CONSTANT_C, jmp_output
stjmp #CONSTANT_D, jmp_output
jmp_output nop ; jmp_output stores the jump table result in bb.
9808_aabb
[aa] → serial console, bb → PC
Writes the value of [aa] (or #aa if immediate) to the serial console, and then jumps to the address bb.
This is useful when printing characters to the console in a loop, since it saves an instruction compared to outc
+ jmp
.
820A_aabb
Shift right [aa], 0 → [aa].7, [aa].0 → C. If new C == 0 then bb → PC
Equivalent to lsr
and then jcc
, but in a single instruction. Useful for looping and shifting a bitmask right, until the first 1 bit is shifted out.
8202_aabb
Shift right [aa], 0 → [aa].7, [aa].0 → C. If new C == 1 then bb → PC
Equivalent to lsr
and then jcs
, but in a single instruction. Useful for looping and shifting a bitmask right, until the first 0 bit is shifted out.
821A_aabb
Shift right [aa], C → [aa].7, [aa].0 → C. If new C == 0 then bb → PC
Equivalent to ror
and then jcc
, but in a single instruction.
8212_aabb
Shift right [aa], C → [aa].7, [aa].0 → C. If new C == 1 then bb → PC
Equivalent to ror
and then jcs
, but in a single instruction.
8028_aabb
[aa] + 1 → [aa], bb → PC
Increments [aa] and then unconditionally jumps to bb.
802C_aabb
[aa] + 1 → [aa]. If C == 1, bb → PC
Increments [aa]. If the existing carry flag is set, jumps to [bb].
Note, the existing carry flag is not the ALU carry output. The carry output from the increment is not used in the jump condition.
8024_aabb
[aa] + 1 → [aa]. If C == 0, bb → PC
Increments [aa]. If the existing carry flag is clear, jumps to [bb].
Note, the existing carry flag is not the ALU carry output. The carry output from the increment is not used in the jump condition.