1984 · Mastertronic

Squirm

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

What the code does that the game never tells the player: a level counter that loops, a death that sounds different on each sound chip, a keyboard that plays, and the remains of the programming tool the game was written with.

01 · The level counter

Bug: level 19 is followed by level 10

The game counts levels on the screen, digit by digit, and gets the carry from 9 wrong. After level 19 the number goes back to 10 and runs 10 to 19 for ever.

Each press runs the game's own instructions at $3887-$389A, taken from the listing, on a model of the C64's processor, the 6502. They work on the two digits of LEVEL on the top line ($0423 and $0424). In the VICE emulator, with the level number set to 19 by hand, the next level showed 10. Numbers written with a $ are hexadecimal, counting in sixteens; those in dark boxes are addresses in the C64's memory, and each opens the Source code tab there.

The level number exists only as two characters on the top line, digits drawn light on dark, whose character codes run from $B0 for 0 to $B9 for 9. At the end of a level, level_complete adds 1 to the units digit ($3887). When the units reach $BA, one past 9, it writes $B1, a 1, into the tens digit and $B0 into the units. It should have added 1 to the tens. So every time the units pass 9, the tens become 1. 09 becomes 10 correctly, and 19 becomes 10 again.

Only the number on the screen is wrong. The maze and the speed come from maze_number and step_delay, which count on correctly, so the 20th level is drawn and played as the 20th while the top line says 10 (How it works).

02 · The death's noise

Bug: the death sounds different on the two kinds of sound chip

The fall lowers the noise's pitch by reading it back from the sound chip, which cannot be read back. On the older chip the pitch jumps to the top after one step. On the newer one it falls as the programmer meant.

The sound player runs the game's own lose_life ($3A7D) on a model of the C64's processor, with the chip's data lines kept as the VICE emulator keeps them. They hold the last value written for 7,424 of the processor's cycles on a 6581 and 663,552 on an 8580; the processor runs about a million cycles a second. The table is what each DEC $D401 wrote there. In VICE, stopping the machine after each one showed the same values, on an 8580 and, from a fresh start, on a 6581.

lose_life switches voice 1 of the SID, the C64's sound chip, to noise at a pitch of $14. Then it lowers the pitch once for each row the player falls, three jiffies (sixtieths of a second) apart, with one instruction: DEC $D401 ($3A8F). DEC reads a byte, takes 1 from it, and writes it back. But the SID's pitch registers, the chip's settings, can only be written. A read of one gets whatever is still on the chip's data lines, the wires that carry each byte to and from the chip, from the last write, until that value fades away.

The C64 was made with two models of the SID over the years, the 6581 and later the 8580, and VICE emulates both. The first DEC comes a few cycles after the write of $14, so on both chips it reads $14 and writes $13. The next comes three jiffies, about 49,000 cycles, later. VICE's 6581 has let the value fade to 0 by then, so DEC writes 0 minus 1. A byte cannot go below 0, so that wraps round to $FF, the highest pitch, and every row after does the same. VICE's 8580 holds the value for much longer, so each DEC reads back what the last one wrote and the pitch steps down a row at a time. How long a real chip holds the value varies from one chip to the next. The two lengths here are VICE's.

03 · The keyboard

Secret: five keys of the keyboard play the game

The game only reads the joystick, but the keys 1, ←, CTRL, 2, and SPACE move the player and fire as well.

KeyLine of $DC01Acts as
1bit 0up
← (top left)bit 1down
CTRLbit 2left
2bit 3right
SPACEbit 4fire: stops the player, and starts a game from the title
C=, Q, RUN/STOPbits 5 to 7nothing alone; held with a direction, they stop it counting

The keyboard's row 7, from the C64's keyboard matrix. In VICE, holding 2, 1, CTRL, and ← for a quarter of a second each moved the player right, up, left, and down, and SPACE started a game. The widget on How it works runs the game's own code on any mix of these lines.

