Battlezone
Battlezone (Atari, 1980), designed by Ed Rotberg, is the seminal first-person vector-graphics tank game: you drive a tank across a wireframe landscape of geometric obstacles, a distant volcano and mountains, hunting enemy tanks, saucers and guided missiles through a periscope gunsight. This build runs the original Atari board in the browser: a MOS 6502 driving the Atari Analog Vector Generator, with an AM2901 math box projecting the 3-D world and the original ROMs. It boots straight into attract mode, the tank battle demo playing out across the horizon, and is wired to the emulators.org in-frame debugger so you can single-step the 6502, read and write the registers and memory, watch the vector RAM display list build word by word, and set execution breakpoints and write watchpoints.
Visit the reference disassembly ↗
Runs on: Web browser
Battlezone Online Emulator
Play Battlezone using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| Battlezone | Battlezone | Battlezone | grey | Open ⛶ |
Machine emulated
The Battlezone arcade board (Atari, 1980) is a single MOS 6502 at 1.512 MHz driving the Atari Analog Vector Generator (AVG), with an AM2901 bit-slice "math box" doing the 3-D rotate-and-project arithmetic the CPU cannot. There is no framebuffer: the CPU builds a list of lines in the RAM it shares with the AVG and the AVG steers the beam to draw them on an X-Y monitor. It was the first mass-market first-person 3-D video game, and a variant was built for the US Army as a gunnery trainer.
Chips
Notes
Embedding
Battlezone is a MOS 6502 at 1.512 MHz driving the Atari Analog Vector Generator (AVG), with an AM2901 bit-slice math box doing the 3-D rotate-and-project arithmetic the CPU can't. There is no framebuffer: the 6502 builds a display list of vectors in the RAM it shares with the AVG, then strobes it to draw. This build vendors a self-hosted machine (no CDN): a JavaScript port of Mike Chambers' public-domain Fake6502, our ports of MAME's AVG state machine and math box, and the original Atari ROMs, driven from a host-owned loop so the debugger can control it.
Boot. The six program ROMs load at $5000–$7FFF (reset/NMI vectors at $7FFA–$7FFF, reached through the hardware's 15-bit address mirror) and the two vector ROMs at $3000; the 6502 starts from $FFFC and drops straight into attract mode:
var m = new BattlezoneMachine(); // loads ROMs, builds 6502 + AVG + math box
m.reset(); // pulls the reset vector at $FFFC
// one display field: 6 NMIs (~246 Hz) of 6144 cycles, then draw the AVG list
for (var s = 0; s < 6; s++) { m.cpu.nmi(); runCycles(6144); }
m.renderTo(ctx, W, H);
The machine is plain objects. Everything the host and debugger need is a field or method on the machine or its CPU:
| Member | Kind | What it does |
|---|---|---|
m.cpu.step() | method | Fetch, decode and run one 6502 instruction; returns its cycle count. The single-step primitive. |
m.cpu.nmi() | method | Pulse the non-maskable interrupt -- the ~246 Hz tick that drives the whole game. |
m.avgGo() | method | Run the AVG state machine over vector RAM/ROM and collect the frame's line segments. Triggered when the CPU strobes 200. |
m.mathbox.go(o,v) | method | The math box command window (860–87F): pokes an operand and, on the last write of a group, runs a projection whose result the CPU reads at 810/818. |
m.dbgRead(a) / m.memWrite(a,v) | method | Side-effect-free read and the CPU write path -- what the debugger's memory views read and poke. |
m.setInput(id,down) | method | Drive a control (lfwd/lback/rfwd/rback/fire/coin/start1) into the input latches. |
m.cpu.a/x/y/sp/pc/flags | field | The live 6502 registers, read and written directly. |
Video. Each field the loop runs one display's worth of 6502 (six NMI slices), then walks the AVG display list the game left in vector RAM and strokes the green wireframe lines to the canvas.
Debugger integration
The debugger drives a host-owned run loop: because the 6502 interpreter runs one instruction at a time, the loop can pause, single-step and check breakpoints between any two instructions -- no changes to the CPU core are needed. window.EMU_BOOT.transport exposes the controls the shared debugger calls:
- pause / resume / isPaused -- stop or restart the
requestAnimationFrameloop. - stepInsn(n) -- call
cpu.step()exactly n times and redraw, so a single step advances the PC by one instruction. - step(n) -- advance n whole display fields (each with its six NMIs).
- breakpoints -- a
Setof PC values. When non-empty the loop runs instruction-by-instruction and pauses before executing a watched address; when empty it runs a field at speed, so an idle debugger costs nothing. - watchpoints -- the machine's
memWriteis the single write path; it flags a hit when a watched address is written and the loop pauses on it. Because the 6502's registers and the whole map are ordinary JavaScript, registers are read and written directly and memory is exposed side-effect-free.
The plug-in (battlezone-debug.js) reads these hooks and calls EmuKit.defineMachine with the 6502 register set bound to the shared mos6502 disassembler, four memory chips (the program space, the program ROM, the vector RAM display list and the vector ROM shape library), and the dual-stick control panel. A neat trick for this machine: point the Vector RAM hex window at $2000 and single-step through a frame -- you can watch the 6502 (fed by the math box) build the tank and horizon into the display list word by word before it strobes the AVG at 200.
Architecture
Battlezone has no video RAM in the usual sense. A single MOS 6502, helped by a math-box coprocessor, builds a list of vectors in the RAM it shares with the AVG, then strobes it; the AVG reads that list and draws a first-person wireframe on an X-Y monitor. Modelled here as plain JavaScript objects:
- CPU -- a port of Fake6502 (NMOS 6502), reaching memory only through the machine's
memRead/memWrite, so the 15-bit bus and I/O live in one place. - AVG -- the Analog Vector Generator, a state machine clocked through a 256×4 PROM (
036408-01.k7): each step the PROM maps the current state, op-code and halt flag to the next state, three of whose bits fire the strobe handlers that latch vector words and integrate the beam, with Battlezone's clipping window so the 3-D view never overwrites the gunsight and radar. Ported straight from MAME'savg_bzone_device. - Math box -- an AM2901 bit-slice unit that does the rotate/project multiplies; modelled by Eric Smith's functional reimplementation (all 16-bit signed, exact two's-complement wrap via an
Int16Array) rather than its microcode PROMs. - Bus / I/O -- 1 KB RAM, IN0 (coins, service, VG-halt, 3 kHz clock), two DIP ports, the POKEY window (the two 2-way tank sticks read through ALLPOT, plus its RANDOM byte), the AVG GO/reset strobes and the math-box command window. Sound is stubbed.
- ROMs -- 12 KB of 6502 program (six mask ROMs) and 4 KB of vector ROM holding the letters, digits and shapes the display list calls into.
The one piece of timing the game truly needs is its ~246 Hz NMI (the 12.096 MHz master clock divided by 4096 then 12); the host loop reproduces exactly that, six NMIs per ~41 Hz display field, and renders the AVG list at the end of each field.