Primitive Types and Arrays

Rust requires you to be explicit about the size of every integer. There is no generic int. This is intentional — it mirrors the actual hardware and prevents silent overflow surprises.

Unsigned integers: u8 and u16

TypeBitsRangeUse in the CPU
u880–255V registers, SP
u16160–65535PC, I, stack entries

u8 is right for V0–VF because CHIP-8 is an 8-bit machine — each register is literally one byte. If a value overflows 8 bits, it wraps. That is not a bug; that is the hardware.

u16 is required for pc and i because they point into memory, which goes up to address 0xFFF (4095) — beyond what 8 bits can hold.

Signed types

Signed equivalents exist: i8, i16, i32, etc. For hardware emulation, unsigned types are almost always correct — addresses and register values are never negative.

Fixed-size arrays: [T; N]

#![allow(unused)]
fn main() {
pub v:     [u8; 16]     // 16 registers, each 1 byte
pub stack: [u16; 16]    // 16 stack slots, each 2 bytes
}

The syntax is [type; count]. The size is part of the type — [u8; 16] and [u8; 32] are different types as far as the compiler is concerned.

Arrays live on the stack (not the heap), their size is fixed at compile time, and they are accessed by index:

#![allow(unused)]
fn main() {
self.v[0]   // V0
self.v[15]  // VF
}

The shorthand for initialization:

#![allow(unused)]
fn main() {
[0; 16]   // array of 16 zeros
[0u8; 16] // same, with explicit type annotation
}

Self as a return type

#![allow(unused)]
fn main() {
pub fn new() -> Self {
}

Self is an alias for the implementing type (Cpu here). It is equivalent to writing -> Cpu, but Self is preferred because it stays correct if the struct is ever renamed.

Why not use Vec or HashMap?

CHIP-8's hardware is fixed: always 16 registers, always a 16-level stack. Vec is for collections whose size changes at runtime. Using a fixed array faithfully represents the hardware and is simpler and faster.

For collections whose size is only known at runtime — like a sprite loaded from memory — we do use Vec. See Vec<T> — Dynamic Arrays and Slices and Ranges.


In the emulator: Every register, stack slot, and memory cell in the CPU is either a u8 or u16. The CHIP-8 Registers chapter explains the hardware reason for each choice. The Type Casting chapter covers what happens when you need to convert between these types — which happens constantly when decoding opcodes.