← All articles

Dig Dug's Last Round Is Built From the Wrong Tables

Dig Dug has no ending either. Clear 255 rounds and the 256th starts like any other: Dig Dug appears in the middle of the screen, the word READY comes up, and then, before you can touch the joystick, he is dead. He comes back on the same spot and dies again, and again, until the game is over. Look closely during the READY pause and you can see why: a Pooka is standing right where he is waiting. The round number in the corner reads 0.

Like the kill screens in Donkey Kong and Pac-Man, this was never designed. It comes from one byte that counts rounds, and from the way the game turns that count into a level layout. This article walks through that code in the original Namco arcade program.

How a round is built

Every round in Dig Dug is put together from three short lists stored in the program ROM: where the pre-dug tunnels go, where the monsters start, and where the rocks sit. There are 15 sets of these lists, one per layout.

The game keeps the round in a single byte of memory, at $840D for player 1, counting from 1. To pick a layout, it does this:

ld   a,(round)    ; the round number
cp   16           ; 16 or more?
jr   c,early      ; no: skip the next two lines
and  3            ; yes: keep the bottom two bits (0 to 3)...
add  a,12         ; ...and add 12
early:
dec  a            ; subtract one: round 1 uses layout 0

Rounds 1 to 15 get their own layouts, 0 to 14. From round 16 on, the game cycles through the last four, so round 255, for example, uses layout 14. It works for 255 rounds.

Two numbers that wrap

On round 256, two small things go wrong in a row.

How the round number picks a layout Rounds 1 to 15 use layout round minus 1, entries 0 to 14. Rounds 16 and up keep the bottom two bits of the round and add 11, cycling through entries 11 to 14; round 255 uses entry 14. Round 256 is stored as 0, which counts as an early round, and 0 minus 1 wraps to 255, an entry the tables do not have. ROUND LAYOUT 1 … 15 round − 1 0 … 14 16 and up bottom two bits + 11 11 … 14 round 255 → layout 14 256 stored as 0 − 1 255 0 is "less than 16", so it takes the early-round path, and 0 − 1 wraps to 255
The round number picks a layout. Round 256 is stored as 0, which takes the early-round path, and 0 minus 1 wraps to 255.

First, the round wraps. The byte holds 255 after round 255. When the next round starts, the game adds one, and 256 does not fit in a byte, so it wraps around to 0. Nothing checks for that. The display at the bottom right shows the round in two digits (round 255 reads ROUND 55), so round 256 reads ROUND 0.

Then the layout number wraps. Is 0 sixteen or more? No. So round 256 takes the path meant for rounds 1 to 15, and subtracting one from 0 gives 255. The game now wants layout 255, from tables that have 15 entries.

Three lists from the wrong places

To find a layout, the game multiplies the layout number by the size of an entry and adds it to the start of the table. That multiply is also done in a single byte, so it wraps too. The result is that each of the three lists is read from a different wrong place:

Where layout 255 is read from The three level tables sit end to end in the program ROM: rocks from $30F0, tunnels from $3168, monsters from $3258 to $32CF, then other data. With layout 255, the tunnel list is read from the first 16 bytes of the monster table, the monster list from $3350, past the end of the monster table, and the rock list from $31E8, the middle of the tunnel table. PROGRAM ROM, $30F0 TO $3370 rocks tunnels monsters other $30F0 $3168 $3258 $32D0 1 2 3 1 rock list: middle of the tunnels 2 tunnel list: first monster entries 3 monster list: past the end red: bytes read for round 256 (layout 255)
Where the three lists for round 256 are read from in the program ROM.
  • The tunnels are read from the start of the next table in the ROM, which holds the monster positions for rounds 1 and 2. Read as tunnel positions, those bytes point at four places that are not on the screen at all: one inside the program ROM, three at addresses where nothing is connected. The game draws its four tunnels there, where they cannot be seen. The only tunnel on the screen is the short one in the middle, which the game draws every round regardless.
  • The monsters are read from past the end of the monster table. All eight bytes there happen to be non-zero, so all eight monster slots get a monster, placed wherever those bytes say. They end up buried in solid earth, with no tunnels around them.
  • The rocks are read from the middle of the tunnel table, and all four of them land on the same row.

One of those eight monster positions is x $71, y $80. That is exactly the square where Dig Dug starts every life.

Caught before you can move

During play, the game checks each monster against Dig Dug: it adds up how far apart they are across and how far apart they are down, and if the total is under 10, he is caught. The Pooka on his start square is 0 away. The first time the check runs, as READY ends, it catches him.

When Dig Dug loses a life, the game puts him back on that square and puts the monsters back where the table says. The Pooka returns to the same spot, and it happens again.

Round 256, step by step

The original program was run under test scripts that reached round 256 in two ways: setting the round byte to 255 at the start of a game, and setting it to 254, then emptying round 255 of monsters so that the game finished that round and moved on to 256 by itself. Both gave the same results. The scripts recorded every byte the setup code read from the three tables, every byte it wrote, and when Dig Dug was caught.

What the code predictsWhat was recorded
The round byte reads 0 on round 2560
The layout number becomes 255 (round 255 used 14)255
The tunnel list comes from the monster tableIt did, at $3258
The monster list comes from past the end of its tableIt did, at $3350
The rock list comes from the tunnel tableIt did, at $31E8
The tunnels are drawn off screen48 bytes went to ROM or to nothing; 36 writes to unconnected addresses were logged
A monster starts on Dig Dug's squareOne did, at x $71, y $80, drawn as a Pooka
Dig Dug is caught as soon as play startsOn every life, at the end of READY

Holding the joystick from the start made no difference on any life, and pumping made none on the first (the only one recorded that way). On round 255 the same joystick input did not move Dig Dug until two frames after play began, too late. After the third life the game was over.

There is one thing the test cannot settle. The four invisible tunnels were written to addresses that the emulator treats as empty. On a real board those addresses might not be empty: depending on how the circuit board decodes addresses, some of them could land in screen memory or in one of the custom chips. So the exact look of round 256 on real hardware might differ. The Pooka on the start square comes from the monster list, which does not depend on that, so the outcome would be the same.

One caveat about the setup: no dump was available of the small Namco chip that reads the DIP switches, so a script answered the game's DIP switch reads with the factory settings instead. The game played normally with it.

The fix that was already there

The most surprising part is near the very start of the program. At address $0018 there are four instructions that do nothing but this: if the round byte has just wrapped to 0, set it to 156. Called straight after the round is increased, they would send the player from round 255 to round 156 and the game would carry on forever. Right next to them is a routine that reduces a number modulo 100.

Nothing in this version of the program appears to call either of them: the disassembly found no call, and breakpoints on both were never reached, through round 255, round 256 and three lost lives.

According to the notes in MAME's source, the Atari version of Dig Dug (its revision 2) does call that routine, so its round counter rolls over to 156 and the round display hides the jump. That version was not available to check here. If those notes are right, the Namco board checked here ends at round 256 and that Atari board never does. The earlier versions of both were not checked.

Credits and further reading

  • The MAME project's Galaga and Dig Dug driver (galaga.cpp), for the hardware memory map, and for its notes on the differences between Dig Dug versions, which are the source of the claims about the Atari set above.
  • "Dig Dug" on Wikipedia, which describes round 256 the same way (an overflow to round 0 and a Pooka on top of Dig Dug) and credits a video by Sharopolis, "The Dig Dug Kill Screen", that was not watched for this article.
  • The technical notes behind this article, with every address and measurement, are on the round 256 page of the Dig Dug case study.