This document is the definitive specification of the QuAC1 instruction set that we will be implementing in this course. If another source contradicts this document, this takes precedance.


  • Minimum addressable unit is 16-bit words
  • 16-bit addressed
  • Total addressable memory is 128 KB (64 k words)


  • Numbers add and subtract according to two’s complement arithmetic.
  • When adding numbers beyond the limits of 16 bits, the value wraps around back to 0 (effectively keeping bits 0–15 and discarding bits 16 and above).


All registers start initalised to 0x0000, and are 16-bits wide.

Code Mnemonic Meaning Behaviour
000 rz Zero Register Always reads as zero, even after being written to.
001 r1 Register 1 General purpose register.
010 r2 Register 2 General purpose register.
011 r3 Register 3 General purpose register.
100 r4 Register 4 General purpose register.
101 fl Flag register See Flags.
110 - Undefined Any operation with this register is undefined.
111 pc Program Counter See Program Counter.
  • rz, fl, and pc may also be described as r0, r5, and r7 respectively.

  • An instruction is allowed to write to rz, however the next time an instruction reads rz it will still read as 0.

  • r1, r2, r3, and r4 are the general purpose registers. You may write to them, and they will store that value. Reading from a general purpose register returns the last value written to them.

Instruction Encoding#

Each instruction in QuAC has one of two formats:

  • Register Operands Format (R-Format), or
  • Immediate Format (I-Format).

These formats describe the general segmentation of an instruction into data fields. These fields are given below.

There’s nothing enforcing future instructions fall into these two formats: R-Format and I-Format only describe the general pattern existing instructions follow. New instructions could follow an entirely different encoding format.

Register Operands Format (R-Format)#

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
op cond rd 0 ra 0 rb

Immediate Format (I-Format)#

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
op cond rd imm8


All Formats#

Name Meaning Description
op Opcode Specifies the operation to perform.
cond Condition Determines whether this instruction executes. See Conditions.
rd Destination / Data Register (not str) Determines which register the result of this instruction is written to, or (for str) is used as the register containing the data to store to memory.2

R-Format only#

Name Meaning Description
0 Literal zero The value is always 0.
ra First Operand / Address Register The register used for the first operand in ALU instructions, or for the address in ldr and str.
rb Second Operand Register The register used for the second operand in ALU instructions. ldr and str set this to 0.

I-Format Only#

Name Meaning Description
imm8 Immediate (8-bit) An immediate, or constant, 8-bit value that is directly encoded into the instruction.

Hardware Instructions#

The following table lists all instructions a hardware implementor of QuAC must handle. The section Pseudo-Instructions lists several more instructions, but the machine code of each additional instruction matches one of the (possibly more general) instructions here. By implementing these, you gain the full pseudo-instruction support ‘for free’.

Every instruction in QuAC can have a condition suffix appended to it. See Conditions for details. The suffix is used to determine the cond bit used in the machine code column.

Syntax Semantic Machine Code
I-Format Instructions
movl rd, imm8 rd ≔ #imm8 0000 <cond> <rd> <imm8>
seth rd, imm8 See below 0001 <cond> <rd> <imm8>
R-Format Memory Instructions
str rd, [ra] [ra] ≔ rd 0100 <cond> <rd> 0 <ra> 0000
ldr rd, [ra] rd ≔ [ra] 0101 <cond> <rd> 0 <ra> 0000
R-Format ALU Instructions
add rd, ra, rb rd ≔ ra + rb 1000 <cond> <rd> 0 <ra> 0 <rb>
sub rd, ra, rb rd ≔ ra - rb 1001 <cond> <rd> 0 <ra> 0 <rb>
and rd, ra, rb rd ≔ ra & rb 1010 <cond> <rd> 0 <ra> 0 <rb>
orr rd, ra, rb rd ≔ ra | rb 1011 <cond> <rd> 0 <ra> 0 <rb>

seth moves an 8-bit constant (imm8) into the high byte of the destination register rd, leaving the low byte of rd unchanged. Formally,

