Zaxxon
Zaxxon (Sega, 1982) is the pioneering isometric-scrolling shooter and the first arcade game to show its action in a three-quarter axonometric view: you pilot a fighter over and through a fortress and into deep space, judging altitude against your shadow to skim walls, clear force fields and refuel. This build is a from-scratch emulation of the Sega arcade board in the browser: a single Zilog Z80 main CPU that runs the game, driving the huge isometric background pixmap, the 32×32 sprite hardware and a two-bit foreground character layer coloured through resistor-DAC PROMs. It boots straight into its self-test and attract mode, and is wired to the emulators.org in-frame debugger, so you can single-step the Z80, read and write its registers and the 64K memory, and set execution breakpoints and write watchpoints.
Board ported from MAME's sega/zaxxon.cpp ↗
Runs on: Web browser
Zaxxon Online Emulator
Play Zaxxon using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| Zaxxon | Zaxxon | Zaxxon | grey | Open ⛶ | |
| Zaxxon (set 2) | Zaxxon | Zaxxon | grey | Open ⛶ |
Machine emulated
The Zaxxon arcade board (Sega, 1982) — a single Zilog Z80 at about 3.04 MHz driving Sega's isometric video hardware: a 256×4096-pixel scrolling background pixmap that gives the game its signature three-quarter view, up to 32 of the 32×32 three-bit sprites, an 8×8 two-bit foreground character layer for the score and text, and colour through resistor-DAC PROMs. The raster is scanned 256×224 and rotated 90° in the upright cabinet. Zaxxon was the first arcade game to advertise on television and a landmark of early pseudo-3D presentation.
Chips
Notes
Embedding
Zaxxon is Sega's 1982 arcade machine: a single Zilog Z80 main CPU at about 3.04 MHz that runs the game, plus a Sega sample/PSG sound board (not synthesised here). This build is a from-scratch JavaScript emulation whose video board is a port of MAME's sega/zaxxon.cpp and sega/zaxxon_v.cpp. The Z80 core is DrGoldfire's MIT Z80.js. The whole board runs from a host-owned requestAnimationFrame loop, so the debugger can pause and single-step it.
Boot. Constructing the machine decodes the character / background / sprite graphics once, builds the colour palette from the PROMs, resets the Z80 and starts the loop; the board comes up in its RAM self-test and then attract mode:
var m = new ZaxxonMachine({ maincpu, colorcodes }); // decode GFX, build palette, reset Z80
function frame(){ // one 60 Hz field
for (var c = 0; c < CYCLES; ) c += m.cpu.run_instruction();
m.vblank(); // raise the Z80 IRQ if interrupts are enabled
m.renderTo(buf32); // draw the 224x256 rotated frame
}
The machine object. Everything the host and debugger need is a field or method on the machine:
| Member | Kind | What it does |
|---|---|---|
m.cpu | field | The Z80 core (DrGoldfire Z80.js). Its state is read and written as one object through getState() / setState(), and it steps one instruction with run_instruction(). |
m.read(a) / m.write(a,v) | method | The Z80 memory bus. read is side-effect-free (it never disturbs the input latches), so the debugger's memory views use it directly. |
m.renderTo(buf32) | method | Render one whole frame — background pixmap, sprites, foreground characters — into a 224×256 Uint32Array, already rotated 90° for the upright cabinet. |
m.setInput(port, mask, down) / m.insertCoin(slot) | method | Drive the joystick / fire / start bits into the IN0/IN2 latches and pulse a coin. |
m.watchSet / m.watchHit | field | A Set of watched write addresses and the flag m.write raises when one is hit — the host loop's watchpoint hook. |
EMU_BOOT.transport | field | The pause / resume / step / breakpoint / watchpoint surface the shared debugger drives. |
Video. Zaxxon's isometric background is a huge 256×4096-pixel pixmap assembled once from the tilemap ROM; each field the renderer scrolls a 256×224 window through it, then draws the 32×32 three-bit sprites and the 8×8 two-bit foreground characters on top. The native 256×224 index buffer is rotated 90° (ROT90, with a cocktail flip bit) into the 224×256 canvas, colouring every pixel through the resistor-DAC palette built from the colour PROMs.
Debugger integration
Because the whole board is ordinary JavaScript and the host owns the run loop, the debugger's controls need no changes to the CPU core: the loop can pause, single-step and check breakpoints between any two instructions. window.EMU_BOOT.transport maps the shared debugger onto the Z80:
- pause / resume / isPaused — stop or restart the
requestAnimationFrameloop. - stepInsn(n) — run exactly n instructions through the core's
run_instruction()entry; single-step always makes progress, even sitting on a breakpoint. - step(n) — advance n whole 60 Hz fields (each a full timeslice of Z80 instructions followed by the vblank IRQ), then redraw.
- breakpoints — a
Setof PC values. When it is non-empty the loop drops into an instruction-at-a-time timeslice and comparesgetState().pcbefore each instruction, pausing before the matched instruction runs. - watchpoints — the machine's single memory-write path (
m.write) tests each address againstm.watchSetand raisesm.watchHit; the loop notices the flag and pauses on the write.
The registers window reads the Z80 core's state live and writes each field straight back: A and F, the B C D E H L bytes and their BC/DE/HL/AF pairs, the IX/IY index registers, SP, PC, the interrupt vector I and refresh R, and the S Z H P/V N C flags (each toggled independently). Register writes go through getState()/setState() so a single edit never clobbers the rest of the machine state. Memory views read through m.read, which returns program ROM, work RAM, video RAM, sprite RAM and the input ports without side effects, so inspecting memory never latches a phantom coin. The disassembler is the shared z80 decoder at /debugger/src/cpus/z80.js — reused unchanged, since the Z80 is already a first-class debugger CPU. The four memory chips exposed are the 64K CPU bus, the 24K program ROM, the 1K video RAM and the 256-byte sprite RAM.
Architecture
Zaxxon (Sega, 1982) is one of the first arcade games with an isometric (axonometric) three-quarter view, giving the illusion of flying over a 3-D fortress. The board is built around a single 8-bit CPU and Sega's tile/sprite video hardware:
- Main CPU — Zilog Z80 at roughly 3.04 MHz. Program ROM at
0x0000–0x5fff, 4 KB work RAM at0x6000, 1 KB foreground video RAM at0x8000, 256 bytes of sprite RAM at0xa000, the input ports and dip switches in the0xc000page, and the coin latch (U55) and video control latch (U56) in the0xc000/0xe000pages. A vblank IRQ each field drives the game. This is the CPU the debugger targets. - Sound — a Sega discrete/sample sound board triggered through an 8255 PPI; it is not synthesised in this build (the game plays silently), but the CPU-side latches are accepted so the game logic runs unchanged.
- Background — the signature isometric scroll: a 256×4096-pixel pixmap of 8×8 three-bit tiles, assembled once from a 32 KB tilemap ROM, scrolled diagonally by the video control latch's 11-bit position register. A BEN bit enables it and a CREF bit selects its colour bank.
- Sprites — up to 32 sprites, each a 32×32 three-bit object with independent X/Y flip, drawn from the 256-byte sprite RAM.
- Foreground — an 8×8 two-bit character tilemap (score and text) over the whole screen, transparent on pen 0, its per-column colour taken from a colour-code PROM.
- Palette — a 256-entry colour PROM turned into RGB by a resistor DAC (three bits each for red and green over 1000/470/220 Ω, two bits for blue over 470/220 Ω); a second colour-code PROM selects the foreground colours.
A 74LS259 addressed latch (U56) holds the interrupt-enable, background-enable, the two colour-reference bits and the background scroll position; a second latch (U55) holds the coin counters and the cocktail flip bit. The graphics ROMs are decoded to one-byte-per-pixel tiles and sprites once at boot.
Sound
Zaxxon has no programmable sound chip. On the real cabinet the effects are recorded analog samples played back by a sample board, plus discrete analog for the ship-engine drone; the CPU only writes sound-command latches to an Intel 8255 PPI. None of that audio is in the game ROM, so there is nothing to decode or route — this is a from-scratch synthesis (the AUDIO-GUIDE's Pattern S): we reproduce the command model faithfully and approximate each effect with a small synth voice.
The hook. The three PPI output ports sit at 0xe03c (A), 0xe03d (B) and 0xe03e (C) — and their 0x1f00 address mirror. The machine's memory-write path forwards writes there to machine.onSound(port, data); the boot loop points that at ZaxxonSound.portW. The latches are active-low (idle = 0xFF); following MAME's sega/zaxxon_a.cpp, a bit falling 1→0 starts a voice and, for the looped voices, rising 0→1 stops it.
The twelve voices. Port A carries the two looped engine tones (bits 2/3, their volume set by bits 0-1), the looped homing missile whistle (bit 4), the one-shot base missile (bit 5), the looped laser / force-field buzz (bit 6) and the looped battleship pulse (bit 7). Port B carries the enemy explosion (bit 4), ship explosion (bit 5) and the player cannon fire (bit 7). Port C carries the enemy shot (bit 0) and the two alarms — target-lock (bit 2) and low-fuel (bit 3). Tonal effects are square/saw/sine oscillators with pitch sweeps and envelopes; the explosions are enveloped white-noise bursts.
Rate & mix. The synth is built at EmuAudio.sampleRate, so no resampling is needed: every video field it mixes all active voices and pushes exactly Math.round(EmuAudio.sampleRate/60) interleaved-stereo Int16 samples (the same value duplicated to L and R) into the shared sink. transport.setMute/isMuted delegate to EmuAudio; audio starts muted (browsers block audio before a gesture) and the Sound button unmutes it from a real click.
Playing with sound. The board boots into attract mode. Insert a coin and press 1P Start to play, then click the Sound button to hear the engine drone and the effects as the game runs.
Caveat. The pitches and timbres are our own approximations — the genuine recorded Zaxxon samples are not present and are not synthesised note-for-note — but every voice is triggered by the real, unmodified game logic through the exact PPI command bits.