All instructions are 2 bytes long, with the exception of loadil (load immediate long) which is 6 bytes and the set and branch instruction which is 4 bytes.
Encoding
Instructions consist of 4 fields each 4 bits wide. The first field is the opcode, the other 3 fields typically specify registers and are called r2,r1 and r0 respectively, although there are exceptions. For loadi (load immediate) r1 and r0 together encode a byte value and for set and branch r2 encodes the condition.
move | move r2,r1,r0 | r2 = r1 + r0 | move sum of source registers to destination |
pop | pop r2 | r2 = (sp) ; sp += 4 | pop top of stack to destination register |
push | push r2 | sp -= 4; (sp) = r2 | push register onto stack |
alu | alu r2,r1,r0 | r2 = r1 op r0 | perform alu operation (op = r[13][7:0]) |
mover | mover r2,r1,n | r2 = r1 + 4*n | add multiple of 4, n = [-8,7] |
stor | stor r2,r1,r0 | (r1 + r0) = r2[7:0] | store byte in memory |
storl | storl r2,r1,r0 | (r1 + r0) = r2 | store long word in memory |
load | load r2,r1,r0 | r2[7:0] = (r1 + r0) | load byte from memory |
loadl | loadl r2,r1,r0 | r2 = (r1 + r0) | load long word from memory |
loadi | loadi r2,#n | r2[7:0] = n | load byte immediate, n = 8b bit value |
loadil | loadil r2,#n | r2 = (pc +2); pc+=4 | load long word immediate |
jal | jal r2,r1,r0 | r2 = pc; pc = r1+r0 | jump and link |
halt | halt | halt execution | |
setbXX | setbXX r1,off | see below | set and branch on condition XX |
Notes
- Loading a byte from memory or immediately does not sign extend it. This means that a destinations register may need to be zeroed out before loading the byte.
- The alu operation performs the operation stored in the lower byte of R13. This means that choosing the operation and actually performing it are two separate steps. You can reuse this operation, if performing multiple additions for example there is no need to reload R13
- R13 is also the flags register: bits 31 is always set while bit 30 and 29 are the sign and zero bit respectively.
- The alu does not calculate a carry or borrow
Set and branch
The set and branch instruction acts on flags in R13 and can be used to set a destination register (selected by field r1) to zero or one based on whether a specific flag is set. If this condition is true, it then adds a 16 bit offset to the program counter (i.e. branches).
If you only want to branch r0 or r2 can be used as the destination register as these are immutable. Likewise if you only want to set a register without branching, a zero offset can be used. The assembler implements these variants with macros: bra, beq, bne, brp and brm for the (un)conditional branches and seteq, setne, setpos and setmin for the conditional set instructions.
The technical implementation of the branch condition is
R13[31:29] & cond[2:0] == cond[3] & cond[2:0]
Where cond is specified by the r2 field of the instruction.
This means that the lower 3 bits of cond select the flag(s) to test while the upper bit determines whether the flag should be set or unset. Because R13[31] is always on we also have the option to unconditionally execute the instruction (or never, if we have cond[3]==0)
R1 en r0 are immutable and hardcoded to hold 1 and 0 respectively.
R13 is the flags and alu operation register.
R14 is the stackpointer targeted by the pop and push instructions.
R15 is the program counter (PC, a.k.a. instruction pointer).
Add and Sub
And, Or and Xor
Not
Shift right, Shift left
Cmp and Test
Mul (high and low 32 bits)
Div and rem (signed and unsigned)
Special registers
R1 en r0 are immutable and hardcoded to hold 1 and 0 respectively.
R13 is the flags and alu operation register.
R14 is the stackpointer targeted by the pop and push instructions.
R15 is the program counter (PC, a.k.a. instruction pointer).
Supported alu operations
Add and Sub
And, Or and Xor
Not
Shift right, Shift left
Cmp and Test
Mul (high and low 32 bits)
Div and rem (signed and unsigned)