How the Genesis 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 console's two brains: the Motorola 68000 that runs the game and the Zilog Z80 that drives the sound, the bus that forces them to share memory, and finally how emulators reproduce both at once. Fifteen modules, each with a live, clickable machine — including a real, tiny 68000 assembler you can step. No game code ships with this page; every simulation is built in your browser.
How CPUs work
Instructions, registers, binary and two's complement, branches and the stack, interrupts — the universal vocabulary of processors, explained from zero.
The two brains
The Motorola 68000, its addressing modes and instruction set, the Z80 sound CPU, the shared bus and its arbitration, the 24-bit memory map, and the console's interrupts.
Emulation
Interpreters vs recompilers, keeping two CPUs and a video chip in step, the timing traps that make accuracy hard, and the settings that map to it all.
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 two chips we'll meet in Part II. We build the vocabulary one word at a time — instruction, register, binary, branch, 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 five ideas wired together.
The fetch–decode–execute loop
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. Every game the Genesis ever ran — every Sonic loop, every Streets of Rage punch, every pixel — is that loop, running fast.
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: 0x1E sitting in memory might
mean “load from cell 14” to the processor, or it might
just be the number 30 in someone's data. Position and convention
decide, nothing else. Programs and data live in the same memory, side
by side — 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 small counter called the program counter (PC). Each trip round the loop it fetches the instruction the PC points at, then normally bumps the PC forward so the next trip fetches the next one. The exception is a branch (a “jump”): an instruction whose entire job is to overwrite the PC with a new address, so execution leaps somewhere else. Branches are how programs make decisions and loops — Module 04 is all about them.
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 millions of cards a second and never misreads one.
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 (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 = load
cell n into A, 2n = add
cell n to A, 3n = store A into
cell n, 4n = jump to
cell n.
Now put this five-instruction program in cells 0–4, with data in
cells 12–14: 1E 2D 2C 3E 40. Read it
with the table above: 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 with your own eyes; press
Run and watch it become a program. This is a
deliberately generic CPU — in Module 08 we replace it with a real,
working 68000.
- 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 is an instruction that rewrites it.
- An instruction's number splits into an opcode (what to do) and operands (what to do it to).
Registers, memory & the ISA
Our toy CPU had one working register, A. Real processors have a couple of dozen, 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. It holds one value — on the 68000, 32 bits — and reading or writing it costs almost nothing. Registers are the workbench; memory is the warehouse. There are only a handful of registers precisely because they're so close: you can't fit a warehouse on a workbench.
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. Good code, like a good carpenter, organises the work to stay at the bench as long as possible.
Now, an important fork in the road — one this course leans on. There
are two philosophies about how instructions may touch memory. In a
RISC
machine, arithmetic is only allowed register-to-register,
and the only instructions that touch memory are explicit loads and
stores. In a
CISC
machine, a single instruction may reach into memory, compute, and
write the result back to memory, all by itself. The 68000 — the
Genesis' main CPU — is proudly CISC: an instruction like
ADD.W D0,(A1) reads a word from the address in A1,
adds a register to it, and writes it back, in one line. Fewer, more
powerful instructions; the price is that each is more elaborate
inside. Keep this contrast in your pocket — it explains why the
68000's instruction set (Module 08) feels so expressive, and why
emulating it (Module 13) is a particular kind of work.
What “the 68000” actually names
Here's a distinction worth getting straight early. An instruction set architecture (ISA) is a contract: the documented list of instructions, the registers, and exactly what each instruction must do. An implementation is a physical chip that honours the contract. Motorola 68000 (the “m68k”) is both a specific 1979 chip and the seed of a whole family — the 68010, 68020, up to the 68060, plus embedded CPU32 cores — all speaking a compatible ISA. The Genesis uses the original 68000 (in the Model 1, a discrete Motorola/Hitachi part; in later models, the same core folded into a bigger ASIC). Any chip honouring the contract runs the same programs; how fast is the implementation's own business. This split is exactly what makes emulation possible: Genesis Plus GX and BlastEm (Part III) implement the 68000 contract in software on your PC.
The 68000 contract that matters to us is a clean, roomy register file — one of the reasons programmers loved this chip:
| Registers | Width | What they hold |
|---|---|---|
| D0 – D7 | 32-bit | Eight general-purpose data registers: integers, counters, pixels, flags — the arithmetic workbench. |
| A0 – A7 | 32-bit | Eight address registers: pointers into memory. A7 doubles as the stack pointer (Module 04). |
| PC | 24-bit used | The bookmark from Module 01. The address bus is 24 bits, so it reaches 16 MB. |
| SR / CCR | 16-bit | The status register; its low byte is the condition-code register — flags X N Z V C (Module 08) — its high byte holds the interrupt mask (Module 05). |
Two register banks, eight of each, all 32 bits wide even though the chip talks to memory 16 bits at a time — a generous, orthogonal layout that made the 68000 a joy to program by hand. We'll put every one of these to work in Part II.
- Registers are a few dozen ultra-fast slots inside the CPU; memory is the big, slow warehouse.
- RISC forbids arithmetic-on-memory; CISC (the 68000) allows a single instruction to read, compute and write memory.
- An ISA is a contract; a chip is an implementation. The 68000 is a family, and emulators implement its contract in software.
- The 68000 gives you eight 32-bit data registers (D0–D7) and eight 32-bit address registers (A0–A7, with A7 = stack pointer).
Numbers in binary
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 the
68000's 32-bit registers cover a bit over four billion values.
Writing long binary strings by hand is miserable, so everyone uses
hexadecimal — base 16, digits 0–9 then A–F. Its
charm is that one hex digit is exactly four bits, so a byte is two
neat hex digits and a 68000 word is four. When you see
$C00000 or 0xFF0000 later, that
$ (the traditional 68000 prefix) or 0x just
means “read this in hex”. Get comfortable now: hardware
addresses are always written this way.
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 a car's odometer rolling over — except we
relabel the top half of the patterns as the negatives. In 8
bits, 11111111 is −1, 11111110 is −2, down
to 10000000 = −128. The lovely consequence: the
same adder circuit adds signed and unsigned numbers correctly without
knowing which it's doing — and to negate a number you simply flip
every bit and add one. The price is a fixed window (−128…+127 for a
byte) and arithmetic that silently wraps when you leave it — a wrap
the 68000 flags for you (Module 08).
An old car odometer at 000000, wound back one click,
shows 999999 — everyone reads that as
“−1”, not “nine hundred thousand”.
Two's complement
is the same trick in binary: the counter has a fixed number of
wheels, and the numbers just below zero are the all-nines
(all-ones) patterns at the top. Nothing about the wheels changed —
only how we agree to read the top half.
Bytes, words & longwords — and which end comes first
The 68000 groups bytes into three sizes you'll meet constantly: a
byte (8 bits, .b), a
word (16 bits, .w), and a
longword (32 bits, .l). Most
instructions let you pick the size. But storing a multi-byte value in
byte-addressed memory forces a choice: which byte goes in the lower
address? The 68000 is
big-endian
— the most significant byte goes first, at the lowest
address, the way we write numbers left-to-right. So the longword
$12345678 sits in memory as
12 34 56 78. Your PC's x86 CPU is the
opposite (little-endian), which is exactly the kind of mismatch an
emulator has to bridge byte-by-byte (Module 14).
Now go poke the bits yourself. Every square below is a button. Flip
them and read the same pattern three ways at once — as an unsigned
number, as a two's-complement signed number, and in hex.
Load the presets to meet the classic citizens: how −1 is
all-ones, why 10000000 is the most-negative value with no
positive twin, and where the wrap happens. Then hit
negate and watch invert-and-add-one turn a number
into its negative — the exact move the 68000's NEG
instruction makes.
- Binary counts in powers of two; hex is its four-bits-per-digit shorthand, and
$/0xflag it. - Two's complement relabels the top half of the patterns as negatives, so one adder handles signed and unsigned alike; negate = invert + 1.
- The 68000 works in bytes (.b), words (.w) and longwords (.l), and stores multi-byte values big-endian: most significant byte first.
- Big-endian vs your PC's little-endian is a real mismatch an emulator must bridge.
Branches, the stack & subroutines
A program that only ran straight down its list of instructions could
never make a decision or repeat anything. The escape hatch is the
branch from Module 01 — an instruction that
overwrites the PC. Unconditional branches (BRA,
“branch always”) jump no matter what. The interesting ones
are conditional: BEQ (branch if equal),
BNE (branch if not equal), BMI (branch if
minus), and a dozen more. Each asks a yes/no question and only jumps
if the answer is yes.
But branch on what? On the flags —
single-bit results left behind by the previous arithmetic. When the
68000 compares or subtracts two numbers, it records: was the result
zero (the Z
flag)? Negative (the N
flag)? Did it carry or overflow? A comparison followed by a
conditional branch is the universal shape of an if:
“compare A with B; branch-if-less to the else-part”. A
loop is the same with the branch pointing backwards. We'll meet the
68000's full flag set — X N Z V C — and watch it change live in
Module 08.
“If you open the door, turn to page 40; if you flee, turn to page 12.” That instruction is a conditional branch: a jump to a new page (address) taken only under a condition. And “go back to page 3 and try again” is a loop. The book's page number is the program counter; your decision is the flag the branch tests.
The stack: the CPU's scratch pad of returns
Programs are built from subroutines — reusable chunks you can call from many places (draw a sprite, play a sound). Calling one is easy: jump to its address. The hard part is coming back — back to wherever the call came from, which differs every time. The answer is a stack: a region of memory used last-in-first-out, like a spike you skewer receipts on. The 68000 dedicates A7 as the stack pointer, always pointing at the top of the stack.
When the 68000 executes BSR or JSR (branch/
jump to subroutine), it does two things: it pushes
the return address onto the stack (writes it there and moves A7), then
jumps. The subroutine runs, and finishes with RTS
(return from subroutine), which pops that address
back off the stack into the PC — landing exactly where it left off.
Because the stack is last-in-first-out, subroutines can call
subroutines that call more subroutines, nesting as deep as memory
allows, and each RTS unwinds one level. This same stack
also saves registers and holds local variables — and, crucially, it's
where the CPU stashes its state when an interrupt fires,
which is the very next module.
BSR pushes the address of the next instruction onto the stack and jumps; the subroutine runs and ends with RTS, which pops that address back into the PC. Nesting works because the stack is last-in-first-out.- A branch rewrites the PC; a conditional branch does so only when a flag (zero, negative, carry…) says to.
- Compare-then-branch is the shape of every
if; a backward branch is a loop. - The stack is last-in-first-out memory addressed through A7, the 68000's stack pointer.
BSR/JSRpush a return address and jump;RTSpops it back — which is how subroutines nest.
Interrupts & exceptions
So far our CPU only does what its program tells it, in order. But a games console is full of hardware with its own sense of timing: the video chip finishes drawing a frame, a controller button changes, a timer expires. The CPU can't afford to sit in a loop constantly asking “are we there yet?” for each. It needs the hardware to tap it on the shoulder. That tap is an interrupt.
An interrupt is a signal wire into the CPU. When a device raises it,
the 68000 finishes its current instruction, then automatically does
something remarkable: it pushes its current state onto the
stack (the PC and status register from Module 04), and jumps
to a special routine — an
interrupt handler
— written to deal with that event. The handler does its quick job
(read the controller, kick off the next frame's work), then executes
RTE (return from exception), which pops the saved state
back and resumes the interrupted program exactly where it
was, as if nothing happened. The main program never even knows it was
paused.
A chef is chopping vegetables when the doorbell rings. They don't
ignore it and they don't stand waiting by the door all evening —
they finish the current chop, set the knife down exactly where
it was, answer the door, then return and pick the knife back
up mid-carrot. The doorbell is the
interrupt;
setting the knife down is pushing state to the stack; picking it
back up is RTE. The genius is that the vegetables never
notice.
How does the CPU know which handler to run? Through the
vector table: a list of handler addresses parked at
the very bottom of memory. Each kind of event has a numbered slot.
The 68000's table lives at address $000000 and is why the
first two longwords of every cartridge are so special — slot 0 is the
initial stack pointer and slot 1 is the
initial PC (the reset vector), loaded automatically
when the console powers on. Further slots hold the addresses of the
interrupt handlers and of the
exception
handlers: an exception is an interrupt raised by the CPU
itself when something goes wrong — a divide by zero, an
illegal instruction, an odd-address word access. Same machinery,
internal trigger.
The 68000 also supports priority: interrupts carry a level 1–7, and a mask in the status register lets code ignore anything below a chosen level, so a high-priority event can interrupt a low-priority handler but not vice-versa. Hold that thought — in Module 12 we'll see the Genesis wire its video chip to two specific levels (VBlank at 6, HBlank at 4) to drive the raster tricks that gave Sega games their look.
RTE. Exceptions (errors) use the same path, triggered from inside the CPU.- Interrupts let hardware tap the CPU on the shoulder instead of the CPU polling in a loop.
- On an interrupt the 68000 saves PC + SR to the stack, runs a handler, and resumes with
RTE. - The vector table at
$000000routes each event to its handler; slots 0 and 1 are the reset SP and PC. - Exceptions are internally-triggered interrupts (errors); interrupt levels 1–7 plus a mask give priority.
The Genesis' two brains
Now we put the Part I toolkit into real silicon — and the Genesis has an unusual amount of it. Where most consoles have one CPU, the Genesis has two: a 16/32-bit Motorola 68000 running the game, and an 8-bit Zilog Z80 running the sound, inherited wholesale from the Master System. These seven modules cover the 68000 itself — its registers, addressing modes and instruction set — then the Z80, the bus they fight over, the 24-bit memory map that ties everything together, and finally the interrupts that pace the whole machine to the television.
Meet the Motorola 68000
The Motorola 68000 is the Genesis' main processor — the chip that runs the game. Introduced in 1979, by the late '80s it was the workhorse of serious computing: the original Macintosh, the Amiga, the Atari ST, countless arcade boards. Sega's choice of it for the Mega Drive in 1988 is a big part of why the console punched above its weight — arcade programmers already knew this chip intimately, and ports came naturally. In the Genesis it runs at 7.67 MHz on NTSC machines (7.60 MHz on PAL), derived by dividing the master crystal.
The “16/32-bit” label needs unpacking, because it's the key to the chip's character. Internally the 68000 is a 32-bit machine: its registers (Module 02) are all 32 bits, and it computes on 32-bit longwords. But it talks to the outside world through a 16-bit data bus — it fetches memory two bytes at a time — and a 24-bit address bus, giving it a 16 MB address space. So a 32-bit longword access takes two bus cycles. This split — 32-bit brain, 16-bit mouth — is why you'll see it called both a 16-bit and a 32-bit CPU, and why the “16-BIT” on the console's lid was marketing shorthand for the bus, not the whole story.
Imagine an accountant with a huge desk who can lay out enormous 32-column ledgers and work on them all at once — that's the 68000's 32-bit interior. But the office door is only wide enough to carry two columns of paper at a time, so fetching a full ledger from the archive means two trips. The 16-bit data bus is that doorway: it doesn't shrink the desk, it just meters how fast paper moves in and out.
The 68000 is not alone on the board. It is the master of the system, but it shares the console with a cast of specialist chips, each doing what it does far better than a general CPU could:
A note on honesty, since it comes up: Sega's “blast processing” was pure marketing. There is no such feature. The phrase gestured, loosely, at the console's ability to push data quickly using the VDP's DMA (Module 10) — genuinely useful, but not a mode you switch on, and not a property of the 68000 at all. When a spec sounds like a slogan, it usually is one. The real 68000 is impressive enough on its facts; the next two modules put it to work.
- The Genesis' main CPU is a Motorola 68000 at 7.67 MHz (NTSC) — the same chip behind the Amiga, Atari ST and countless arcade boards.
- It is 32-bit inside (registers, arithmetic) but talks to memory over a 16-bit data bus, with a 24-bit address bus reaching 16 MB.
- It is the system master, sharing the board with the Z80 sound CPU, the VDP and the sound chips.
- “Blast processing” was a marketing phrase, not a feature — it loosely referred to fast VDP DMA.
The register file & addressing modes
We met the register file in Module 02: eight data registers D0–D7 for arithmetic, eight address registers A0–A7 for pointers, with A7 as the stack pointer. What makes the 68000 a pleasure to program is not the registers themselves but the rich set of ways an instruction can specify where its operand lives. This is the chip's CISC heart (Module 02) on full display. Each way is an addressing mode, and every mode boils down to one question the CPU answers before it can act: what is the effective address (EA) — the real memory location this operand refers to?
The simplest modes don't touch memory at all.
Data-register direct (D3) means “the
operand is register D3”. Address-register
direct (A2) is the same for an address register.
Immediate (#42) means the value is a
constant baked into the instruction itself. No effective address is
computed; the data is right there.
The powerful modes use an address register as a pointer.
Register indirect — written (A1) — means
“the operand is in memory, at the address held in A1”. From
that base, four elaborations do most of the heavy lifting in real
code:
- Post-increment,
(A1)+: use the pointer, then advance it by the operand's size. This is how you sweep forward through an array — the whole of amemcpyis a post-increment read paired with a post-increment write. - Pre-decrement,
-(A1): back the pointer up first, then use it. Pair it with post-increment on another register and you have a stack — which is exactly how A7 pushes and pops (Module 04). - Displacement,
16(A1): add a fixed signed offset baked into the instruction. Perfect for a known field inside a record — “the health value is 8 bytes into the player struct”. - Indexed,
8(A1,D2): base register plus a variable index register plus a constant. Base = array start, index = element number, constant = field offset — one instruction reachesarr[i].field.
Finally, absolute modes ($C00000.L) name
a fixed memory address outright, with no register at all — how code
reaches the fixed hardware registers we'll map in Module 11. That
orthogonality — nearly any mode usable with nearly any instruction —
is the 68000's signature, and why hand-written assembly for it reads
so cleanly.
“Take this box” (immediate — you're holding it). “The box at number 12” (absolute — a fixed address). “The box where your finger is pointing” (register indirect — follow the pointer). “The box your finger points at — then move your finger to the next one” (post-increment). “Three doors past where you're pointing” (displacement). An addressing mode is just a grammar for saying which box, and the 68000 speaks an unusually expressive dialect.
The lab is the whole register file over a 64-byte memory window. Pick
a mode and an address register (and, where the mode uses them, an
index register and a displacement), and watch the 68000 compute the
effective address the way the silicon does — the arithmetic spelled
out, the target byte lit in memory. Start with (An), then
try (An)+ and 8(An,Dn) to feel how a single
operand can reach deep into a data structure.
- An addressing mode is a rule for computing an operand's effective address before the instruction acts.
- Direct and immediate modes touch no memory; register-indirect modes follow an address register as a pointer.
- Post-increment/pre-decrement walk arrays and build stacks; displacement and indexed modes reach fields inside structures.
- The 68000's orthogonality — most modes with most instructions — is what makes its assembly so expressive.
The 68000 instruction set in practice
With registers (Module 02) and addressing modes (Module 07) in hand,
the instruction set almost falls out. The 68000 has a famously
regular, human-readable assembly language built from a modest number
of operation families. MOVE copies data — from anywhere
to anywhere the addressing modes allow — and is by far the most
common instruction. ADD, SUB,
MULS, DIVS do arithmetic;
AND, OR, EOR, NOT
do bitwise logic; CMP compares; Bcc and
BSR branch (Module 04). Most take a
size suffix — .b, .w or
.l (Module 03) — so ADD.W works on 16 bits
and ADD.L on 32. There are handy shortcuts too:
MOVEQ loads a small constant fast, ADDQ/
SUBQ add or subtract a small number, and
DBRA decrements a register and loops in a single
instruction — the 68000's built-in for-loop.
The subtle, essential part is the condition-code register (CCR). Almost every arithmetic and data instruction leaves a fingerprint in five flags, and conditional branches read them. The 68000's five, in the order they sit in the register:
| Flag | Name | Set when… |
|---|---|---|
| X | Extend | A second copy of carry, kept for multi-precision arithmetic (ADDX). Unlike C, it isn't disturbed by every instruction — MOVE and CMP leave it alone. |
| N | Negative | The result's most-significant bit is 1 — i.e. the value is negative in two's complement (Module 03). |
| Z | Zero | The result was exactly zero. This is what BEQ/BNE test. |
| V | Overflow | The signed result didn't fit — e.g. adding two positives gave a “negative”. Two's-complement wrap, caught. |
| C | Carry | An unsigned carry out of (or borrow into) the top bit. |
The reason two flags exist for what looks like the same thing —
V (signed overflow) and C (unsigned carry) — is that the CPU
genuinely doesn't know whether your bytes are signed or unsigned
(Module 03 again). It computes both interpretations' error
conditions every time, and your choice of conditional branch
(BCS reads carry, BVS reads overflow,
BLT combines N and V for signed “less than”)
picks which one you meant. That is the whole trick of writing correct
comparisons on this chip.
A careful bookkeeper doesn't just write the total of a column — they scribble tiny notes in the margin: “came to zero”, “went negative”, “carried a digit”, “this overflowed the box”. Later, when deciding what to do next, they read the margin, not the whole sum again. The CCR flags are those margin notes, and a conditional branch is the bookkeeper glancing at one of them.
Enough theory — let's assemble and run some. The lab below is a
genuine, if small, 68000 assembler and stepper written in JavaScript.
It supports MOVE, MOVEQ, ADD/
ADDQ, SUB/SUBQ,
CMP, TST, CLR, the logic
instructions, Bcc/BRA and DBRA,
over D0–D7, A0–A7 and a small memory, computing the flags
size-correctly. The default program sums 1…5 with a
DBRA-style loop and stores the result to memory. Press
Assemble, then Step and watch the
registers change and — the point of the whole module — the
X N Z V C lights react on every instruction. Then
edit the code and run your own.
bne falls through.
MOVEdominates; arithmetic, logic, compare and branch families do the rest, most taking a .b/.w/.l size suffix.- Shortcuts like
MOVEQ,ADDQandDBRAmake common patterns one instruction. - The CCR's five flags — X N Z V C — record the last result; conditional branches read them.
- V (signed overflow) and C (unsigned carry) both exist because the CPU computes both interpretations; your branch choice picks which.
The Z80 sound CPU
The Genesis has a second processor, and it is a much older, humbler one: the Zilog Z80, an 8-bit CPU from 1976 that powered the ZX Spectrum, countless arcade machines — and Sega's own previous console, the Master System. It runs at 3.58 MHz with its own private 8 KB of RAM, entirely separate from the 68000's work RAM. Its job on the Genesis is sound: it runs the sound driver, a small program that reads music and sound-effect data and pokes the two audio chips — the YM2612 FM synthesiser and the SN76489 PSG — at exactly the right moments.
Why a whole second CPU just for sound? Two reasons, one practical, one historical. Practically, sound is relentless: FM channels need their envelopes and note changes serviced hundreds of times a second, on a tight schedule, and doing that on the 68000 would steal cycles from the game at the worst moments. Offloading it to a dedicated chip keeps the music rock-steady no matter how busy the game logic gets. Historically, the Z80 was already there: including it let the Mega Drive play Master System games with an adapter, since a Master System is essentially a Z80 plus a video chip. Sega got backward compatibility and a free sound coprocessor from one decision.
A busy restaurant kitchen (the 68000) can't also play live music — it's flat out cooking. So the restaurant hires a small house band (the Z80) that reads its own sheet music and plays without needing the chef's attention. Now and then the chef sends over a note — “play the victory fanfare” — but the band handles the tempo, the notes, the timing entirely itself. The sound driver is the band's sheet music; the 68000 just occasionally requests a song.
The two CPUs are only loosely coupled, and that coupling is the interesting engineering. The 68000 is the master: it can reset the Z80, halt it, and — the key mechanism — request the Z80's bus so it can reach into the Z80's RAM and the sound chips directly, for example to upload a new sound driver or trigger music. While the 68000 holds that bus, the Z80 is frozen. Getting this handshake right (and knowing exactly how many cycles the Z80 loses to it) is a real concern for both game programmers and emulator authors — it is the heart of the bus arbitration we build a lab for in the very next module. There is also a hardware wrinkle famous among developers: reaching the YM2612's registers from the 68000 side works, but the sanctioned path is through the Z80, and mishandling the bus timing can drop writes.
- The Genesis' second CPU is a Zilog Z80 at 3.58 MHz with its own private 8 KB RAM.
- It runs the sound driver, servicing the YM2612 FM chip and SN76489 PSG so the 68000 doesn't have to.
- Including it also gave the Mega Drive Master System backward compatibility (a Master System is essentially a Z80 + video chip).
- The 68000 masters it — reset, halt, and bus-request to reach the Z80's RAM and the sound chips directly.
Sharing the bus: 68000 / Z80 / VDP DMA
A bus is just a shared bundle of wires — address lines, data lines, control lines — that connects a CPU to memory and devices. The trouble is that a bus can serve only one master at a time, and the Genesis has several things that want to be master. This is bus arbitration, and on the Genesis it's a three-way negotiation whose outcome shapes performance in ways that don't show up on any spec sheet.
The most important sharing is between the 68000 and the VDP, the video chip. Both need the path to memory, and the VDP has a trick that makes it greedy: DMA (Direct Memory Access). When a game wants to blast a big chunk of graphics into video RAM — a new level's tiles, a full palette, a screen's worth of scrolling data — it programs the VDP's DMA engine to copy it, and the engine does so by itself, fast. But while a DMA runs, it holds the bus, and the 68000 is frozen for the duration: every DMA cycle is a 68000 cycle stolen. (This, and only this, was the seed of the “blast processing” slogan from Module 06 — fast DMA fills, dressed up.)
The timing of a DMA matters enormously. During vertical blank — the brief window each frame when the electron beam is off-screen and the VDP isn't drawing — DMA runs at full speed and the stolen 68000 cycles are “free”, because the CPU had little useful video work to do anyway. But a DMA during active display, mid-frame, competes with the VDP's own screen fetches and steals cycles the game may badly want. Skilled Genesis programmers schedule their big transfers into vblank almost religiously, for exactly this reason.
The third master is the Z80 (Module 09). Most of the
time it minds its own bus, but when the 68000 asserts
BUSREQ to reach the sound chips, the Z80 stalls until
released. So the full picture is a rotating handshake: the 68000 runs,
until the VDP steals the main bus for a DMA; the Z80 runs on its own
clock, until the 68000 borrows it. Everyone gets a turn; nobody gets
all of it.
Three trains, one shared stretch of track. A signaller lets one through at a time and holds the others at a red light. The express (the VDP's DMA) gets priority when it runs, and everything else waits; the local (the 68000) makes good progress until the express is scheduled; a branch-line service (the Z80) mostly runs elsewhere but occasionally needs the shared stretch too. Nothing is lost — but total throughput depends entirely on how cleverly the timetable packs the express runs into the gaps. That timetable is a Genesis programmer's real craft.
The lab is that timetable, for one frame. The top lane is the 68000 trying to execute; the middle lane is the VDP's DMA — scattered small transfers during active display plus one big fill in vertical blank; the bottom lane is the Z80. Drag the DMA size up and watch the 68000's red stall slices grow — and read off how much of the frame the CPU spent frozen. Toggle the Z80 bus request to see the Z80 lane stall when the 68000 reaches across. Replay it a few times; the finish moves with the DMA load.
- A bus serves one master at a time; the Genesis has three that want it — the 68000, the VDP DMA engine, and the Z80.
- VDP DMA holds the main bus and freezes the 68000 while it runs — fast fills bought with stolen CPU cycles.
- DMA in vertical blank is nearly free; DMA during active display steals cycles the game wants — so programmers schedule it into vblank.
- The 68000 can also halt the Z80 via BUSREQ to reach the sound chips; arbitration is a constant rotating handshake.
The memory map
The 68000 has 24 address bits, so it can name any of 16 million byte addresses (Module 06). But the Genesis has nowhere near 16 MB of anything — a few megabytes of cartridge ROM, 64 KB of work RAM, a handful of hardware registers. So most of that vast address space is empty, and the parts that aren't are scattered across it at fixed landmarks. That layout is the memory map, and knowing it is knowing the console: every act a program takes is a read or write to some address in this map.
How does a single address reach the right chip? Through address decoding. Simple logic on the console's board watches the top few address lines and switches on exactly one device for any given address. Because the decoding usually only looks at the high bits, each device actually answers across a big block — and often mirrors, appearing repeatedly, because the low bits within its block are ignored by the decoder. That's why the 64 KB of work RAM answers all across the top 2 MB of the map, and why the canonical “correct” address is a convention, not the only one that works.
The landmarks every Genesis programmer memorises: the cartridge
ROM lives at the very bottom, from
$000000 — which is why the 68000's reset vectors (Module
05) point straight into the cart, so the console boots the game's code.
The Z80's world sits at $A00000, the
I/O ports (controllers, region) at $A10000,
the Z80 bus-control registers just above at
$A11100, the VDP at $C00000,
and the 68000's work RAM up at the top, canonically
$FF0000. (Somewhere near $000100 in the ROM
sits the cartridge header — the ASCII string “SEGA MEGA
DRIVE” or “SEGA GENESIS” that later consoles' TMSS
check reads; that story belongs to the cartridge course.)
A postal sorter doesn't read the whole address to route a letter — the first digit or two of the postcode already says which district, and that's enough to drop it in the right sack. The address decoder is that sorter, reading only the high address bits; the districts are ROM, RAM, the VDP and so on. And just as a small village might be reachable by several near-identical postcodes, a device that ignores its low address bits appears mirrored many times across its block.
The lab is an interactive decoder. Type a 24-bit address, drag the
slider across the whole 16 MB space, or click a landmark — and watch
which device answers, with the address shown in hex and binary so you
can see the top bits doing the selecting. Click through
$000004 (the reset PC), $C00000 (the VDP data
port) and $FF0000 (work RAM) to walk the map a program
actually uses.
- The 68000's 16 MB space is mostly empty; real devices sit at fixed landmarks within it.
- Address decoding watches the top bits to enable one device — so devices answer across whole blocks and often mirror.
- ROM at $000000 (with the reset vectors), Z80 at $A00000, I/O at $A10000, VDP at $C00000, work RAM at $FF0000.
- Booting works because the reset vectors at the bottom of the map point straight into the cartridge ROM.
Interrupts on the Genesis
Module 05 built the general machinery of interrupts. Now we wire it to the television, because on a games console the screen is the clock. A TV draws the picture one horizontal line at a time, top to bottom, then flies the beam back to the top and starts again — 60 times a second on NTSC (224 visible lines), 50 on PAL (240 lines). The VDP knows exactly where the beam is at every instant, and it uses that knowledge to interrupt the 68000 at two precise moments.
The first is vertical blank (VBlank), raised at level 6, once per frame, the instant the beam finishes the last visible line and begins its trip back to the top. This is the game's heartbeat. The VBlank handler is where a game does everything that must happen exactly once per frame and must not tear the picture: update the scroll positions, upload the frame's new sprites and graphics via DMA (Module 10 — this is why DMA belongs in vblank), read the controllers, advance the music tick. Miss your VBlank budget and the frame drops; hit it every time and the game runs at a locked, smooth 60.
The second is subtler and is the source of the Genesis' signature visual tricks: the horizontal blank (HBlank) interrupt at level 4. It can fire in the tiny gap between visible scanlines, mid- frame — but firing on every one of 224 lines would drown the CPU, so the VDP has a programmable line counter: you tell it “interrupt me every N lines”, and it counts them down for you. Because the handler runs partway down the screen, it can change VDP settings that take effect for the lines below it — and that is how you get effects that were pure magic in 1990.
Picture a stage crew repainting a backdrop while the audience files in row by row. They can't repaint the rows already seated — but the moment a row fills, they can change everything for the rows still empty below. The TV beam is the filling audience, drawing top to bottom; the HBlank interrupt is the cue to the crew between rows. Change the scroll offset there and the bottom of the screen scrolls independently of the top — a locked status bar over a moving playfield. Change the background colour and you paint a gradient sky one band at a time.
The priority ordering is deliberate: VBlank at 6 sits above HBlank at 4, so the once-a-frame heartbeat can always preempt a mid-screen raster routine, but not vice-versa. Everything you'll see praised about Genesis graphics — the parallax hills, the heat-haze, the reflective water that ripples along a single line, the interface bars welded to a scrolling world — is, underneath, some programmer's HBlank handler firing on exactly the right line and rewriting a VDP register in the few microseconds before the beam moves on. It is Module 05's shoulder- tap, aimed with a stopwatch.
- On a console the screen is the clock: the VDP interrupts the 68000 based on where the TV beam is.
- VBlank (level 6) fires once per frame at the bottom — the game's heartbeat for scroll, sprites, DMA, input and music.
- HBlank (level 4) can fire mid-frame; a programmable line counter chooses which scanlines trigger it.
- Changing VDP state in an HBlank handler affects the lines below it — the basis of split-screens and raster effects.
Emulating in Genesis Plus GX & BlastEm
Finally: how does software on your PC stand in for all of this? Your processor speaks x86-64 or ARM64 — it cannot run a single 68000 instruction. An emulator must translate. But the Genesis makes the problem richer than a single CPU: there are two CPUs plus a video chip, all running on their own clocks, and the emulator must keep them in step. These three modules cover the translation machinery, the scheduling and timing traps where accuracy is won or lost, and the settings in real emulators — Genesis Plus GX, BlastEm and (for the FM chip) Nuked-OPN2 — that map to everything you've learned.
Interpreters vs recompilers: running two CPUs
Strip the problem to its core: an emulator holds the game's memory in
a big array, and in that array sit 68000 instructions — numbers, as
Module 01 taught you. Your CPU can't execute them. The direct solution
is an interpreter: a software re-enactment of the
fetch–decode–execute loop. Keep a variable for the PC and one for each
of the 68000's registers D0–D7/A0–A7 and the CCR; loop forever: read
the word at PC, look at its opcode bits, jump to the matching handler
(“this is an ADD.W: sum these two slots, set the
flags”), advance PC, repeat. It's Module 01's toy lab and Module
08's assembler, scaled up — and it is exactly what both Genesis Plus GX
and BlastEm do.
The cost is real: ten to fifty host instructions for each emulated one, paid again every time that instruction runs. The classic fix is a recompiler, or JIT: translate each straight-line run of guest code (a basic block) into equivalent x86-64 or ARM64 once, cache the result keyed by guest address, and every later visit is a lookup and a jump. Amortised over millions of executions, the one-off translation cost vanishes and the loop runs near host speed — the technique that makes emulating fast, modern consoles feasible at all.
Here's the honest twist for the Genesis, and it's a good lesson in engineering judgement: the recompiler payoff barely matters here. The 68000 runs at 7.67 MHz. A modern PC can interpret it — decode and re-execute every instruction, every time — hundreds of times faster than real time without breaking a sweat. So Genesis Plus GX and BlastEm choose interpretation on purpose: it is dramatically simpler, easier to make bit-exact, and the speed it leaves on the table is speed nobody needs. (BlastEm is celebrated precisely for its cycle-accurate interpreter.) The recompiler's amortisation argument is real — but it only pays when the guest is fast enough to hurt, which a 7.67 MHz chip simply isn't.
An interpreter is a live translator who re-translates a speech every night from scratch. A recompiler writes the translation down the first night and just reads it thereafter — a huge win if the speech is long and repeated enough to repay night one. For the Genesis, the “speech” is delivered slowly enough that the live translator keeps up effortlessly and never falls behind — so why hire the more complicated one? Sometimes the simple tool is the right tool.
There's a second, larger challenge unique to a multi-chip console: an emulator isn't running one CPU, it's running the 68000, the Z80 and the VDP, each on its own clock, and they interact (Module 10's bus, Module 09's sound handshake). The emulator advances them in tiny interleaved slices — run the 68000 for a few cycles, run the Z80 for its proportional share, let the VDP draw its lines, service interrupts at the right instants — so that from any chip's point of view the others behave exactly as the hardware did. Getting that scheduling right, not raw CPU speed, is where Genesis emulation actually gets hard (Module 14).
The lab races the two strategies over one hot loop, purely to make the amortisation concrete. Watch the recompiler pay its striped translation toll up front — at low repeat counts it actually loses — then drag the run count up and watch it pull ahead. Then remember the punch line: for a 7.67 MHz 68000, the interpreter's line was already fast enough, so the accurate emulators just… stay on it.
- An interpreter re-runs fetch–decode–execute in software: 10–50 host instructions per guest one, every pass.
- A recompiler translates each basic block to native code once and reuses it — amortisation is the whole trick.
- At 7.67 MHz the 68000 is easy to interpret faster than real time, so Genesis Plus GX and BlastEm deliberately keep the simpler, more accurate interpreter.
- The real difficulty is scheduling: interleaving the 68000, Z80 and VDP on their own clocks so they interact exactly as the hardware did.
Timing & the accuracy traps
If Module 13 made emulation sound like a solved problem, this module is
the cold shower. Translating an ADD is easy. The hard part
is everything the real hardware does implicitly — the exact
passage of time, the precise interleaving of three chips, the corner
cases of the bus — which software must now reproduce explicitly.
Accuracy isn't one dial; it's a series of specific traps, and each is a
place where an emulator author chose truth over convenience.
Trap 1 · Time is shared between chips
A real Genesis runs its 68000, Z80 and VDP simultaneously, and games depend on their exact relative timing. If the emulator runs the 68000 too far ahead before letting the VDP catch up, a game that writes a VDP register expecting the beam to be at a certain scanline will see the wrong line — and a raster effect (Module 12) lands in the wrong place, or a status bar tears. The fix is fine-grained interleaving: advance each chip in small steps and never let one race too far ahead of the others. Too coarse and you get subtle glitches; too fine and you burn host CPU on synchronisation. This balance is the single biggest quality difference between a “runs most games” emulator and a reference one.
Trap 2 · Cycle-exact DMA and bus stalls
Module 10's arbitration must be modelled to the cycle. When the VDP runs a DMA, the real 68000 freezes for a precise number of cycles that depends on where in the frame the DMA happens. Some games lean right up against that timing — kicking off a DMA and then doing exactly as much 68000 work as fits before the beam reaches a critical line. Model the stall as slightly too short or too long and the game's careful choreography drifts. BlastEm in particular built its reputation on getting these cycle counts right where looser emulators approximate them.
Trap 3 · Code that rewrites itself
A recompiler (if one is used) caches its translation of the guest's code — but Genesis games load new code from the cartridge, decompress routines into RAM, and occasionally patch themselves. The moment those bytes change, a cached translation of the old bytes is a lie, and the emulator must notice the write and invalidate the stale block or it will keep executing a ghost. (This is a headache recompilers carry and pure interpreters neatly sidestep — one more reason the accurate Genesis emulators favour interpretation from Module 13.)
Trap 4 · The sound chips are analogue-ish
The YM2612 FM chip (Module 09) is where “close enough” goes to die. Its real output has quirks — a slightly crunchy DAC, an infamous “ladder effect” distortion, exact envelope timing — that define the Genesis' sonic character. Early emulators approximated the chip and sounded subtly wrong to anyone who knew the hardware. The project Nuked-OPN2 answered this by emulating the YM2612 at the transistor-netlist level — essentially simulating the actual silicon — and Genesis Plus GX can use it for reference-grade FM. It's the audio equivalent of cycle-exact CPU timing: correctness measured against the real chip's bits and quirks, not against what sounds plausible.
A tribute band can play all the right notes and satisfy a casual listener. But hand the original master tape to someone who grew up on it and they'll hear every difference: the exact reverb, the slightly detuned lead, the tape hiss. Games are that superfan — a mountain of accidental dependencies on the exact timing, the exact bus stalls, the exact FM distortion. Plausible gets the notes right; accurate is what stops two decades of software from noticing it isn't playing on real hardware.
Why not just always be maximally accurate? Because precision costs host cycles — fine interleaving, cycle-exact bus modelling and netlist-level FM all add work. For the Genesis the budget is generous (Module 13: the guest is slow), so the best emulators can afford to be very accurate — which is exactly why Genesis Plus GX and BlastEm aim for reference-grade behaviour rather than trading it away. The lesson generalises: pick the accuracy the platform's speed lets you afford, and spend it where games can tell.
- The 68000, Z80 and VDP share time; fine interleaving keeps their interactions — especially raster timing — correct.
- DMA bus stalls must be cycle-accurate, because some games time their work right up against them.
- Self-modifying and freshly-loaded code makes cached recompilations stale — a hazard interpreters avoid.
- Faithful sound needs the YM2612's quirks; Nuked-OPN2 emulates the chip at the netlist level for reference-grade FM.
Try it in an emulator
You now have the full picture, so let's make it hands-on. Both Genesis Plus GX (widely available as a libretro core in RetroArch) and BlastEm expose debugging and configuration that map directly onto everything you've learned. Names shift between versions, so we'll describe each by what it does — which, pleasantly, you can now explain from first principles:
- The debugger / disassembler — BlastEm ships a built-in debugger; Genesis Plus GX has debug builds and the libretro core exposes state. Point it at a running game and you'll see live 68000 registers D0–D7/A0–A7, the PC, and the CCR flags X N Z V C — the exact board from your Module 08 lab, now driven by a real game. Single-step it and watch
MOVE,DBRAandBccdo their thing. - The VDP / plane viewer — most Genesis emulators can show the raw tile planes, the sprite list and the palette. Find a game with a status bar and you'll see the Module 12 split: the scroll value changing at one scanline.
- Interrupt / raster visualisation — some builds mark where on the screen interrupts fire. That horizontal line mid-frame is the HBlank of Module 12, live.
- Region & timing (NTSC/PAL) — switch a game between 60 Hz NTSC and 50 Hz PAL and watch the speed and pitch change. That's the 7.67 vs 7.60 MHz clock and the 224 vs 240 line count from Modules 06 and 12, felt directly.
- The Nuked-OPN2 FM option — where offered (Genesis Plus GX), toggle between the fast FM approximation and the netlist-accurate Nuked-OPN2 core and listen for the difference Module 14 described. On a good pair of headphones it's audible.
Before this course, an emulator's debug menus were a wall of jargon — you'd toggle something and hope. Now you've been inside the machine and traced every wire: the register panel is your Module 08 CCR, the plane viewer is Module 12's raster split, the FM toggle is Module 14's accuracy trade. The tools stop being intimidating the instant you know what each readout is soldered to.
Three experiments worth actually running
- Step the CPU. Open a debugger, pause a game, and single-step a few instructions. You'll recognise everything — the opcodes, the flags updating, a
BSR/RTSpair pushing and popping the stack from Module 04. The course, running for real. - Break the timing on purpose. If your emulator offers an “inaccurate/fast” timing mode, switch to it and find a game with fancy raster effects (a wavy water line, a parallax background). Watch the effect glitch or tear — that's Module 14's Trap 1 made visible.
- A/B the FM chip. Toggle Nuked-OPN2 on and off during a bass-heavy tune. Sorting out what changed is a live lesson in why bit-exact hardware emulation matters (Module 14).
And that's the course. You began with a cook and a stack of numbered cards; you end able to read a 68000 disassembly, explain why the Genesis carries a whole second CPU just for sound, predict which scanline a raster effect fires on, and say exactly why the accurate emulators interpret rather than recompile. The fetch–decode–execute loop is still down there, under everything, turning — on a 1988 games console, on the machine you're reading this with, and in the emulator faithfully pretending to be one on the other.
- You can trace a frame end-to-end: the 68000 runs the game, VBlank paces it, HBlank paints the tricks, the Z80 keeps the music, the bus arbitrates between them.
- You can read and step 68000 assembly and predict the CCR flags — you built a working assembler doing exactly that.
- You know why Genesis emulation favours accurate interpretation, and where its real difficulty (cross-chip timing) lives.
- Want the rest of the console? Continue with the graphics & VDP, FM & PSG audio, cartridge and homebrew courses.