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 ofra
andrb
and storing into the register indexed byrd
, because it does not make sense to add the register indices (values between 0 and 7) nor ‘overwrite’ the register indexrd
itself. -
{x}
Denotes that the contentsx
are optionally allowed. -
[x]
Denotes the data stored in memory at addressx
. For example,[r1]
refers to the contents in memory, located at the address stored inr1
. -
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 tora
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 tora
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 and (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 tora
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 tora
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.