The C64 reads its keyboard as a grid of eight rows and eight columns. One chip, CIA 1, one of the C64's two input and output chips, drives a row with one port and reads the columns with its other port, $DC01. Joystick port 1 is wired to that same input port, on bits 0 to 4. The KERNAL, the C64's built-in system code, leaves row 7 of the keyboard selected between its checks of the keys, and Squirm never changes it. So each of row 7's first five keys pulls down the same line as a joystick direction or fire, and move_player ($306E), which reads $DC01, cannot tell the two apart. The game also wants each value exact. A key from bits 5 to 7, held with the stick, turns a direction into a value the game does not know, and the player carries straight on.

04 · The high score

Bug: a new high score wipes the score off the top line

The moment a new best score is entered, the top line shows it as HI and puts SCORE back to 000000.

Name entry in VICE: the name DCC with 000300 climbing the table; the top line still says SCORE 000300
A score of 300 climbing the table: the top line still says SCORE 000300.
The table after the climb: DCC 000300 on the first row; the top line says HI 000300 and SCORE 000000
At the top: HI 000300, and SCORE 000000.

Both from the VICE emulator, after a game ended with its score set to 300 by hand and the name DCC entered.

The score is never stored anywhere but the screen (How it works). When a score reaches first place, enter_name copies its digits into the stored pattern the top status line is drawn from ($0FA4). Then it redraws the status lines from those patterns (draw_status_lines, called at $3258) to show the new HI. The same pattern holds SCORE as 000000, ready for a new game, so the redraw puts that on the screen too. The score the player just made stays on the screen only as the new HI.

05 · The bonus tune

Secret: from level 9, the bonus run plays part of the title tune

The bonus tune is written to last exactly as long as the run on the first levels. The runs get faster, the tune does not, and from level 9 the routine that plays it reads on past its end into the title tune.

Each bar is one of the 255 notes bonus_tune_step ($3D5C) plays, its height the pitch, a gap a silent note. The blue ones are bonus_tune ($3DB8), the orange ones the first bytes of title_tune_voice1 ($3E80), which follow it in memory. The marks show how many notes a run of 901 jiffies (sixtieths of a second) plays at each speed. The sound player runs the game's own code, for a run at six jiffies a pass and at four.

bonus_tune_step ($3D5C) plays one note each pass of the bonus run, each time round the loop that moves the player. It counts its place from 0 to 254 before it starts again ($3D66). The table at $3DB8 holds 151 notes and then 49 zeros, which are silent. The 200 bytes after it are the title tune's two voices. Notes 200 to 254 are therefore the first 55 bytes of the title tune's first voice.

The run lasts 901 jiffies whatever the level, and each pass waits as long as it does in play (step_delay). On the first four levels a pass is six jiffies, so the run has 151 passes, one for each note of the tune. On levels 5 to 8 a pass is five jiffies, so the run has 181 passes. The tune ends, and the last two and a half seconds of the run are silent. On levels 9 to 12 a pass is four jiffies, and the run has 226 passes. After more than three seconds of silence, the run plays the rising notes of the title tune's opening. From level 13 the run outlasts all 255 notes, and the tune starts again.

06 · The worms' test

Secret: an older version of the worms could not step onto the player

The test a worm makes before each step was rewritten, and the old one is still in the code behind a jump. It refused the player's cell, so a worm could only catch a player who ran into it.

Both tests run on the model of the 6502, with the worm's next cell holding each character in turn. One is cell_is_free ($3C93), the test the game uses. The other is old_cell_check ($32D5), started with the low byte of the cell's address already loaded, as the instruction that the jump wrote over would have done. 0 means the worm may move there.

Every worm's step ends with a call of try_worm_step ($32EF), which ends with a jump to $32D2. There a jump instruction (JMP, three bytes) sends it on to cell_is_free ($3C93), at the far end of the program. Those three bytes were written over the start of the older test. The rest of the old test is still there, from $32D5 to $32EE. It reads the cell and takes a space ($20) or an egg ($46) as free. The new test adds one comparison, with the player's character $51.

With the old test the worms went round the player. The main loop only checks the cell the player moves into, so a player could die only by running into a worm. With the new one, a worm may crawl onto the player. The worms move before the player in each pass, so a player that does not move off the cell in the same pass dies there.

07 · The speed

From level 25, the game stops waiting

