Search › A-Z › P › Pole Position
Pole Position
Pole Position (Namco, 1982) is the first-person racing landmark. Its real main board is a pair of Zilog Z8002 16-bit CPUs plus a Z80 and the Namco 3-voice WSG for sound. This build ships a faithful, single-CPU from-scratch Zilog Z8002 interpreter (the non-segmented 16-bit Z8000) with the new z8000 disassembler and the full in-frame debugger — the hard, novel part. There is no bundled game ROM, so the default program is a public-domain Z8002 demo authored here: it programs the WSG, draws a perspective road with a steerable car and polls the wheel, exercising the CPU, video, sound and input end to end. Booting the original dual-Z8002 board and its road/sprite video is a documented follow-up.
Runs on: Web browser
Pole Position Online Emulator
Play Pole Position using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| Pole Position (Namco/Atari, 1982) | Pole Position | Pole Position | grey | Open ⛶ |
Machine emulated
The Pole Position main board (Namco, 1982) pairs two Zilog Z8002 16-bit CPUs (the non-segmented Z8000, 3 MHz) with a Zilog Z80 and the Namco 3-voice WSG for sound and custom road/sprite video. This emulator implements a from-scratch single-Z8002 interpreter and drives an authored public-domain demo; the full dual-CPU board is a follow-up.
Chips
Notes
Embedding
The whole machine is one hand-written file, pole-position.js, exposing a global PolePosition. It decodes the authentic ROM set (base64 in rom/polepos-roms.js, global PP_ROMS), builds the palette and graphics from the real PROMs, wires the Z80 master (Z80.js) and the two Zilog Z8002 game CPUs to the shared video/sprite/road RAM, and paints the 256×224 screen. Vendor the four files and drive a loop you own:
var core = PolePosition.create(canvas); // canvas becomes 256x224
core.reset();
(function loop(){ core.runFrame(); core.render(); requestAnimationFrame(loop); })();
Plain JavaScript. Everything the host or debugger needs is a method or field on the object:
| Member | Kind | What it does |
|---|---|---|
core.runFrame() | method | Advance one 60 Hz field with a TRUE time-ordered interleave: the Z80, both Z8002 and both MB8843 MCUs each carry an absolute cycle clock (MAME z8000tbl costs) and only the furthest-behind one is stepped, so the two subs stay phase-locked. Real interrupt cadence (Z80 IRQ at 64V/192V, Z8002 NVI at VBLANK, 06xx /NMI). The game program composits the frame itself. |
core.step() | method | Execute one instruction on Z8002 game CPU #1 — the single-step primitive the debugger drives. |
core.R · core.pc | fields | The Z8002 #1 register file (R0..R15) and program counter, read and pokeable live. |
core.peek(a) · core.poke(a,v) | methods | Side-effect-free read / write of the Z8002 #1 address space (ROM + shared RAM) for the memory views. |
core.setInput(mask,down) | method | Press/release a control bit (wheel, accelerator, brake, gear, start, coin). |
core.onSound(off,val) | hook | Every write into the Namco WSG register window; the host forwards it to a NamcoWSG built on the real waveform PROM. |
core.ev52 · core.ev54 | fields | Per-field DAC event logs from the real 52xx (voice) and 54xx (noise) MCUs — flat [cycle, value…] arrays the host turns into audio in polepos-voices.js. core.frameCycles is the CPU-cycle span the timestamps use. |
core.engineLatch() | method | The car engine-sound latch — RPM pitch (lsb/msb) and the CLSON enable — driving the discrete engine wavetable synth. |
Debugger integration
The debugger plug-in (pole-position-debug.js) reads the live core each refresh and calls EmuKit.defineMachine. It targets Zilog Z8002 game CPU #1 — the CPU that runs the real game code — through the shared z8000 decoder:
- CPU decoder.
/debugger/src/cpus/z8000.jsdisassembles the non-segmented Z8002 (theLD/ ALU /INC/DEC/ single-operand /LDK/LDA/ shift /PUSH/POP/CALL/RET/JP/JR/DJNZgroups, big-endian 16-bit words, RH/RL byte naming). Pointed at the realsub1ROM it disassembles the genuine Z8002 game program. - Registers. R0..R15, the PC, the FCW status word and each flag (C, Z, S, P/V, DA, H) are read live and written back.
- Memory. The Z8002 #1 space (ROM + shared sprite/road/alpha/view RAM), side-effect-free reads.
- Single step is one
step(); breakpoints compare the PC before each instruction; write watchpoints wrap the write path. All host-side, so they cost nothing when the debugger is closed.
Architecture
Namco/Atari's Pole Position (1982) is an unusually heavyweight board for its era, and this build runs its genuine ROM set:
- Three CPUs. A Zilog Z80 "master"/sound CPU plus two Zilog Z8002 16-bit game CPUs (the non-segmented Z8000) sharing the sprite, road, alphanumeric and background RAM.
- Namco custom MCUs (real). The 06xx serial bus with the 51xx (coin/credit/inputs/protection) and 53xx (steering + dips) customs — each a Fujitsu MB8843 run from its genuine ROM on a from-scratch MB88xx core (a port of MAME's
mb88xx.cpp). The 06xx port (namco06.cpp) runs the clock divider, pulses the Z80 /NMI one byte per transfer and each selected MCU's /IRQ with the R/W line. The 52xx (voice, an MB8843) and 54xx (noise, an MB8844) sound customs run on the same MB88xx core from their genuine ROMs: the 52xx walks the voice sample ROM and streams a 4-bit DAC (the Fuji speech), the 54xx streams three 4-bit noise channels, both triggered by the game's 06xx commands. - Custom video. A background tilemap, a 4bpp sprite layer with hardware zoom (driven by a vertical scale-LUT PROM), an alphanumeric overlay, and the road generator that builds the scaling perspective road from three road ROMs and a road-colour PROM. The palette and many lookup tables live in PROMs. The renderer is a direct port of MAME's
polepos_v.cpp. - Sound. The Namco 3-voice WSG (its real waveform PROM
pp1-5.3b), the Namco 52xx voice and 54xx noise customs (real MB8843/MB8844 MCUs from their genuine ROMs), and the discrete engine-sound section (the engine wavetable ROM clocked by the RPM latch) — all mixed toEmuAudio.
Honest scope. Every ROM is real and SHA1-verified, every graphic is decoded from those ROMs through a faithful port of the real video hardware, the Z80 master + both Z8002 execute their real ROM code with the full Z8002 instruction set, and the Namco 51xx/53xx MCUs run their genuine MB8843 ROMs over the real 06xx handshake. There is no director; no forced flags. The game boots past the self-test and sustains: the 51xx coin/protection handshake passes, both Z8002 start and stay phase-locked, the attract mode cycles (title screen and demo race), and a coin plus the accelerator reaches a live, steerable qualifying lap. The two subs are held in lockstep by a cycle-accurate time-ordered interleave (MAME z8000tbl costs) so sub #1's scene-init memory clear and sub #2's live stack in the shared sprite RAM never race. Sound is now the whole board: the Namco WSG, the 52xx Fuji speech and 54xx noise (real MB8843/MB8844 MCUs from their genuine ROMs), and the discrete engine tone are all synthesised and mixed. The one documented approximation is the analog back-end — the R-ladder DACs are modelled as conductance-weighted 4-bit DACs and the op-amp filters as their exact filter2 biquads (real component values) rather than a full SPICE node solve. See README "WHAT WORKS".
Sound
The whole Pole Position sound board, mixed to one EmuAudio output: the Namco 3-voice WSG, the Namco 52xx voice and 54xx noise customs, and the discrete engine-sound section.
Namco WSG. The core exposes the 0x40-byte sound register window; every write invokes core.onSound(offset, value), forwarded to a NamcoWSG built on the real waveform PROM pp1-5.3b (eight 32-sample waveforms, low nibble to the DAC) — not a synthesized table.
52xx voice + 54xx noise. Both customs are real Fujitsu MCUs (MB8843 / MB8844) run inside the core from their genuine ROMs, triggered by the game's own commands over the 06xx bus (custom #2 / #3, /IRQ pulsed by the 06xx timer). The 52xx walks the voice sample ROM and the 54xx streams three noise channels; the core time-stamps every 4-bit DAC write (with the emitting MCU's CPU-cycle clock) into core.ev52 / core.ev54. polepos-voices.js reconstructs each sample-and-hold DAC waveform at the audio rate and passes it through the board's analog back-end (the discrete polepos_discrete DAC ladders and op-amp filters, ported as filter2 biquads).
Engine. The discrete engine tone is the polepos_sound_device: an 8-slot wavetable in the genuine engine ROM (pp1_15/pp1_16) read by a phase accumulator whose pitch is the car RPM latch the game writes at $a200/$a300 (exposed as core.engineLatch()), then the three engine op-amp filters — a faithful port of MAME's sound_stream_update.
Render path. Once per video frame the shim renders Math.round(EmuAudio.sampleRate/60) interleaved-stereo Int16 samples: wsg.render() fills the buffer, then voices.mixInto() adds the 52xx + 54xx + engine on top, and the frame is pushed to EmuAudio.
Mute contract. transport.setMute/isMuted delegate to EmuAudio; audio starts muted and the Sound button unmutes from a real gesture.
Honest scope. Every voice is driven by real hardware — the WSG PROM, the two voice/noise MCUs from their genuine ROMs, and the engine wavetable ROM under the game's RPM latch — so the Fuji speech, the tyre/engine noise and the engine tone all follow the game live through attract and the qualifying lap. No sound is a pre-recorded clip; there is no director. The one approximation is the analog back-end: the R-ladder DACs are modelled as conductance-weighted 4-bit DACs, and the op-amp filters as their exact biquads (real R/C values) rather than a full SPICE node solve; the 52xx output rail clamp is applied symmetrically to keep the mix AC-centred.