SearchA-ZP › Pole Position

Pole Position

1982 Arcade Public domain Online

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.

Project home ↗

Runs on: Web browser

Pole Position Online Emulator

Play Pole Position using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Pole Position (Namco/Atari, 1982)Pole PositionPole PositiongreyOpen ⛶

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:

MemberKindWhat it does
core.runFrame()methodAdvance 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()methodExecute one instruction on Z8002 game CPU #1 — the single-step primitive the debugger drives.
core.R · core.pcfieldsThe Z8002 #1 register file (R0..R15) and program counter, read and pokeable live.
core.peek(a) · core.poke(a,v)methodsSide-effect-free read / write of the Z8002 #1 address space (ROM + shared RAM) for the memory views.
core.setInput(mask,down)methodPress/release a control bit (wheel, accelerator, brake, gear, start, coin).
core.onSound(off,val)hookEvery write into the Namco WSG register window; the host forwards it to a NamcoWSG built on the real waveform PROM.
core.ev52 · core.ev54fieldsPer-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()methodThe 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.js disassembles the non-segmented Z8002 (the LD / ALU / INC/DEC / single-operand / LDK / LDA / shift / PUSH/POP / CALL/RET / JP/JR / DJNZ groups, big-endian 16-bit words, RH/RL byte naming). Pointed at the real sub1 ROM 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 to EmuAudio.

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.