Each cycle of four levels takes a jiffy off the main loop's wait. Six cycles take it to nothing, and from then on the game runs as fast as the processor can.

The level's step_delay as the game counts it: $3BAF sets $FA, and $3A72 adds 1 each time the stage counter reaches 5 and goes back to 1. Beside it is what the wait at $3C56 makes of the value. The wait stores it in $A2, which the KERNAL's interrupt routine, run by a timer, adds 1 to 60 times a second. Then it loops while $A2 is $80 (128) or more.

step_delay ($2689) starts at $FA, which is 250, six short of 256. The wait lasts until $A2 counts past $FF (255) to 0. At level 25 the value reaches $00, which is below $80 already, and the loop goes straight through. A pass then takes only as long as its own work, many times faster than the ten passes a second of the first level. The value goes on rising every four levels, but the main loop would only wait again at $80, more than 500 levels later.

08 · The assembler

The tape carries the menu of the assembler the game was written with

The file on the tape is a plain copy of memory from $0801 to $3F47. Nearly 2,000 bytes after its first line are most of another program: the BASIC menu of the CBM 64 Assembler, by Derek Bush and Peter Holmes, 1983.

Read by the page from $084C to $0F9F of the listing, line by line along the BASIC program's own links, with BASIC's keywords spelt out and the colour and cursor codes left out. The game never reads these bytes.

An assembler turns a programmer's instructions, written as short words, into the machine code the processor runs. This one has a menu written in BASIC, the programming language built into the C64. A BASIC program on the C64 starts at $0801 and is a chain of lines, each starting with the address of the next. The game's own BASIC is one line, 10 SYS 15133. SYS starts machine code at an address, here 15133, which is $3B1D. The chain ends two bytes after that line. The bytes after it, up to $0F9F, were simply in memory when the game was saved. They are lines 15 to 340 of the assembler's menu, with its title page, its error messages, and the line-by-line entry routine that calls the assembler's machine code with SYS 52113 and SYS 51972. Just before line 15 are the words "2.03/06-07-83", the end of a version and a date.

The character set, the 256 shapes the screen's characters are drawn from, holds more of the same tool. From $2AF0 the shapes of the unused characters are really text: the three-letter names of the processor's instructions, such as AND, ROL, and CPX, from the assembler's table of them. Together they show how the game was made. A BASIC menu ran an assembler whose machine code sat high in memory, at $CB04 and $CB91, the two SYS addresses. The game's code was assembled above the menu program, and then everything from $0801 to the game's end was saved to tape in one go.

09 · The arrow

An arrow is animated 8.6 times a second and never drawn

The game's interrupt routine cycles three characters, the three frames of an arrow, but no screen in the game ever shows them.

Drawn from the character set at $2800 as the game's own irq_handler ($3CB3) leaves it, run on the model of the 6502 sixty times a second: the arrow ($40-$42), the egg ($46), the two treasures ($47-$4A), and the eight frames of the spinning figure ($4E-$55).

On every seventh interrupt the handler moves the shapes of characters $40, $41, and $42 round one place ($3CC4). Nothing in the title, the mazes, the status lines, or the high-score screen holds those characters, and no code writes them to the screen. The arrow costs 24 bytes of shapes, and about 270 of the processor's cycles on every seventh interrupt for as long as the game runs.

10 · Two quirks in the code

A write to the wrong chip, and a call into the middle of an instruction

Neither changes what the player sees.

WhereWhat the code does
$3649The last loop of colour_maze starts with its counter still at $FF, where the loop before left it. So before the 75 cells it means to colour, it writes one colour to $DC4C, past the end of colour RAM. That address belongs to CIA 1, an input and output chip: it is a second copy of the chip's serial data register, which nothing in the game uses.
$2441The start-up shows the title with a call of $2441 ($3B74). That address is not the start of an instruction. It holds the last byte of a branch, $F5, and after it comes the $60 of a return. Read from $2441, $F5 $60 is SBC $60,X, a subtraction. It changes only the accumulator and the flags, the processor's working number and its notes on the last result. The next instruction is the real start of show_title_screen ($2443).

The first is a loop that runs one step too many. The second works by luck: the stray subtraction happens to be harmless, so the title appears as it should.