Saturday 22 February 2020

Instruction Set Architecture

The last couple of weeks I experimented a bit with details of the instruction set and the final design is shown below. We settled for just 13 instructions, although the alu supports about 15 different operations so 28 instructions would be an equally valid number.

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.


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.

movemove r2,r1,r0r2 = r1 + r0move sum of source registers to destination
poppop r2r2 = (sp) ; sp += 4pop top of stack to destination register
pushpush r2sp -= 4; (sp) = r2push register onto stack
alualu r2,r1,r0r2 = r1 op r0perform alu operation (op = r[13][7:0])
movermover r2,r1,nr2 = r1 + 4*nadd multiple of 4, n = [-8,7]
storstor r2,r1,r0(r1 + r0) = r2[7:0]store byte in memory
storlstorl r2,r1,r0(r1 + r0) = r2store long word in memory
loadload r2,r1,r0r2[7:0] = (r1 + r0)load byte from memory
loadlloadl r2,r1,r0r2 = (r1 + r0)load long word from memory
loadiloadi r2,#nr2[7:0] = nload byte immediate, n = 8b bit value
loadilloadil r2,#nr2 = (pc +2); pc+=4load long word immediate
jaljal r2,r1,r0r2 = pc; pc = r1+r0jump and link
halthalthalt execution
setbXXsetbXX r1,offsee belowset and branch on condition XX


  • 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)

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
Shift right, Shift left
Cmp and Test
Mul (high and low 32 bits)
Div and rem (signed and unsigned)

CPU design

The CPU design as currently implemented largely follows the diagram shown below. It features a 16 x 32bit register file and 16 bit instructi...