An interactive course · bsnes & the 65816

How the Super Nintendo thought.

We start from the very beginning — what a processor even is — assuming you know nothing about computer architecture. Once the vocabulary is solid, we meet the Ricoh 5A22: a 16-bit 65816 core wrapped in Nintendo's own DMA, HDMA and math hardware. We finish inside bsnes, where a single master clock keeps the whole console honest. Fifteen modules, with ten live, clickable machines along the way so you can watch every idea run, not just read about it. No game code ships with this page; every simulation is built in your browser.

Part I

How CPUs work

This first part is pure fundamentals — nothing about any specific console or chip yet. It's the toolkit every processor is built from, whether that's your phone, your laptop, or the chip we'll meet in Part II. We build the vocabulary one word at a time — instruction, register, addressing mode, stack, interrupt — and you can step and run each idea in a tiny simulated machine. If a term is ever used before it's explained, that's a bug; tell us. Everything in Parts II and III is just these six ideas wired together.

Modules 01–06Difficulty Absolute beginnerLabs 5 interactive
Module 01 · Part I

The fetch–decode–execute loop

GoalUnderstand, from scratch, what a processor actually does: fetch a number from memory, work out what it means, do it — forever.

Let's start with what a processor physically is. Strip away the marketing and a CPU is a machine that repeats one tiny ritual, millions of times per second, without ever getting bored. The ritual has three steps: fetch a number from memory, decode it — work out what command that number stands for — and execute it. Then do the next one. That's the whole job. Everything a Super Nintendo ever did — every Mode 7 castle, every Chrono Trigger battle, every pixel — is that loop, running fast. What sets the relentless pace is a clock — a steady electrical pulse, a metronome inside the chip; each tick lets the machine take its next tiny step. Module 06 puts real numbers on it.

Two words in that ritual need unpacking. Memory is a long row of numbered pigeonholes, each holding one small number (a byte). The pigeonhole's position in the row is its address — cell 0, cell 1, cell 2, and so on. And an instruction is just one of those stored numbers that the CPU has agreed to treat as a command. There is nothing special about the number itself: $A9 (the $ just marks a number written in hexadecimal, base 16 — Module 03 unpacks that notation) sitting in memory might mean “load the accumulator” to the processor, or it might just be the number 169 in someone's data. Position and convention decide, nothing else. Programs and data live in the same memory, side by side — this is the single most important idea in all of computing, and (in Module 14) it's also what makes emulators sweat.

So how does the CPU know which cell holds its next command? It keeps a bookmark: a counter called the program counter (PC). Each trip round the loop it fetches the instruction the PC points at, then bumps the PC forward so the next trip fetches the next cell. The exception is a branch or jump: an instruction whose whole job is to overwrite the PC with a new address, so execution leaps somewhere else. Branches are how programs make decisions and loops.

Analogy · A cook with recipe cards

Picture a cook working from a stack of numbered recipe cards, one short command per card: “fetch the flour”, “add two eggs”, “go back to card 3”. The cook keeps a finger on the current card — that finger is the program counter. Read a card, do what it says, slide the finger to the next card. The cook never sees the “whole recipe”; only ever one card at a time. A CPU is exactly this cook — just one who reads a couple of million cards a second and never misreads one.

Memory 0 · $A9LDA 1 · $6DADC 2 · $8DSTA 3 · $4CJMP data PC = 1 (the bookmark) 1 · Fetch read the cell PC points at 2 · Decode $6D → “add from memory” 3 · Execute do it, then advance PC repeat, forever instruction = a number
The loop that runs the world. Instructions are ordinary numbers stored in memory. The program counter points at the next one; the CPU fetches it, decodes what the number means, executes it, advances the PC, and repeats. Those hex opcodes — $A9, $6D, $8D, $4C — are the real 65816 codes for LDA, ADC, STA and JMP.

A worked example, by hand

Let's invent the smallest possible machine and run it on paper. Our toy CPU has 16 memory cells (addresses 0–15), one working register called A — the accumulator (a register is a scratch value inside the CPU — Module 02 is all about them), and a PC. Each instruction is one byte: the first hex digit says what to do — the opcode — and the second says which cell to do it to. Our whole instruction set is four opcodes: 1n = LDA (load) cell n into A, 2n = ADC (add) cell n to A, 3n = STA (store) A into cell n, 4n = JMP (jump) to cell n. The 65816 is, at heart, exactly this kind of accumulator machine — just with a real, rich instruction set.

Now put this five-instruction program in cells 0–4, with data in cells 12–14. Read it: load cell 14, add cell 13, add cell 12, store cell 14, jump to 0. Cell 13 holds 2 and cell 12 holds 1, so each trip round the loop the number in cell 14 grows by 3. That's it — a counter, the “hello world” of hardware. The lab below is this exact machine. Press Step and watch each fetch, decode and execute happen; press Run and watch it become a program.

Toy-CPU lab — a visible fetch–decode–execute machinesimulated · live
Memory · 16 cells
Registers
Press Step to run one phase of the loop, or Run to let it fly.
Violet = where the PC points · cyan = memory being read · magenta = memory being written.
Key takeaways
  • A CPU repeats one loop forever: fetch an instruction, decode it, execute it.
  • Instructions are ordinary numbers in memory — programs and data share the same pigeonholes.
  • The program counter is the bookmark; a branch or jump is an instruction that rewrites it.
  • An instruction's number splits into an opcode (what to do) and operands (what to do it to).
Module 02 · Part I

Registers, memory & the ISA

GoalLearn what registers are, meet the actual 65816 programmer's model, and see why a CISC memory-operand machine is the opposite of a load/store RISC.

Our toy CPU had one working register, A. Real processors have more, and the reason is distance — literally. Memory is a large structure physically far (in chip terms) from the circuits that do arithmetic, so every trip to it costs time. A register is a small storage slot built inside the CPU, right next to the arithmetic circuitry. It holds one value and reading or writing it costs almost nothing. Registers are the workbench; memory is the warehouse. There are only a handful precisely because they're so close — you can't fit a warehouse on a workbench. And the difference is measurable in clock ticks: an instruction that works purely with registers (like INX, add 1 to X) takes the 65816 about 2 cycles, while one that must fetch its operand from memory (like ADC $2000) takes 4 or more. Multiply that gap by millions of instructions a second and you see why compilers and programmers fight to keep hot values in registers.

Analogy · The workbench and the warehouse

A carpenter doesn't walk to the warehouse for every screw. They fetch a tray of parts to the bench, work there — where everything is within arm's reach — and carry results back when done. A register is a spot on the bench; a load is the walk to the warehouse shelf; a store is the walk back.

The 65816 programmer's model

Here is every register a Super Nintendo programmer actually touches. The star is the accumulator A, where arithmetic lands. Two index registers X and Y count and point. The stack pointer S tracks a scratch area we'll meet in Module 05. The processor status register P is a row of one-bit flags. And a cluster of registers exist purely to reach the 65816's big 24-bit address space: the program bank PBR, the data bank DBR, and the direct page D (Modules 04 and 09 explain those in full).

RegisterWidthWhat it holds
A8 or 16Accumulator — the main work register for arithmetic and logic. Its width is a flag you set (Module 08).
X · Y8 or 16Index registers — counters and pointers, used to walk arrays. Their width is a separate flag.
S16-bitStack pointer — the top of the stack (Module 05). In emulation mode its high byte is locked to $01.
P8 flagsStatus: N V M X D I Z C — plus the hidden E emulation bit.
PC16-bitProgram counter — the bookmark from Module 01, within the current program bank.
PBR · DBR8-bitProgram & data bank registers — which 64 KB bank code and data live in (Module 09).
D16-bitDirect page register — a movable fast-access window into bank 0 (Module 04).

The flags in P record the outcome of the last operation and steer branches: N (negative, top bit set), V (signed overflow), Z (zero), C (carry). I masks interrupts, D switches arithmetic into decimal mode (Module 03). And two flags are the whole reason Part II exists: M sets the accumulator's width and X sets the index registers' width — 8 or 16 bits — which is Module 08's headline story.

