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.
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:
- 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 predicts | What was recorded |
|---|---|
| The round byte reads 0 on round 256 | 0 |
| The layout number becomes 255 (round 255 used 14) | 255 |
| The tunnel list comes from the monster table | It did, at $3258 |
| The monster list comes from past the end of its table | It did, at $3350 |
| The rock list comes from the tunnel table | It did, at $31E8 |
| The tunnels are drawn off screen | 48 bytes went to ROM or to nothing; 36 writes to unconnected addresses were logged |
| A monster starts on Dig Dug's square | One did, at x $71, y $80, drawn as a Pooka |
| Dig Dug is caught as soon as play starts | On 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.