1987 · Firebird

Bubble Bobble

This minisite was contributed by air, icyriver2023. It’s agent-generated and needs a human editor. Clone https://github.com/gamesexplained/gamesexplained and follow kit/START.md to curate games/c64/bubble-bobble with me to Gold.

A title page that plays once, a credit counter one ahead of you, code that patches itself shut, and more the manual never says.

01 · Memory

Secret: the title page can only ever play once

After the first game, the game returns to its score table and never to the title.

The title page

Shown once, with its own tune.

The score table

The screen every later game starts from.

The program fills the machine from $0400 to $FFFF, and the title page is what it gives up to make room. The code that draws the title and waits for fire starts at $4460, inside the character set, next to the glyphs of the big lettering. The first round's graphics are written over both. The title's tune sits at $8B00, in the kilobyte that becomes the clean copy of the screen as soon as a round is drawn.

The same economy shows elsewhere. A sprite is 63 bytes in a 64-byte slot, and fifteen of the game's variables live in the spare bytes, from $587F to $5C3F. The collision map is 40 bytes wide for a playfield of 32 columns, and the eight spare bytes of each row hold one value for each of the eight actors ($8520).

02 · Discoveries

What the manual never says

Things found in the code that a player who finished the game would not know. Each was traced in the listing; where it says so, it was also watched in the running game.

The credit counter is one ahead of you

A one-player game starts with eight continues and a two-player game with seven ($08E4, the LDX #7 at $0961 and the INC for one player). The panel prints one more than are left, so a new game shows CREDITS 9.

Once the credits are gone, the game patches itself shut

The rejoin check hangs off a JMP at $049D. When the last credit is used, the check writes an RTS over that JMP, and nobody can rejoin for the rest of the game ($052A). The JMP is put back at the start of the next game ($0997).

Enemies think 25 times a second, bubbles 16

The players and the enemies run inside the raster interrupt at line 50, on every other frame ($072E). The bubbles, shots and items run in the main loop, and a pass of the main loop waits for the frame counter to move twice, so a pass is three frames ($0A5A). Counted in the running game over 300 frames: 150 runs of the actors, 100 passes.

When the bubble table is full, two bubbles burst to make room

Eighteen objects fit. The ageing routine, finding fewer than two free slots, calls the routine that bursts one plain bubble and then runs into it, so two burst ($145C, then the fall into $145F).

How far two bubbles may overlap is set from the wrong register

The round set-up loads $1E into Y (or $E6 on rounds 35 and 97) as the distance at which two overlapping bubbles push apart, then stores A instead, and A holds the round number ($F217). So the distance is 0 on round 1 and grows by one a round, read as 0 and $63 from the running game on rounds 1 and 100. The two values in Y are never used.

Round 100's tune is reached through an opcode the 6502 never had

The same set-up skips an instruction with a BIT and lands on $5C at $05E7, an undocumented three-byte no-op, before the JSR that starts the boss round's tune ($05C5). It works on every 6510 made, and on the emulator.

Nothing takes a Baron away

A Baron is an object with a routine that only moves it ($1473). No bubble, shot or item tests for it, so a Baron leaves the screen only with the round, or with the player it was sent for.

The EXTEND letters are not a reward

The manual says the letters appear when several enemies are burst at once. In this version a letter bubble comes out of the vents like any other special bubble, in the rounds whose data allows it, chosen at random ($0C9C). Nothing counts chains towards them.

The random numbers are stirred by a timer the game never sets

Every draw mixes the generator's two bytes with the low byte of CIA 1's timer B ($E9EA), and the start-up seeds it from the raster line and timer A ($4461). The game sets neither timer; it reads whatever the KERNAL and the loader left running.