CPU Instruction Guide

Last updated:

The full opcode table lives in the CPU Instruction Reference. This page explains how to read it.

Don't read it top to bottom because checkpoint #2 sends you there one opcode at a time.

There's 256 instructions, and they are identified by a onebyte opcode (0x00 to 0xff). Each instruction has its own fixed behavior.

Most instructions are a unique pair of mnemonic, addressing mode: Other instructions are mnemonic. The opcode byte just encodes that pair. LDA $42 (opcode A5), zero page and LDA #$42 (opcode A9) are different instructions, they have the same mnemonic (LDA), but different addressing modes.

The addressing mode describes how a specific value is obtained. The mnemonic usually describes what to do with that value.

Special instructions

There's a small group of unique instructions that don't have an addressing mode. They do unique work and I call these special:

Operands

Instructions can have 0, 1 or 2 operands. The importance of this is just for matching nestest, because the format... (complete this)

Illegal instructions

Also known as undocumented instructions in official docs. Many (most?) games make use of these instructions, so they are included here. and the genuinely unstable ones carry a warning.

Flags

Flags are shown as N V - B D I Z C: a letter means the instruction sets that bit, - means it's preserved. N and Z track the result — the last value the instruction computes (the register it loads, the byte it writes, or its final register update), which isn't always the output. C and V, when touched, are spelled out in the entry.

Addressing modes

Addresses are written high:low when assembled across cycles — typically ADH:ADL for the operand address being built, or PCH:PCL for the program counter. Cycle 1 (opcode fetch) is omitted from every mode below.

Access type. A memory mode doesn't pin down the cycles on its own: it works out where the operand is, but what happens at that address depends on the instruction's access type:

Each instruction below is tagged with its access type. The memory modes share their address-calculation cycles and then branch on it, shown as Access type: read / write / rmw. Two things to notice in the branch:

Implied, Accumulator, and Immediate have no memory operand to branch on, so their single form is listed as-is — Accumulator is an RMW on A with no bus access.

Opcodes in each group are listed ascending by value.

Dummy reads

Each cycle is written as assignments, Zig-style. bus.read(addr) is a read bus cycle returning the byte at addr; bus.write(addr, val) is a write cycle. _ = bus.read(addr) is a dummy read: the CPU still drives the cycle on the bus, it just throws the byte away (_ discards it, as in Zig). The hardware can't skip these. // starts a comment.