The 65816 programmer's model — boxes drawn to width (16-bit = full, 8-bit = half) accumulator · M flag splits it (Module 08) B hidden high byte A 8-bit A · 16-bit = B:A index registers · X flag truncates them X hi forced $00 when X=1 X lo Y hi forced $00 when X=1 Y lo status register P · eight 1-bit flags N V M X D I Z C E hidden emulation bit — swapped with C by XCE where code runs · PBR supplies the bank, PC the offset PBR PC 16-bit program counter where absolute data lives DBR 8-bit bank glued onto 16-bit absolute addresses (Module 09) stack & direct page — both live in bank $00 S 16-bit stack pointer (Module 05) D 16-bit direct-page base (Module 04)
Every register, drawn to width. A full-width box is 16 bits, a half-width box 8. The accumulator is really B:A — the M flag hides or reveals B — while X and Y simply lose their high bytes when the X flag is 1 (Module 08 has the fine print). PBR prefixes the PC, DBR banks absolute data, and P is eight one-bit flags with the hidden E bit hanging off the side.

All of this — which registers exist, every instruction the chip understands, how each is encoded into bytes, what the flags mean — is written down in the chip's ISA, its instruction set architecture: the documented contract between hardware and software. Write your program to the contract and it runs on any chip that implements it — which is exactly how the same 65816 ISA could serve the Apple IIGS and the Super Nintendo, and why an emulator (Part III) is, at heart, a program that honours the contract in software.

CISC: arithmetic that touches memory

