This page provides a more verbose description of each instruction and what it does, which is more useful when it actually comes to writing assembly, rather than implementing the CPU in hardware. There’s nothing in here that isn’t already covered in the ISA Document as far as building the CPU is concerned.

Machine Code Lookup#

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
MOVL 0 0 0 0 cond rd imm8
SETH 0 0 0 1 cond rd imm8
STR 0 1 0 0 cond rd 0 ra 0 0 0 0
LDR 0 1 0 1 cond rd 0 ra 0 0 0 0
ADD 1 0 0 0 cond rd 0 ra 0 rb
SUB 1 0 0 1 cond rd 0 ra 0 rb
AND 1 0 1 0 cond rd 0 ra 0 rb
ORR 1 0 1 1 cond rd 0 ra 0 rb

Hardware Instructions#

  • ra, rb, etc. denote the register index in machine code tables, and the register contents in descriptions. This may seem like it can cause confusion, but in practice the register index is never used in calculations, so it’s always obvious in context which interpretation to use. For example, rd ≔ ra + rb is adding the contents of ra and rb and storing into the register indexed by rd, because it does not make sense to add the register indices (values between 0 and 7) nor ‘overwrite’ the register index rd itself.

  • {x} Denotes that the contents x are optionally allowed.

  • [x] Denotes the data stored in memory at address x. For example, [r1] refers to the contents in memory, located at the address stored in r1.

  • The value of cond in the machine code is determined by the suffix on the instruction (see Conditions).

ADD#

Syntax#

add {rd,} ra, rb
  • rd defaults to ra if not present.

Machine code#

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

Description#

rd ≔ ra + rb

The add instruction adds the value of ra to a second operand value in rb, and stores the result in rd.

Modifies the flags as follows

Flag Condition
Z 1 if the result is zero, else 0
N 1 if the result (viewed as a 2's complement integer) is negative, else 0
C 1 if a carry occurred (the result would have the 17th bit set if it existed), else 0
V 1 if an overflow occurred (the inputs have the same sign, but the result has the opposite sign), else 0

Examples#

; r1 == 0xFFFF (-1)
; r2 == 0x0006
add r3, r1, r2
;   r1 == 0x0005
; VCNZ == 0b0100  ; no overflow (different input signs)


; r1 == 0x7FFF
; r2 == 0x0005
add rz, r1, r2
;   rz == 0x0000  ; rz is always read as 0
; VCNZ == 0b1010  ; result 0x8004 has opposite sign
                  ; to inputs and is negative

AND#

Syntax#

and {rd,} ra, rb
  • rd defaults to ra if not present.

Machine Code#

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

Description#

rd ≔ ra & rb

The and instruction computes the bitwise AND of ra and rb and stores the result in rd.

Modifies the flags as follows

Flag Condition
Z 1 if the result is zero, else 0
N 1 if the result (viewed as a 2's complement integer) is negative, else 0
C Always 0
V Always 0

Examples#

; r1 == 0b1100 (12)  ; leading 12 bits are all 0
; r2 == 0b1010 (10)
and r3, r1, r2
; r3 == 0b1000 (8)
; VCNZ == 0b0000

MOVL#

Syntax#

movl rd, imm8
  • imm8 is an integer between 0 and 255 (inclusive)

Machine Code#

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

Description#

rd ≔ imm8

The movl instruction loads an 8-bit immediate, extends it with zeros, and writes it to register rd. The low byte of rd contains imm8, and the high byte of rd contains zero after executing this instruction.

The flags are unchanged.

Examples#

; r1 == 0x1234
movl r1, 0xAB
; r1 == 0x00AB  ; upper byte got cleared to 0x00

LDR#

Syntax#

ldr rd, [ra]

Machine Code#

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

Description#

rd ≔ [ra]

The ldr instruction loads a word from memory specified by the memory address in ra, and stores the result into rd.

The flags are unchanged.

Examples#

; r1 == 0
ldr r2, [r1]
; r2 == 0x5210  ; it loaded its own machine code!
movl r2, 0x45   ; what happens if we change r2?
ldr r2, [r1]    ; did we change what's in memory?
; r2 == 0x5210  ; ...changing r2 does not change memory

movl r1, 1      ; let's try a different address
ldr r3, [r1]
; r3 == 0x0245  ; we read the `movl r2, 0x45`
                ; instructions machine code!

ORR#

Syntax#

orr {rd,} ra, rb
  • rd defaults to ra if not present.

Machine Code#

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

Description#

rd ≔ ra | rb

The orr instruction computes the bitwise ORR of ra and rb, and stores the result in rd.

Modifies the flags as follows

Flag Condition
Z 1 if the result is zero, else 0
N 1 if the result (viewed as a 2's complement integer) is negative, else 0
C Always 0
V Always 0

Examples#

; r1 == 0b1100 (12)  ; leading 12 bits are all 0
; r2 == 0b1010 (10)
orr r3, r1, r2
;   r3 == 0b1110 (14)
; VCNZ == 0b0000

SETH#

Syntax#

seth rd, imm8

Machine Code#

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

Description#

rd = (imm8 << 8) | (rd & 0xff)

The seth instruction moves an 8-bit immediate into the high byte of the destination register rd, leaving the low byte of rd unchanged.

Contrast with movl, which moves an 8-bit immediate into the low byte of a register, and zeros out the high byte.

The flags are unchanged.

Examples#

; r1 == 0x1234
seth r1, 0xAB
; r1 == 0xAB34  ; note the lower byte is left unchanged

STR#

Syntax#

str rd, [ra]

Machine Code#

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

Description#

[ra] ≔ rd

The str instruction stores a word from memory contained in rd to the memory address ra.

The flags are unchanged.

Examples#

; r1 == 1
; r2 == 0x0385
str r2, [r1]
movl r3, 0xFF
; r3 == ???  ; we modified memory in the location the
             ; second instruction lives at. What will
             ; actually be in r3 now?

SUB#

Syntax#

sub {rd,} ra, rb
  • rd defaults to ra if not present.

Machine Code#

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

Description#

rd ≔ ra - rb

The sub instruction subtracts the value in rb from the value in ra and stores the result in rd.

Modifies the flags as follows

Flag Condition
Z 1 if the result is zero, else 0
N 1 if the result (viewed as a 2's complement integer) is negative, else 0
C 1 if a carry occurred (when calculated as ra + ~rb + 1), else 0
V 1 if an overflow occurred (the inputs have opposite signs, and the result has the same sign as rb), else 0. (Equivalently, invert the sign of rb and apply the add overflow rule).

Examples#

; r1 == 0x0005
; r2 == 0x0008
sub r3, r1, r2
;   r3 == 0xFFFD  ; (-3) Check by adding 3. What do you get?
; VCNZ == 0b0110  ; carry because we actually
                  ; added 0x0005 + 0xFFF7 + 1


; r1 == 0x0001
; r2 == 0x8000
sub r3, r1, r2
;   r3 == 0x8001
; VCNZ == 0b1010  ; overflow because r1 and r2 have
                  ; different signs, but the result
                  ; has the same sign as r2.
bars search times arrow-up