Combat (1977)#
Combat shipped in the box with the original console — for millions of people it was the Atari. Written by Larry Wagner with hardware help from Joe DeCuir, it packs 27 game variations (tanks, tank-pong, invisible tanks, biplanes, jets) into a 2 KB ROM. It’s also one of the most thoroughly annotated programs in 2600 history: the commented disassembly this teardown follows runs from Harry Dodgson’s original through Nick Bensema’s notes (1997) to Roger Williams’ full overhaul (2002).
It makes a perfect first teardown for one reason: its structure is exactly the frame loop this book teaches. Read Combat and you’re watching the TIA & Racing the Beam model run a real game.
The shape of the program#
Combat is a single loop. After a one-time START, every frame does the same three things — sync, think, draw — then jumps back:
VCNTRL emits the three VSYNC lines and arms the RIOT timer (TIM64T) to mark the end of vertical blank. Then the seven game-logic routines run during that blank — all the thinking happens where the beam isn’t drawing. Finally VOUT, the kernel, draws the whole screen, and JMP MLOOP begins again. The original Atari names for these top-level routines come from DeCuir’s own presentation notes; the discipline of “think in VBLANK, draw in the kernel” is the book’s frame model, named.
The seven jobs of a frame#
Each VBLANK routine is one chapter of this book at work:
| Routine | Does | Chapter |
|---|---|---|
GSGRCK | reads Select/Reset/difficulty, debounces, cycles variations | Buttons & Switches |
LDSTEL | sets NUSIZ widths and the color registers | Size & Copies |
CHKSW | reads joysticks, turns and accelerates the tanks | The Joystick |
COLIS | reads the collision latches, scores hits, bounces missiles | Collisions |
STPMPL | turns headings into HMOVE motion for players and missiles | Horizontal Positioning |
ROT | builds the rotated sprite buffer for this frame | Sprites |
SCROT | converts the BCD score into score-graphics offsets | Numbers, Scoreboard |
VOUT then draws a two-line kernel: the score up top (an asymmetric playfield showing two different numbers by rewriting PF1 mid-line), then the reflected maze and both tanks and missiles down the screen. The score trick is the kernel in miniature — for each row it packs a player’s tens and ones digits into one PF1 byte and writes it during the left half, then rebuilds the byte for the other player and rewrites the same register before the beam reaches the right half, chasing it across the line (the full mechanics are on the Scoreboard page).
Three clever bits worth the price of admission#
27 games from 27 bytes. Every variation is one byte in the VARMAP table — a bit-packed descriptor of features (tank vs. plane, guided vs. straight missiles, maze type, invisibility, billiard-hit…). At game-select those bits are unpacked into a few flag variables tested all over the code with BIT. The entire 27-game matrix is a lookup table and some bit masks.
The “Combat Stack Trick.” In the kernel, Combat needs to enable or disable each missile on the right scanline, fast. So it points the stack pointer at the missile registers (LDX #$1E / TXS) and uses PHP to write them — because a push stores the processor-status byte, and the Z flag sits in exactly the bit ENAM0/ENAM1 reads for enable. Set up the zero flag to mean “missile on,” PHP, and the missile toggles — no load, no store, just a push landing on a hardware register through address mirroring. It’s a famous hack, and a vivid lesson in counting cycles.
Mirror the artwork, don’t store it. ROT only keeps the first 180° of each tank’s rotation in ROM. The other half-circle it generates on the fly: set the TIA’s reflect bit (REFP) for the horizontal flip, and copy the sprite bytes last-to-first for the vertical flip. Half the rotation art, for free — and it interleaves the two players’ shapes into even/odd bytes so the two-line kernel can stream both.
Why Combat is worth reading#
Combat is this whole book compressed into 2 KB: a VBLANK/kernel frame loop, sprite positioning and reflection, an asymmetric score, hardware collisions, BCD, bit-packed game data, engine and bounce sounds, even an elaborate momentum system you barely notice while playing — all cooperating inside one tight loop. You can read every chapter here and still not feel how the parts combine until you watch a real game do it. Combat is where they combine.
Read the source. This analysis follows the publicly circulated Combat disassembly — Larry Wagner’s game, disassembled by Harry Dodgson, commented by Nick Bensema (1997), and overhauled by Roger Williams (2002). A copy is on GitHub at johnidm/asm-atari-2600 →
combat.asm. It’s well worth reading in full once the map above makes its shape legible.