rd ≔ (#imm8 << 8) | (rd & 0xff)

16-bit values that do not correspond to a machine code pattern in this table are undefined instructions. A correct QuAC program will never attempt to execute an undefined instruction. Hardware may act in any way it chooses if a program does.

More details on what each instruction does, and how they affect the flags, can be found here.

Program counter#

The program counter register pc behaves as a general purpose register the same as r1r4 with respect to instructions reading and writing to it.

Its value is also used as the address to fetch the next instruction from. Therefore, reading its value will give you the address of the currently executing instruction. After an instruction is executed pc is incremented by 1 unless pc was written to by the just-finished instruction.3 If that instruction’s condition failed, then pc is still incremented, even if the instruction would have written to it.


15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Undefined V C N Z

The flag register fl stores the 4 flags generated by the ALU whenever any ALU instruction is successfully executed.

  • If an ALU instruction is executed (add, sub, and, or orr) is executed, the flags are updated.
  • If a non-ALU instruction (str, ldr, movl, or seth) is executed, the flags are left unchanged.
  • If an ALU instruction’s condition fails, then the flags are left unchanged.

fl may be read from like any other general purpose register. Writing directly to fl is undefined behaviour.4

The undefined bits impose no restrictions on reads and writes. They may even be changed when performing non-ALU instructions or when an instruction fails its condition.

The rules for setting each flag when executing an ALU operation are as follows

Flag Meaning Description
Z Zero Set if the result is zero.
N Negative Set if the result, viewed as a 2’s complement signed integer, is negative.
C Carry If the instruction is add or sub, then set if the calculation carried. If the instruction is and or orr, then always 0.
V Overflow If the instruction is add or sub, then set if the calculation overflowed. If the instruction is and or orr, then always 0.

See lab 2 for hints on calculating when these conditions occur.


Conditions are fields on an instruction that control whether that instruction is executed or not. Before executing the instruction, the CPU checks the condition on the instruction against the current CPU state.

  • If the condition succeeds, then the instruction is executed like normal.
  • If the condition fails, then the instruction is not executed, acting as if it were a nop.

The QuAC ISA defines two conditions

Name Suffix Encoding Condition Meaning
Always - 0 - Always executes
Equals eq 1 Z == 1 Execute if latest ALU result was zero

The encoding bit is substituted in place of cond in the instruction table.

The names of the conditions are derived from the cmp ra, rb instruction. For example, the equals condition checks for Z == 1 because cmp ra, rb sets the Z flag if and only if ra == rb.

Other conditions can be emulated using these. For example,

; Conditionally execute on C
movl r1, 0b100     ; mask for the carry bit
and rz, fl, r1     ; bitwise AND the mask with the flag
jpeq skip_if_fail  ; jumps if C was not set
nop                ; only executed if C was set

; Conditional jump on 'greater than' condition
; (Greater than is equivalent to Z = 0 and N = 0)
movl r1, 0b11   ; mask for Z and N
and rz, fl, r1  ; bitwise AND the mask with flag
jpeq <label>    ; only executed if Z and N were 0


These instructions are special cases of instructions that already exist in the ISA, but make writing assembly a bit easier. Note how the machine code in each case corresponds to an instruction in the above table. By implementing those base ISA instructions, the pseudo-instructions are obtained for free.

Syntax Meaning Machine Code
mov rd, ra rd ≔ ra 1000 <cond> <rd> 0 <ra> 0000
jpr ra pc ≔ ra 1000 <cond> 111 0 <ra> 0000
cmp ra, rb ra - rb 1001 <cond> 000 0 <ra> 0 <rb>
nop Do nothing 0000 0000 0000 0000
jpm [ra] pc ≔ [ra] 0101 <cond> 111 0 <ra> 0000
jp imm8 pc ≔ imm8 0000 <cond> 111 <imm8>
jp <label> pc ≔ <label> Up to the assembler

The compare instruction cmp is useful for comparing the values between two registers. It is synthesised as a subtraction with rz as the destination register (cmp ra, rb is equivalent to sub rz, ra, rb), which means the result of the subtraction is discarded, but the flags are still set.

cmp r1, r2    ; if r1 == r2:
movleq r3, 1  ;     r3 = 1

  1. Quarel-Akram-Connor, after the designers Shoaib Akram, Jon Connor and David Quarel. 

  2. All instruction in QuAC except for str write a result to a destination register. str uses rd as the data to store to memory. While a little confusing, this makes the implemention in hardware a bit easier to deal with. 

  3. This allows for the synthesis of many other instructions. For instance, we can use the following code to skip over a small piece of code.

    movl r2, 2  ; r2 ≔ 2
    add pc, r2  ; pc jumps two addresses forward
    movl r1, 1  ; this instruction gets skipped
    movl r3, 3  ; this instruction gets executed

    Or (stranger still) to load the machine code of an instruction itself, and use it as data

    ldr r1, [pc]  ; r1 ≔ 0x5710

  4. The reason why writing to the flag register is undefined is to make the base ISA as compatible as possible with extensions, as well as make the implemention easier. You might decide that writing to fl has no effect, or that it can be written to like any other register. Both are compatible with the spec. You might also want to define a meaning for the other bits in the flag register, and have the CPU behave differently depending on the value of those bits set by the user. 

bars search times arrow-up