Registers and the CPU
A register is a tiny storage location inside the CPU — faster than memory, but very limited in number. Before writing a single line of code, we need to know exactly what hardware we are modeling.
General-purpose registers: V0–VF
#![allow(unused)] fn main() { pub v: [u8; 16] }
Sixteen registers, each holding one byte (0–255). Programs use them to store temporary values: the current score, a position, a loop counter. In code, v[0] is V0, v[15] is VF.
VF is special. Several opcodes write to it automatically as a side effect:
| Opcode | What VF receives |
|---|---|
ADD Vx, Vy | 1 if result overflowed (carry), 0 otherwise |
SUB Vx, Vy | 1 if Vx ≥ Vy (no borrow), 0 otherwise |
SHR Vx | the bit shifted out |
DRW Vx, Vy, n | 1 if any pixel was erased (collision), 0 otherwise |
Programs should not store data in VF — any arithmetic or draw instruction will overwrite it.
Index register: I
#![allow(unused)] fn main() { pub i: u16 }
A 16-bit pointer to a memory address. Before drawing a sprite, you point I at the sprite data. Before reading or writing memory, you point I at the location. It is the CPU's one shared memory pointer.
It is 16-bit because addresses go up to 0xFFF (4095), which does not fit in 8 bits.
Program counter: PC
#![allow(unused)] fn main() { pub pc: u16 }
Holds the address of the next instruction to execute. Each cycle:
- Read the 2-byte opcode at
memory[pc]andmemory[pc + 1] - Advance
pcby 2 - Decode and execute
PC starts at 0x200. The first 512 bytes of memory were reserved for the original firmware, so all CHIP-8 programs begin there.
Stack and stack pointer
#![allow(unused)] fn main() { pub stack: [u16; 16] pub sp: u8 }
The stack stores return addresses for subroutine calls. When a CALL instruction runs, the current PC is pushed onto the stack so RET can come back to it. CHIP-8 supports up to 16 levels of nesting.
CALL addr → stack[sp] = pc; sp++; pc = addr
RET → sp--; pc = stack[sp]
Summary
| Register | Size | Role |
|---|---|---|
| V0–VE | 8-bit | General-purpose data |
| VF | 8-bit | Status flag (carry, borrow, collision) |
| I | 16-bit | Memory address pointer |
| PC | 16-bit | Next instruction address |
| SP | 8-bit | Stack depth counter |
| stack | 16 × 16-bit | Return address storage |
Rust concepts used here: The CPU is modeled as a
struct— see Structs and impl Blocks. The choice ofu8andu16for each field is explained in Primitive Types and Arrays. Thenew()function that initializes PC to0x200is covered in Constructors and Initialization.