Now the family question. Many modern chips are RISC load/store machines: arithmetic only works register-to-register, and the only instructions that touch memory are explicit loads and stores. The 65816 is the opposite — a CISC design. A single ADC $2000 reads a value straight out of memory, adds it to A, and sets the flags, all in one instruction. And 65816 instructions are variable length: one byte (INX), two (LDA #$10), three (LDA $2000), even four (LDA $7E2000). The opcode's identity and — as Module 08 will show — the current flag state decide how many bytes follow. This flexibility is exactly what makes decoding trickier than a fixed-size RISC, and it shapes every emulator in Part III.

LDA $2000
load A straight from memory address $2000
ADC $2002
add the value at $2002 to A — memory operand!
STA $2004
store the result back to memory

Three instructions on the 65816 where a RISC would need a load, a load, an add, and a store — because ADC is allowed to read memory directly.

Key takeaways
  • Registers are ultra-fast slots inside the CPU; memory is the big, slow warehouse.
  • The 65816 model: accumulator A, index X/Y, stack S, status P (N V M X D I Z C + hidden E), PC, and the bank/page registers PBR, DBR, D.
  • The 65816 is CISC: arithmetic can read a memory operand directly, and instructions are variable length.
  • The M and X flags set register widths — the pivot the whole of Part II turns on.
Module 03 · Part I

Numbers in silicon

GoalSee how bits encode whole numbers, negatives, carry and overflow — and meet the 65xx family's oddest party trick, BCD decimal mode.

Everything a CPU touches is stored in bits — switches that are either 0 or 1. A row of bits reads as a number in binary, exactly like decimal but with columns worth 1, 2, 4, 8, 16… instead of 1, 10, 100. So 1011 is 8 + 2 + 1 = 11. With n bits you get 2n patterns: a byte (8 bits) covers 0–255, and a 16-bit value covers 0–65535. Programmers write these in hexadecimal — base 16, one hex digit per four bits — because it lines up with the hardware. So $FF is 255, $100 is 256, and a full bank address like $7E2000 is just six tidy hex digits.

Negative numbers need a convention, and the universal one is two's complement: keep counting past the top and let the value wrap around, like an odometer rolling over — except we relabel the top half of the patterns as the negatives. In 8 bits, $FF is −1, $FE is −2, down to $80 = −128. The lovely consequence: the same adder circuit adds signed and unsigned numbers correctly without knowing which it's doing. Two flags watch the edges: the carry flag catches an unsigned result spilling past the top of the register, and the overflow flag catches a signed result whose sign came out wrong. Carry is also how you chain bytes: add the low bytes, then add the high bytes plus carry, and you've added a 16-bit number on an 8-bit ALU.

Here is overflow in four lines. Take $7F — read as signed, +127, the largest positive byte. Add $01. The adder produces $80 — which, read as signed, is −128. Two positives just made a negative: the sign came out wrong, so the CPU sets V. (Carry stays clear — nothing spilled past the top, and the unsigned reading 127 + 1 = 128 is perfectly correct.)

Analogy · The odometer

An 8-bit register is a car odometer with three... no, with room for 0 to 255. Drive one past 255 and it clicks back to 0 — that click is the carry. Two's complement just decides to call the top half of the dial — 128 to 255 — the negatives −128 to −1 instead. Same physical dial, two ways of reading it, and the same clicking works for both.

The 65xx oddity: decimal mode

Here's something most CPUs never had. The 65816 (like the 6502 before it) has a decimal mode, switched on by the D flag in the status register. With D set, ADC and SBC stop doing plain binary arithmetic and instead treat each byte as two packed BCD digits. In BCD, the byte $25 literally means the number twenty-five — each half of the byte (a nibble, four bits, one hex digit) holds one decimal digit: the high nibble is the tens, the low nibble the units — and ADC carries from the units to the tens at 10, not 16. Add $25 and $18 in decimal mode and you get $43 (forty-three), not $3D. It's built for score counters, clocks and anything displayed as decimal digits, sparing the programmer a binary-to-decimal conversion. The catch: a nibble greater than 9 (like the $F in $1F) isn't a valid BCD digit, and feeding one to a decimal-mode add gives nonsense — one more thing an accurate emulator must reproduce exactly.

Go poke the bits yourself. Every square below is a button. Flip them and read the same pattern four ways at once — as an unsigned number, as a two's-complement signed number, as hex, and as packed BCD. Then switch the width between 8 and 16 bits and watch the ranges change: it's the M/X flag width choice of Module 08, previewed.

Integer lab — one bit pattern, four readingsbinary · live
high byte low byte
Unsigned
Signed (two's comp.)
Hexadecimal
Packed BCD
Note
Key takeaways
  • Binary counts in powers of two; hexadecimal groups four bits per digit and lines up with the hardware.
  • Two's complement relabels the top half of the pattern space so one adder handles signed and unsigned.
  • Carry catches unsigned overflow (and chains multi-byte adds); the V flag catches signed overflow.
  • The D flag switches ADC/SBC into BCD decimal mode — base-10 arithmetic in hardware, a 65xx trademark.
Module 04 · Part I

Addressing modes

GoalSee how the same instruction reaches its data a dozen different ways — and how each mode resolves an operand and the registers to one 24-bit effective address.

An instruction like LDA — load the accumulator — needs to know where to load from. The way it specifies that “where” is its addressing mode, and the 65816 is famous for having a rich set of them. The same mnemonic plus a different mode produces a different opcode byte, a different instruction length, and a different number of cycles. Master the modes and you've mastered most of what makes 65816 code look the way it does.

The address a mode finally computes is called the effective address. Some modes build it from operand bytes alone; others fold in a register. The simplest is immediate (LDA #$42): the operand is the data, so there's no address at all. Absolute (LDA $2000) gives a 16-bit address, and the current data bank supplies the top 8 bits. Absolute long (LDA $7E2000) spells out all 24 bits itself. One spelling habit to absorb now: the 65816 is little-endian — it stores multi-byte values low byte first. In memory the operand $2000 is the byte 00 followed by 20, and the CPU glues them back together low-then-high.

Two register-backed shortcuts matter enormously. The direct page (the 65816's grown-up version of the 6502's “zero page”) is a movable 256-byte window into bank 0, its base held in the D register. LDA $10 means “D plus $10” — a one-byte operand reaching a fast, nearby scratch area. And indexed modes add X or Y to the address, so a loop can walk an array by simply incrementing an index. Stack everything up — direct or absolute, short or long, plain or indexed, direct or through a pointer (indirect) — and you get the two dozen ways the 65816 can say “there”.

Analogy · Giving someone directions

“Here, take this” is immediate — the thing is in your hand. “House number 8000” is absolute — but only makes sense once you know the town (the data bank). “Number 16 on our street” is direct-page — short, because “our street” (D) is understood. “Go to the address written on that note” is indirect. And “the fourth house past this one” is indexed. Same destination, wildly different instructions to reach it.

Anatomy of the workhorse — LDA ($10),Y operand $10 1 byte after the opcode D = $1F00 direct-page base + read $00:1F10 bytes 00 20 → pointer $2000 DBR = $7E supplies the bank base $7E:2000 bank : pointer Y = $0010 the index — walks the array + $7E2010 24-bit effective address operand + D → find the pointer · read the pointer · DBR supplies the bank · add Y → the address actually read
Four little sums, one address. LDA ($10),Y adds the operand to D to locate a pointer in bank 0, reads the 16-bit pointer stored there (little-endian!), borrows the bank from DBR, then adds Y. The lab below runs these exact numbers.

The lab lets you pick a mode and watch it resolve. The register file is fixed at plausible values (data bank $7E, direct page $1F00, X = 4, Y = $10, stack at $01FF); a small memory holds the pointers the indirect modes chase. Choose a mode and watch the numbered steps combine the operand with the highlighted registers into one 24-bit effective address.

Addressing-mode explorer — operand + registers → 24-bit addresssimulated · live
Register file
Operand bytes
Resolution, step by step
Key takeaways
  • An addressing mode is how an instruction names its data; the result is a 24-bit effective address.
  • Immediate needs no address; absolute borrows the data bank; absolute long spells all 24 bits out.
  • The direct-page register D gives a movable short-operand window; X and Y index for array walks.
  • Indirect modes chase a pointer stored in memory — the workhorse (dp),Y is how you scan arrays.
Module 05 · Part I

The stack & subroutines

GoalUnderstand the stack: a last-in-first-out scratch area that makes subroutines, nested calls and interrupts possible.

Programs are built from subroutines — reusable chunks of code you can call from anywhere. But that raises a question: when a subroutine finishes, how does the CPU know where to return to? The answer is the stack: a region of memory used strictly last-in-first-out, like a spike where you impale receipts. You push a value on top; you pull (pop) the top value back off. The stack pointer S always marks the top.

On the 65816 the stack grows downward: a push writes to the address in S and then decrements S; a pull increments S and then reads. In emulation mode (how the console boots) the stack is locked to page 1 — addresses $0100–$01FF, just like the 6502 — so only the low byte of S moves. Switch to native mode (Module 08) and the stack becomes a full 16-bit pointer that can live anywhere in bank 0, so a program can have as much stack as it needs.

Subroutines ride on this. JSR (jump to subroutine) pushes the return address onto the stack, then jumps; RTS (return) pulls that address back and resumes just after the call. Because pushes stack up, calls can nest as deep as memory allows — each RTS unwinds one level. The 65816 adds long versions for its 24-bit world: JSL pushes the program bank too and RTL restores it, so a subroutine can live in a different 64 KB bank. One convention worth knowing, because every 65xx debugger shows it: JSR actually pushes the address of the last byte of the JSR instruction itself, not of the instruction after it — and RTS compensates by adding 1 to the address it pulls before resuming. You'll see that +1 happen in the lab's narration. And interrupts (Module 06) use the very same mechanism — they push the return address (in native mode the program bank PBR too) and the status register P, so RTI can restore everything and the interrupted code resumes exactly as it was.

Analogy · A stack of plates

A cafeteria plate dispenser: you add clean plates to the top and take them from the top. The last plate on is the first plate off — LIFO. When a subroutine calls another which calls another, each return address is a plate; every RTS lifts the top plate and goes back exactly where it came from. Lose track of the plates — push without pulling, or vice versa — and the program returns to a wrong address and crashes. The stack's discipline is what keeps nesting sane.

Three calls deep — what the stack remembers main JSR SubA at $8005–$8007 SubA JSR SubB at $8105–$8107 SubB JSR SubC at $8205–$8207 SubC running now page-1 stack · grows downward ↓ $01FF · $80 $01FE · $07 $01FD · $81 $01FC · $07 $01FB · $82 $01FA · $07 $01F9 · S → next free byte — the next push lands here ┐ ret $8007 (+1 → $8008) ┘ frame 1 · back into main ┐ ret $8107 (+1 → $8108) ┘ frame 2 · back into SubA ┐ ret $8207 (+1 → $8208) ┘ frame 3 · back into SubB
The receipts of three nested calls. Each JSR pushed a two-byte return address (high byte first, so it reads low-then-high in memory) — the address of the JSR's own last byte, which RTS will pull and add 1 to. Three frames deep, S has walked down six bytes; each RTS will unwind exactly one frame, newest first.

Play with it below. PHA pushes the accumulator; PLA pulls it back. JSR pushes a two-byte return address (high byte first) and RTS pulls it. Watch S walk downward on every push and back up on every pull, and watch the top-of-stack byte light up.

Stack lab — push, pull, call & returnsimulated · live
Stack memory · page 1 · S = $01FF
The stack lives in page 1.

Violet outline = S (next free byte) · cyan = the current top of stack.

Key takeaways
  • The stack is a last-in-first-out scratch area; the stack pointer S marks the top, and it grows downward.
  • Emulation mode locks the stack to page 1 ($0100–$01FF); native mode frees S to roam all of bank 0.
  • JSR/RTS push and pull a return address; JSL/RTL do it across banks for the 24-bit world.
  • Interrupts push the return address (plus PBR in native mode) and the P status register, so RTI resumes interrupted code intact.
Module 06 · Part I

Interrupts & timing

GoalLearn how hardware interrupts hijack the CPU at just the right instant, meet the vector table, and see how the master clock divides into three CPU speeds.

So far our CPU has run in a straight line, one instruction after the next. But a games console is full of hardware that needs attention at precise moments — most of all the video chip, which draws the screen line by line and must be left alone while it draws. The mechanism is the interrupt: a hardware signal that makes the CPU stop what it's doing, save its place, and jump to a special handler routine — then, via RTI, resume the interrupted code exactly where it left off. The stack from Module 05 is what makes that clean resume possible: the interrupt pushes the return address (in native mode the program bank PBR too) and the P register before jumping, and RTI restores them all.

Because so much of this module is about the television, here is how a television of that era actually drew. A CRT paints the picture with an electron beam sweeping left to right, one thin scanline at a time, working from the top of the screen to the bottom — on an NTSC console, 262 scanlines per frame, about 60 frames every second. After the last visible line the beam must travel back up to the top-left corner, and that enforced pause — a band of lines during which nothing is being drawn — is the vertical blank.

The Super Nintendo's CPU listens for three: NMI (non-maskable interrupt), which fires at the start of V-blank — the moment the screen finishes drawing and it's finally safe to update video memory; IRQ (interrupt request), which games program to fire at a chosen horizontal/vertical beam position for mid-screen effects; and RESET, which starts the machine. The names are literal, and they connect back to a flag from Module 02: IRQ is a request the program may refuse — setting the I flag masks it — while NMI is non-maskable: no flag can shut it out.

How does the CPU know where each handler lives? A fixed vector table at the top of bank 0 — and there are really two of them, one per personality of the chip (Module 08 explains the two modes). In native mode the entries are: COP $FFE4, BRK $FFE6, NMI $FFEA, IRQ $FFEE. In emulation mode, the 6502-style set: COP $FFF4, NMI $FFFA, RESET $FFFC, and one shared IRQ/BRK entry at $FFFE. Notice RESET appears only in the emulation column: the 65816 always wakes up in emulation mode, which is why the first thing nearly every game does is the mode-switch spell you'll meet in Module 08. On an interrupt the CPU reads the address from the right slot and jumps — no guessing.

Analogy · The phone that must be answered

You're doing paperwork when the phone rings. You mark your line, take the call, deal with it, then return to the exact word you stopped at. The ring is the interrupt; the bookmark you jot is the stack push; the phone number you dial back is the vector. NMI is the call you're not allowed to ignore — the screen is blanking now and won't wait.

Two vector tables at the top of bank $00 — each cell holds a 16-bit handler address Native mode (E = 0) $FFE4 · COP $FFE6 · BRK $FFEA · NMI $FFEE · IRQ no RESET entry here — you can't wake up in native mode Emulation mode (E = 1) $FFF4 · COP $FFFA · NMI $FFFC · RESET $FFFE · IRQ / BRK IRQ and BRK share one entry, just like the 6502 NMI handler runs every V-blank IRQ handler fires at a beam position boot code always starts in emulation mode → CLC : XCE (Mod 08) power on → read $FFFC → jump
Two tables, one per personality. Native mode reads the $FFE4–$FFEE column; emulation mode the 6502-compatible $FFF4–$FFFE column. RESET exists only on the emulation side — the chip always boots as a 6502, which is exactly the situation Module 08 picks up from.

The clock, and why it has three speeds

First, what a clock even is, with numbers. A quartz crystal on the console's circuit board vibrates about 21 million times a second — precisely 21.477 MHz on an NTSC console — and provides the master clock: a metronome ticking 21 million times a second, where each tick is one master cycle, the smallest unit of time the console knows. The 5A22 doesn't generate that beat; it takes the crystal's signal and divides it. The CPU does not run one instruction per tick; instead each memory access takes a whole number of master cycles, and that number depends on what is being accessed. Fast regions (including cartridge ROM wired for FastROM) cost 6 master cycles per access — an effective 3.58 MHz. Most of the map, including SlowROM, costs 8 cycles — 2.68 MHz. And the slow I/O area (the old serial joypad ports at $4000–$41FF) costs 12 cycles — 1.79 MHz. Same CPU, three speeds, chosen by the address bus.

One tick-train, three access lengths master clock · 21.477 MHz — every vertical stroke is one master cycle FastROM access · 6 cycles → 3.58 MHz effective SlowROM / WRAM access · 8 cycles → 2.68 MHz slow I/O access · 12 cycles → 1.79 MHz ($4000–$41FF)
Division, not magic. The crystal's 21.477 MHz tick-train is the only clock there is; the 5A22 simply lets each memory access consume 6, 8 or 12 ticks depending on the address. Slower region → more ticks per access → a lower effective MHz for code touching it.

The lab draws one NTSC frame as a vertical timeline: 262 scanlines, the visible region on top and V-blank below. A beam sweeps down; the instant it crosses into V-blank, NMI fires and the counter ticks. Below, pick a memory-access speed and read how the master clock divides into the CPU's three real rates.

Timing lab — the frame, the beam & the NMIsimulated · live
scanline 0 region visible NMIs fired 0

That's the whole Part I toolkit: the loop, registers, number formats, addressing modes, the stack, and interrupts & timing. Everything in Part II is these six ideas put into real Nintendo silicon. Time to meet the chip.

Key takeaways
  • An interrupt makes the CPU save its place on the stack and jump to a handler, then resume via RTI.
  • NMI fires at V-blank (the only safe time to touch video memory); IRQ fires at a chosen beam position; RESET boots the machine.
  • Two vector tables at $FFE0–$FFFF — native (NMI $FFEA, IRQ $FFEE, BRK $FFE6, COP $FFE4) and emulation (NMI $FFFA, IRQ/BRK $FFFE, COP $FFF4, RESET $FFFC); RESET always lands in emulation mode.
  • The board's crystal provides the 21.477 MHz master clock; the 5A22 divides it into 3.58 / 2.68 / 1.79 MHz access speeds, chosen by the address.
Part II

The 5A22

Now we put the Part I toolkit into real silicon. Nintendo didn't design a CPU core from scratch — it licensed the proven 65816 and asked Ricoh to wrap it in exactly the peripherals a games console needs: DMA and HDMA controllers to move data fast, a hardware multiply/divide unit, controller ports, WRAM refresh, and clock generation, all on one die called the 5A22. These six modules cover the CPU's two operating widths, its unusual 24-bit banked memory, the two DMA engines, and the math unit — each mapping straight back to a fundamental you already know.

Modules 07–12Difficulty HardwareLabs 5 interactive
Module 07 · Part II

Meet the 5A22

GoalMeet the actual chip: a 65C816 core plus Nintendo's on-die DMA, math, I/O and clock hardware — and its three region clocks.

The Super Nintendo's CPU chip is the Ricoh 5A22. At its heart is a 65C816 processor core — the 16-bit successor to the 6502 family that powered a generation of home machines: the Apple II used the 6502 itself, the Commodore 64 its 6510 variant, and the NES a Ricoh 2A03 built around a 6502 core. But the 5A22 is more than the core: Ricoh integrated onto the same die a set of peripherals that would otherwise need separate chips. That's why we call it the 5A22 and not just “the 65816” — the CPU you program is the core, but the silicon in the box does far more.

What Ricoh added around the core: the DMA and HDMA controllers (eight channels that copy blocks of memory without the CPU lifting a finger — Modules 10 and 11), a hardware multiply and divide unit (the 65816 core can't multiply on its own — Module 12), the parallel I/O and controller ports plus automatic joypad reading, the H/V counters and timer that track the drawing beam's position and can fire an IRQ at a chosen screen coordinate (Module 14 returns to these), WRAM refresh logic to keep the 128 KB of work RAM alive — work RAM is DRAM, which forgets its contents within milliseconds unless every row is periodically re-read, so the 5A22 quietly does that re-reading itself — and the clock circuitry that takes the 21.477 MHz master signal from the board's quartz crystal and divides it into the access speeds of Module 06. The core thinks; the 5A22's peripherals do the heavy lifting around it.

Analogy · The chef and the fitted kitchen

The 65816 core is a skilled chef. But a chef alone in an empty room can't cook — the 5A22 is the fitted kitchen built around them: the dishwasher that clears plates while they work (DMA), the calculator on the wall for portions (the math unit), the intercom to the dining room (the controller ports), and the clock on the wall everyone works to. Nintendo bought the chef and built the kitchen to suit exactly one restaurant.

The Ricoh 5A22 and the console around it A 65C816 core sits inside the 5A22 alongside DMA/HDMA, the multiply/divide unit, I/O ports, WRAM refresh and clock generation. The chip connects to 128 KB of work RAM, the PPU, the APU sound subsystem and the cartridge. Ricoh 5A22 the SNES CPU chip · 21.477 MHz master clock 65C816 core the CPU you program 8/16-bit · Part I DMA + HDMA · 8 ch · Mod 10–11 multiply / divide · Mod 12 I/O + joypad ports WRAM refresh clock divider (crystal in) → 6 / 8 / 12 master cycles per access a 65816 core plus everything a console needs, on one die WRAM · 128 KB $7E–$7F · Mod 09 PPU $2100–$213F · video APU (SPC700) $2140–$2143 · sound Cartridge ROM · SRAM · chips Region clocks Fast · 6 cyc · 3.58 MHz Slow · 8 cyc · 2.68 MHz I/O · 12 cyc · 1.79 MHz all ÷ from 21.477 MHz
One chip, one clock. The 5A22 packages a 65C816 core with DMA/HDMA, the math unit, I/O and clock generation, and drives the WRAM, PPU, APU and cartridge. Every access is metered at 6, 8 or 12 master cycles.
Core
65C816
16-bit successor to the 6502
Master clock
21.477 MHz
NTSC · divides to 3.58 / 2.68 / 1.79
Address space
16 MB
24-bit · 256 banks of 64 KB
Work RAM
128 KB
$7E–$7F · refreshed on-die
DMA channels
8
General DMA + per-line HDMA
Math unit
8×8 · 16÷8
Unsigned multiply & divide
Key takeaways
  • The SNES CPU chip is the Ricoh 5A22: a 65C816 core plus Nintendo's on-die peripherals.
  • Those peripherals are the DMA/HDMA controllers, the multiply/divide unit, I/O and joypad ports, the H/V counters and timer IRQ, WRAM refresh, and the clock divider.
  • The core is the processor you program; the 5A22's extras do the fast data movement and math around it.
  • One 21.477 MHz master clock feeds the three region access speeds from Module 06.
Module 08 · Part II

8-bit or 16-bit? The M & X flags

GoalUnderstand the 65816's headline feature: emulation vs native mode, and the M and X flags that resize the registers — and re-decode the very same bytes.

Here is the 65816's defining trick — and its most notorious trap. The chip can run in two personalities. It boots in emulation mode, where it behaves as a fast 6502: all registers 8-bit, the stack pinned to page 1. A game switches to native mode with a two-instruction spell — CLC then XCE (exchange carry with the hidden E flag) — and unlocks the full 16-bit machine.

In native mode two status flags choose register widths independently. The M flag sets the accumulator and memory width: M = 0 makes A a full 16-bit register, M = 1 keeps it 8-bit. The X flag sets the index registers X and Y the same way. You flip them whenever you like with REP (reset = clear flag bits, going 16-bit) and SEP (set flag bits, going 8-bit); each takes an operand mask naming the P bits to touch — REP #$30 clears both M and X (everything 16-bit), SEP #$20 sets just M (8-bit A, 16-bit indexes). Want 16-bit maths but 8-bit loop counters? Set M = 0, X = 1. It's yours.

Two follow-on traps hide in the resizing itself. Setting X = 1 is destructive: the high bytes of X and Y are forced to $00 and their old contents are gone — flip back to 16-bit and they stay zero. Setting M = 1 is gentler: A's high byte (called B) is hidden, not erased, and reappears intact when M returns to 0 — you can even swap it in and out with XBA. And emulation mode simply forces M = X = 1 outright, one more reason the CLC:XCE escape is the first thing games run.

And now the classic gotcha. An immediate instruction like LDA # carries its value inline — so how many operand bytes follow depends on the current M flag. With M = 0 (16-bit) A9 34 12 is LDA #$1234, a three-byte instruction. With M = 1 (8-bit) the very same opcode reads only one operand byte: A9 34 is LDA #$34, and the 12 that follows is now the next instruction. The identical bytes decode into completely different programs depending on invisible processor state. Get the flag tracking wrong — in your head, in a disassembler, or in an emulator — and everything downstream shifts. It's the single most common way to misread 65816 code.

Analogy · A recipe that reads in two languages

Imagine a recipe where “add 2 cups” means two cups in one chef's dialect but two and a half in another's — and the dialect can switch mid-page with a tiny margin note. Read on without noticing the note and every later line is off by an ingredient. The M and X flags are that margin note: REP/SEP silently change how many bytes each following instruction eats.

The lab makes it tangible. Toggle M and X and watch A, X and Y resize between 8 and 16 bits. Below them, a fixed stream of bytes is re-decoded live under the current flags — watch instruction boundaries slide as immediate operands grow and shrink, turning one byte stream into two different programs.

Width-flipper lab — M, X & the re-decoding byte streamsimulated · live
Native mode · click to flip: note the polarity: flag set (1) = 8-bit · flag clear (0, lit button) = 16-bit
Registers resize with the flags
The same bytes — A9 34 12 A2 10 00 E8 CA 60 — re-decoded
Key takeaways
  • The 65816 boots in 6502 emulation mode; CLC:XCE switches it to native 16-bit mode via the hidden E flag.
  • In native mode, the M flag sets accumulator/memory width and the X flag sets index width — each 8 or 16 bit.
  • REP #$30 clears the flags (16-bit); SEP #$30 sets them (8-bit) — toggle at will, even mid-routine.
  • X = 1 destroys the high bytes of X/Y (forced $00); M = 1 merely hides A's B byte; emulation mode forces M = X = 1.
  • Immediate operand length tracks the flags, so the same bytes decode into different instructions depending on M/X.
Module 09 · Part II

The 24-bit address space & banks

GoalMap the 16 MB the 65816 can reach — 256 banks of 64 KB — and learn where WRAM, the PPU, the APU, the CPU registers and cartridge ROM all live.

The 6502 could address 64 KB — 16 bits of address. The 65816 widens that to 24 bits: 16 MB, organised as 256 banks of 64 KB each. A full address is written bank:offset, like $7E:2000. The registers from Module 02 do the widening: the program bank PBR supplies the top 8 bits of the PC (which bank code runs in), and the data bank DBR supplies the bank for absolute data accesses. The direct page D gives a movable fast window inside bank 0.

What's actually at those addresses is the SNES memory map, and it's worth knowing by heart. The 128 KB of work RAM lives in banks $7E–$7F; the low 8 KB ($0000–$1FFF) is mirrored into the bottom of every “system” bank. The win isn't speed — the mirror is metered at the same 8 cycles — it's encoding: with the mirror under its feet, code can reach that RAM through short one-byte direct-page operands and plain absolute addresses, whatever the DBR currently points at, instead of spelling out 24-bit long addresses into $7E. The hardware registers sit in a narrow band of every system bank: the PPU (video) at $2100–$213F, the APU (sound) mailbox ports at $2140–$2143, the CPU registers (NMI control, the math unit, DMA enables) around $4200–$421F, and the DMA channel registers at $4300–$437F. Cartridge ROM fills the upper halves of the banks ($8000–$FFFF) and the big upper banks.

Analogy · A tower of identical floors

Picture a 256-floor tower, each floor a 64 KB bank. Some floors are pure storage (RAM in $7E–$7F, ROM up top). But every “system” floor shares an identical service corridor near the entrance — the same lift buttons to the PPU, the APU, the DMA desk — because the hardware is wired into all of them. The bank registers are which floor you're currently standing on.

The whole 16 MB, in one picture — banks $00–$FF across, offset $0000–$FFFF down (LoROM) every column is one 64 KB bank · thin register bands widened so you can see them $00 $20 $40 $60 $80 $A0 $C0 $E0 $FF $7E–$7F ↓ $0000 $2100 $2140 $4000 $4200 $4300 $8000 $FFFF WRAM mirror = $7E:0000–$1FFF PPU regs $2100–$213F CPU regs $4200–$421F DMA regs $4300–$437F open bus / expansion cart ROM LoROM 32 KB half cartridge $40–$7D · mapper-dependent FastROM mirror same map as $00–$3F, ROM readable at 6 cycles cartridge $C0–$FF · mapper-dependent WRAM $7E–$7F · its low-8KB mirror · $2180 port PPU ($21xx) & DMA ($43xx) registers APU ports $2140–$217F CPU registers & joypad I/O ($40xx–$42xx) cartridge ROM / SRAM unmapped — open bus (Mod 14)
The poster to keep. Banks run left to right, the 64 KB inside each bank runs top to bottom. The “system” banks $00–$3F all share one layout — WRAM mirror at the bottom, the $21xx and $4xxx register bands, a LoROM cartridge half on top — and banks $80–$BF repeat it as the FastROM mirror. The skinny cyan stripe is all 128 KB of real WRAM ($7E–$7F); register bands are drawn far thicker than true scale (the $21xx band is 256 bytes of a 65,536-byte bank).

The lab is a live map probe. Type any 24-bit address — or hit a preset — and it tells you which region (or mirror) that address lands in, with a bar showing where inside the bank it falls. Try $7E0100 (work RAM), $002100 (a PPU register), $004202 (the multiply register from Module 12), and $808000 (cartridge ROM).

Memory-map viewer — where does this address land?LoROM layout · live
Key takeaways
  • The 65816 addresses 24 bits: 16 MB in 256 banks of 64 KB, written bank:offset.
  • PBR sets the code bank, DBR the data bank, D the direct-page window — together they reach the whole space.
  • WRAM is $7E–$7F, with its low 8 KB mirrored into every system bank; PPU $2100, APU $2140, CPU/DMA $4200/$4300.
  • Cartridge ROM occupies the upper halves of banks and the big upper banks; the exact layout is the mapper's job.
Module 10 · Part II

DMA — feeding the PPU fast

GoalSee how the 5A22's DMA controller shovels blocks of memory into the PPU far faster than the CPU could — and why the CPU stops dead while it does.

A frame of SNES graphics needs a lot of data poured into the PPU during V-blank: new tiles into VRAM, new colours into the palette, new sprites into OAM. Doing it with the CPU — a loop of load, store, load, store — is painfully slow, and V-blank is short. So the 5A22 includes a DMA controller: eight channels that copy a block of memory into a hardware port at a fixed cost of 8 master cycles per byte. The two ends of the copy live on different buses. The source side rides the A-bus — the CPU's ordinary 24-bit address bus, the one that reaches WRAM and cartridge ROM. The destination is on the B-bus — a small 8-bit side bus that carries only the $21xx register ports (VRAM, palette, sprite memory and friends), which is why a DMA destination is named by a single port byte.

You set a channel up by writing its registers at $43x0–$43x6 (where x is the channel number): the control byte and transfer pattern, the destination port, the 24-bit source address, and the byte count. Then you write a single bit to $420B (MDMAEN) to arm the channels you want, and the transfer fires immediately. A common destination is $2118/$2119, the VRAM data port — feed it a stream and the PPU fills video memory. The transfer pattern chooses how bytes fan out across the destination ports: one register (mode 0), alternating two (mode 1, perfect for the low/high VRAM pair), and so on.

The price: while a DMA runs, the CPU is halted. The DMA engine owns the bus; the processor simply stops until the transfer completes. And at 8 master cycles per byte (≈ 2.68 MB/s), the budget is tighter than you'd hope. An NTSC V-blank is only about 38 scanlines — roughly 52,000 master cycles — which is room for just 5–6 KB of DMA before the beam starts drawing again. Filling all 64 KB of VRAM costs about 524,000 cycles — nearly a frame and a half. So real games budget: they split big uploads across several V-blanks, or switch the display off entirely (forced blank) during loading screens and transfer at leisure while nothing is drawn. It's a deliberate trade — pause the thinker so the mover can go flat out — but only for as long as the blank lasts.

Analogy · The bucket chain vs the fire hose

The CPU copying bytes one instruction at a time is a bucket chain: correct, but slow, and it ties up everyone. DMA is a fire hose bolted straight from the reservoir to the fire — you turn one valve ($420B) and it pours. You can't do anything else while the hose is running full-bore, but you don't need to: it's done in a blink.

One transfer, two buses — and a parked CPU WRAM $7E–$7F cart ROM tiles · maps · music A-bus · 24-bit · memory side DMA channel reads A-bus · writes B-bus 8 master cycles per byte B-bus · 8-bit · $21xx ports only $2104 · OAM (sprites) $2118/$2119 · VRAM $2122 · CGRAM (palette) $2132 · fixed colour CPU — halted parked off the bus until the copy ends ✕ no access arm with one write to $420B (MDMAEN) → the engine streams source → port, byte after byte, until the count runs out
Why the CPU must stop. There is one A-bus and the DMA engine needs it, so the processor is parked while the engine bridges memory onto the B-bus port row. Mode 1 alternates two adjacent ports — perfect for VRAM's low/high pair.

The lab is a channel you configure and fire. Pick a destination port and transfer pattern, set a byte count, and press Run: watch bytes stream from work RAM into the PPU port while the cycle counter climbs and the CPU sits halted. The elapsed microseconds show why DMA, not a CPU loop, is how the SNES moves graphics.

DMA lab — configure a channel & watch it streamsimulated · live
cost wall time
Key takeaways
  • DMA is a hardware copy engine — eight channels that move blocks from memory to a fixed hardware port.
  • Configure a channel at $43x0–$43x6, then arm it with a bit in $420B (MDMAEN); the transfer fires at once.
  • Transfer patterns fan bytes across destination ports — mode 1 feeds the $2118/$2119 VRAM pair.
  • DMA costs 8 master cycles per byte (≈ 2.68 MB/s) and halts the CPU for its duration — a deliberate trade.
  • One NTSC V-blank (~38 lines ≈ 52k master cycles) fits only ~5–6 KB of DMA — big loads span frames or run under forced blank.
Module 11 · Part II

HDMA — mid-frame magic

GoalUnderstand H-blank DMA: a tiny table-driven transfer every scanline that lets one register change value down the screen — the source of gradients and per-line effects.

Module 10's DMA runs once, in a burst, during V-blank. Its sibling is cleverer. HDMA — horizontal-blank DMA — performs a tiny transfer every scanline, in the sliver of time between drawing one line and the next. Instead of one big block up front, HDMA drips a handful of bytes to a hardware register 224 times a frame, so that register can hold a different value on every line of the screen. That single idea is behind a huge fraction of the SNES's signature look.

You arm HDMA channels by writing $420C (HDMAEN) instead of $420B, and each channel reads from a table in memory. The table format is compact: each entry starts with a line-count byte, followed by the data to write to the target register — or, in indirect mode, a pointer to that data. The count byte does double duty via its top bit. Values $01–$80 mean hold: write the data once, then keep it for 1–128 lines. Values $81–$FF switch on repeat mode: write fresh data on every line, for (count & $7F) lines. And $00 ends the table. A concrete table, in real bytes — say we're feeding the fixed-colour register: $20 $48 (hold: write $48 once, keep it 32 lines) · $83 $50 $58 $60 (repeat: 3 lines, each getting its own value) · $20 $00 (hold $00 for 32 lines) · $00 (end). The CPU sets the table up once; the HDMA hardware then walks it with no CPU instructions at all — though not quite for free: each active channel steals a few machine cycles from the CPU every scanline it writes (Module 14 counts the cost).

What can you paint with a register that changes every line? A colour gradient in the sky (drip a slightly different colour to the fixed-colour register each line). Per-line scrolling to shear the screen into parallax layers or wavy water. Changing the Mode 7 transformation matrix each line to bend a flat plane into a curved horizon. Moving the window registers to carve circular spotlights and iris wipes. All of it is “change one register, every scanline” — and all of it is HDMA.

Analogy · Repainting the fence as the parade passes

A single-DMA burst is painting the whole fence one colour before the parade starts. HDMA is a painter who dabs one fresh shade on the fence in the instant between each float passing — so by the end the fence is a smooth rainbow, even though only a dab was applied at a time. The “instant between floats” is H-blank; the dab is the tiny per-line transfer.

A frame, and the table that paints down it H-blank — HDMA writes land here V-blank · lines 225–261 — HDMA tables re-arm for the next frame visible lines 0–224 · beam sweeps left → right, top → bottom lines 0–31 · one colour, held lines 32–34 · fresh colour EVERY line lines 35–66 · $00, held the HDMA table in memory — real bytes $20 $48 hold: write once, keep 32 lines $83 $50 $58 $60 $83 = $80 repeat bit + 3 → three lines, one value each $20 $00 hold $00 for 32 lines $00 line count 0 → end of table each entry governs a group of scanlines; the repeat-bit entry ($83) delivers a different byte on each of its lines
Table on the right, screen on the left. Hold entries ($20 $48) set a value and coast; the repeat-mode entry ($83 …) hands the register a fresh byte on every one of its lines. Every write is squeezed into the H-blank sliver at the end of a scanline; the whole dance resets during V-blank.

The lab is HDMA painting a sky. It writes the fixed-colour register $2132 a new value on each scanline, interpolating between a top and bottom colour you pick — and animates the beam filling the gradient line by line, exactly as the hardware would. The table on the side shows the entries a gradient this smooth really needs: repeat-mode counts (top bit set) followed by one colour byte per line — a hold-mode table would paint bands, not a fade.

HDMA lab — a gradient, one scanline at a timesimulated · live
The HDMA table (set up once)
Key takeaways
  • HDMA does a small transfer every scanline, in H-blank, so a register can change value down the screen.
  • Channels are armed by $420C (HDMAEN) and driven by a table: a line-count byte ($01–$80 = hold; $81–$FF = repeat, fresh data every line) plus data, or a pointer (indirect).
  • It powers gradients, per-line scroll and parallax, per-line Mode 7 matrices, and window shapes.
  • The CPU builds the table once; the hardware walks it all frame with no CPU instructions — at the price of a few stolen cycles per active line (Module 14).
Module 12 · Part II

The math registers

GoalUse the 5A22's hardware multiply and divide — a peripheral the 65816 core lacks — and learn why you must wait a set number of cycles before reading the answer.

The 65816 core has add, subtract, and bit operations — but no multiply and no divide instruction. Doing them in software is slow, and games need them constantly (scaling, positioning, dividing a span into steps). So Ricoh built a small multiply/divide unit into the 5A22, reached not through instructions but through memory-mapped registers: you write the operands to certain addresses, wait, then read the result back from others. It's a peripheral, like DMA — the core just moves bytes to and from it.

Multiply is unsigned 8×8→16. Write the first operand to $4202 and the second to $4203; writing $4203 starts the calculation. After 8 CPU cycles — machine cycles of the CPU, note, not master-clock ticks — the 16-bit product is waiting in $4216 (low) and $4217 (high). Divide is unsigned 16÷8. Write the 16-bit dividend to $4204/$4205 and the 8-bit divisor to $4206; writing $4206 starts it. After 16 cycles the quotient sits in $4214/$4215 and the remainder in $4216/$4217. The counts aren't arbitrary: the unit is a shift-and-add machine that grinds out one bit of the answer per cycle — 8 cycles for an 8-bit multiplier, 16 for a 16-bit quotient. Even division by zero is defined rather than fatal: the quotient comes back $FFFF and the remainder is the dividend, passed through untouched — the lab reproduces exactly that.

That wait is the crucial detail. The unit isn't instantaneous — it grinds through the math over those 8 or 16 cycles. Read the result registers too early and you get a half-finished value, a subtle bug that plagued real 65816 programmers. In practice you just make sure a few instructions pass between writing the last operand and reading the result — easy once you know, mysterious if you don't. (And an emulator has to model exactly this delay to run buggy games faithfully — a Part III theme.)

Analogy · The vending machine

You put in your operands (press the buttons), and the machine whirs. Reach into the tray immediately and it's empty — the can hasn't dropped yet. Wait the few seconds it takes and the answer is there. The math unit is exactly this: the whir is those 8 or 16 cycles, and grabbing the tray early gets you nothing useful.

Four registers, two meanings — the result wires are shared divide · quotient lo/hi divide · remainder lo/hi $4214RDDIVL $4215RDDIVH $4216RDMPYL $4217RDMPYH multiply · 16-bit product lo/hi — same two registers multiply leaves $4214/5 alone the ritual, in time: write $4203 (or $4206) whir: 8 / 16 CPU cycles one answer bit per cycle read $4216/7 (·$4214/5) read here, mid-whir → half-finished bits
Why product and remainder collide. $4216/$4217 is one pair of registers with two jobs: multiply parks its product there, divide its remainder. Start a divide and the last product is gone. The bottom strip is the ritual the vending-machine analogy describes — and the red mark is the classic too-early read.

The lab is the math unit with its wait modelled. Choose multiply or divide, set the operands, and press Go: the cycle bar fills over the 8 or 16 cycles, and only when it's done do the result registers show the true answer. Notice the register names match the real hardware — and that the product and the division's remainder share $4216/$4217.

Math-unit lab — multiply / divide with the cycle waitsimulated · live
12
10
Cycle wait
write the operands, then start the unit
Key takeaways
  • The 65816 core can't multiply or divide; the 5A22 adds a hardware unit reached through memory-mapped registers.
  • Multiply (8×8→16): write $4202 and $4203, wait 8 cycles, read the product from $4216/$4217.
  • Divide (16÷8): write $4204/5 and $4206, wait 16 cycles, read quotient $4214/5 and remainder $4216/7.
  • You must wait the cycles before reading — an early read returns a half-finished value.
Part III

Emulating it in bsnes

Finally: how does software on your PC stand in for the whole 5A22 — the CPU, the PPU drawing beam, the sound chip, the DMA engines — all at once, and all in step? Your processor can't run a single 65816 instruction, so an emulator must interpret them; but the harder half is timing, because on real hardware everything shares one clock. These three modules cover how bsnes runs the CPU, the accuracy traps that separate a screenshot-correct emulator from a truthful one, and the debuggers where you can watch it all with your own eyes.

Modules 13–15Difficulty EmulationLabs callbacks to Part I–II
Module 13 · Part III

Interpreters & cycle accuracy

GoalUnderstand how bsnes interprets the 65816 on a single shared master-clock scheduler — and why stepping one clock at a time is what makes it faithful.

Strip the problem to its core: bsnes holds the game's memory in a big array, and in that array sit 65816 instructions — numbers, as Module 01 taught you. Your CPU can't execute them, so bsnes runs an interpreter: a software re-enactment of the fetch–decode–execute loop. It keeps a variable for the PC and for each of the 65816's registers, reads the opcode byte at PC, looks at it, jumps to the matching handler (“this is ADC: add this memory value to the emulated A, set the flags”), advances the virtual PC, and repeats. It is Module 01's toy lab, grown up to the full instruction set — including the M/X width tracking from Module 08, which the interpreter must follow byte by byte.

But the Super Nintendo is not just a CPU. While the CPU runs, the PPU is drawing the beam across the screen, the APU's SPC700 is generating sound, and the DMA engines may be moving memory — and on real hardware all of them share the one 21.477 MHz master clock from Module 06. The genius of bsnes is that it emulates that literally. A single scheduler steps every component off the same master clock: run the CPU forward a few clocks, run the PPU forward the same few, run the APU, keep them all pinned to the same instant. This is cycle accuracy — the emulator's model of time is the hardware's model of time.

The alternative, which faster emulators of the past took, is instruction stepping: run a whole CPU instruction, then catch the other chips up in a lump. It's quicker, but it smears time — the PPU “sees” the CPU's writes only at instruction boundaries, not at the exact clock they happened. For most games that's invisible. For the ones that lean on the precise interleaving of CPU, PPU and DMA — reading a beam-position counter mid-instruction, timing an effect to a specific clock — only the shared single-clock model gets it right. That's why bsnes's author built it around one clock: one clock matters because the real machine only ever had one.

Analogy · The orchestra and the one conductor

An orchestra where each player follows their own watch drifts apart in seconds. A real orchestra follows one conductor's baton — every player on the same beat. Instruction stepping is players glancing at the score only at the end of each bar; cycle accuracy is watching the baton on every single beat. The master-clock scheduler is that conductor, and the 5A22's 21.477 MHz clock is the beat.

Same instruction, two models of time master clock bsnes · cycle-stepped CPU PPU mid-instruction write → the PPU sees it on this exact clock ✓ …both advance a few clocks at a time, forever in step older emulators · instruction-stepped CPU one whole instruction, run in one go PPU catch-up lump — replays the same span afterwards the same write happens here… but the PPU only learns about it later, at the lump — time has smeared ✗
Where accuracy lives. Top: the shared-clock scheduler slides CPU and PPU forward in tiny alternating slices, so a store that lands mid-instruction reaches the PPU on the true master cycle. Bottom: instruction-stepping finishes the whole instruction first, then replays the PPU in a lump — the write arrives late, and any game timing an effect against the beam sees the smear.

Look back at the toy CPU (Module 01). An interpreter is literally that lab, scaled up: a PC variable, register variables, a dispatch on the opcode, a handler per instruction. bsnes genuinely contains one — the difference is that its loop advances a shared clock every step, so the drawing beam and the sound chip stay welded to the CPU at all times.

Key takeaways
  • An interpreter re-enacts fetch–decode–execute in software: a PC, register variables, a per-opcode handler.
  • bsnes steps the CPU, PPU, APU and DMA off one shared 21.477 MHz master clock — a single scheduler.
  • Cycle-stepping keeps every chip pinned to the same instant; instruction-stepping smears time to boundaries.
  • One clock matters because the real console only ever had one — games can depend on the exact interleaving.
Module 14 · Part III

The accuracy traps

GoalTour the places where a fast SNES emulator and a truthful one pull apart: open bus, DMA/HDMA timing, the H/V timer and early IRQs, the notorious CPU↔APU boot sync, and self-modifying code.

If Module 13 made cycle accuracy sound like a solved recipe, this module is the cold shower. The traps are all the things the real 5A22 does implicitly — leftover values on the bus, transfers that steal exact cycles, counters that tick with the beam, chips that must agree at boot — which software has to reproduce explicitly and precisely. Miss one and the emulator isn't slow; it's wrong, often in a way that only shows up in one scene of one game.

Trap 1 · Open bus

Not every address has hardware behind it. When the CPU reads an unmapped address, real silicon doesn't return a clean zero — it returns whatever value was last on the bus, because nothing drove new bits onto the wires. This is open bus, and some games genuinely depend on the specific leftover value they get back. An emulator that returns 0 (or a random number) there will run most games and then mysteriously break the one that read open bus on purpose.

Trap 2 · DMA and HDMA steal exact cycles

Module 10's DMA halts the CPU — but for exactly how long, and starting on exactly which master cycle, matters. HDMA is worse: it interrupts the CPU for a few cycles on every scanline, at a precise point in H-blank, and the amount stolen depends on how many channels are active and their transfer sizes. Get the count or the timing of these steals slightly wrong and any game that counts cycles — or times an effect against the beam — drifts out of sync.

Trap 3 · The H/V timer and the “early IRQ”

First, a correction to a widespread misattribution: the beam-chasing hardware lives in the 5A22 itself, not in the PPU. It's the CPU chip (Module 07's peripheral list) that keeps horizontal and vertical counters tracking the beam, and it's the CPU chip you program: write a target position into $4207–$420A and the timer fires an IRQ when the beam gets there, acknowledged by reading $4211 (TIMEUP). The PPU's only role is on the reading side: touching $2137 latches the counters — freezes a snapshot of the moving beam position — and software then reads that snapshot from $213C/$213D. The exact relationship between the CPU's clock and those counters is delicate, and there's a famous class of bug where an IRQ fires one cycle early because the timer-to-CPU timing was modelled a hair off. One cycle is enough to make a raster effect tear or a game hang.

Trap 4 · The CPU↔APU boot handshake

The single most infamous trap. The sound subsystem is a whole separate CPU — the SPC700 — running on its own clock, and the two processors talk through the four mailbox ports at $2140–$2143 (Module 09). At boot they perform a precise handshake: the main CPU uploads the sound driver, and both sides step through an agreed protocol whose timing games silently rely on. Because the two chips run on different clocks that only bsnes-style scheduling keeps correctly related, a small timing error makes the handshake desync — and the game hangs on a black screen at boot. Whole eras of emulators fought this exact bug.

The boot handshake — two chips, two clocks, one fragile conversation 5A22 CPU steps off 21.477 MHz SPC700 (APU) its own crystal · ~24.576 MHz-derived boot ROM: $2140 ← $AA · $2141 ← $BB — “I'm ready” $CC + destination address — “here comes the driver” data byte via $2141 + index i via $2140 echoes i back through $2140 — “got byte i” ↺ repeat for every byte of the sound driver — thousands of round-trips if the emulator's clock ratio drifts: CPU reads the echo a hair early, sees the OLD index, resends — both sides now wait on each other forever → black screen
A conversation with no referee. The upload protocol is pure convention riding on the $2140–$2143 mailboxes — each side just polls until the other's byte appears. Get the two clocks' ratio wrong by a whisker and one read lands one cycle early, the echo check fails, and the handshake deadlocks. Only a scheduler that keeps both clocks honestly related (Module 13) survives it.

Trap 5 · Programs and data share memory

Module 01 called this “the single most important idea in all of computing” and promised it makes emulators sweat. Here's the sweat. Because instructions are just bytes in the same memory as data, nothing stops a program from writing over its own instructions — and SNES games really do it: code copied into WRAM and patched on the fly, loop operands rewritten each frame, self-modifying code of every flavour. A naive interpreter survives this for free — it re-reads memory every fetch. But the moment an emulator gets clever and caches anything derived from those bytes (pre-decoded instructions, translated blocks), it must watch every write and throw away any cached copy the write just invalidated. Miss one store and the emulator cheerfully executes code that no longer exists.

Analogy · The certified flight simulator

A flight sim that only looks right is a video game; one a licensing examiner accepts must match the aircraft's numbers, because pilots build reflexes against the real machine's behaviour, not its appearance. Games are those pilots: a thousand accidental dependencies on open-bus values, exact DMA steals, precise counter timing and the boot handshake. Fast gets the plane in the air; accurate is what keeps three decades of software from noticing it changed aircraft.

Why not always be maximally accurate? Because faithfully modelling every cycle-steal, counter and open-bus read is expensive — the reason cycle-accurate cores like bsnes/higan need far more host horsepower than the faster, looser emulators that came before. The art is a model that's truthful where games can tell and cheap where they can't; the reference cores simply refuse to cut the corners at all.

Key takeaways
  • Open bus: unmapped reads return the last value on the bus, and some games depend on it.
  • DMA and especially HDMA steal exact cycles at exact times; sloppy timing desyncs cycle-counting games.
  • The H/V timer lives in the 5A22 ($4207–$420A, $4211); the PPU only latches counters for reading ($2137, $213C/D). Mis-timing the IRQ by one cycle is a classic, visible bug.
  • The CPU↔APU (SPC700) boot handshake across separate clocks is the notorious make-or-break sync.
  • Programs can rewrite their own bytes — anything an emulator caches from memory must be invalidated on writes.
Module 15 · Part III

Try it in bsnes / Mesen

GoalTurn the whole course into things you can watch for yourself — the debuggers, tracers and register viewers — then close the loop back to Module 01.

You now have the full picture, so let's make it hands-on. Modern SNES emulators ship debuggers that expose every mechanism this course described. Mesen 2 (mesen.ca) gives you the most complete 65816 debugger going; bsnes (github.com/bsnes-emu/bsnes) is the reference cycle-accurate core this course leaned on, and its debug-oriented fork bsnes-plus adds a similar tool set. All are accurate enough to trust what they show you. Here's what to open and what you'll recognise:

  • The disassembler & CPU register view — see A, X, Y, S, the P flags (including M and X), and the PBR/DBR/D bank registers live. Watch the flags flip on a REP/SEP and the disassembly re-length its immediates, exactly as your Module 08 lab did.
  • The trace logger — dump every instruction the CPU executes, with the effective address each addressing mode resolved to (Module 04) and the cycle it ran on. This is the fetch–decode–execute loop of Module 01, written to a file.
  • Breakpoints & the memory viewer — break when the code writes $420B (a DMA kicking off, Module 10) or $420C (HDMA, Module 11), or reads $4216 (a multiply result, Module 12). Watch the memory map from Module 09 light up as regions are touched.
  • The event/PPU viewer — see NMI fire at V-blank and IRQs at their beam positions (Module 06), and watch HDMA writes march down the scanlines painting a gradient (Module 11).

Three experiments worth actually running

  • Catch a mode switch. Set a breakpoint on the first XCE after boot and step it. You'll watch the machine leave 6502 emulation mode and become the 16-bit 65816 — Module 08, live, on the first few instructions of nearly every game.
  • Follow a DMA. Break on a write to $420B, then read the channel registers at $4300–$4306 the game just set up: source, destination port, size. You've just decoded a real transfer by hand (Module 10).
  • Watch HDMA paint. Find a game with a gradient sky, open the PPU/event viewer, and watch a colour register change value scanline by scanline (Module 11). The gradient in your lab is happening for real, 224 times a frame.

And that's the course. You began with a cook and a stack of numbered cards; you end able to read a 65816 trace, explain why the same bytes can decode two ways, and predict which register a DMA or the math unit will touch. The fetch–decode–execute loop from Module 01 is still down there, under everything, turning — on a 1990 games console, on the machine you're reading this with, and in bsnes faithfully pretending to be one on the other, one shared clock at a time.

Course complete ✦
  • You can trace a frame end-to-end: the CPU sets up DMA/HDMA, the beam draws, NMI fires at V-blank, repeat.
  • You know the 65816 model — A/X/Y/S/P, the M & X width flags, the 24-bit banked address space — and the 5A22's DMA, HDMA and math peripherals.
  • You understand why bsnes runs everything off one master clock, and the accuracy traps that make it hard.
  • You can open a debugger and map every register and event to a mechanism you now understand.

An interactive course in the “Inside the Super Nintendo” collection. Every lab on this page is a from-scratch simulation written for teaching — no game code, no copyrighted material, nothing emulated for real. Hardware details are grounded in public documentation of the 65816 and the Ricoh 5A22; figures we couldn't pin down precisely are marked “about” on purpose.

Register map · $2100–$213F PPU · $2140–$2143 APU · $4200–$421F CPU · $4300–$437F DMA · WRAM $7E–$7F