Radar Rat Race · Commodore 64 · 1982
This game has no sprites for its cast. The rat, the cat, the cheese and the death squeak are all letters: entries in a custom font, stamped onto the screen two characters wide and two tall. Everything below is read straight out of a paused snapshot of the running machine.
01 · The screen, as captured
A reconstruction, not a screenshot. Every pixel here is computed from three separate regions of the snapshot: the 1,000 bytes of screen RAM holding character codes, the 1,000 bytes of colour RAM (which lives with the video chip, not in the CPU's address space), and the 2KB custom font. Put them together and the frame reassembles itself.
320×200: 40 columns by 25 rows of 8×8 characters, the C64's native text resolution, upscaled with no smoothing. The grey blocks are walls and the white gaps between them are the corridors; the rat and a cheese are both visible among them, drawn as characters like everything else. The black rectangle at right is the radar panel: its white dots are cheese blips, drawn as characters. The cyan square and the purple X's are the game's only hardware sprites: one for the rat and one for each red rat, drawn here from the sprite positions in the snapshot. Stars and cats are never shown on the radar.
02 · The cast
Using the font for graphics costs almost no
memory, and the hardware draws it for free. Here is the entire cast, pulled from font entries
$40 through $5F. These are the real pixels, not illustrations.
Each one occupies four consecutive font entries, stamped as a 2×2 block
at screen offsets 0, 1, 40, 41: the 40 is one screen row.
03 · Four frames of animation
The entire animation budget for the player is four tiles. The game picks one based on the rat's committed direction, then stamps it. That's the whole renderer. There is no interpolation, no sprite multiplexing and no in-between frames. Press a direction.
Four routines in the code, one per direction, each differing only in which four font entries it reads. The middle button cycles them.
04 · The level format
The whole maze is 224 bytes. One bit per cell, 32 cells across by 56 down, four bytes to a row: a 1 means wall. What you see on screen is a nine-by-nine window onto it, redrawn every time the rat moves, always centred on it. Here is the entire level, with the visible window marked.
Two of these exist in the whole game, at $F370 and
$F450: one for rounds 1–3 and 8–11, the other for rounds 4–7 and 12–15. Scrolling is done in single-character
steps, so when the rat sits on an odd cell the renderer draws half-tile slivers along the
edges to keep the motion smooth.
The red rats consult that bitmap to
decide where they can walk. The player does not. The player's own collision reads back the
characters already drawn on screen and checks them against two codes:
$7F for an interior wall, $2A for the border beyond the map's edge.
Two sources of truth for one maze: the bitmap the level is authored in, and the picture
rendered from it.
The player's test is a blacklist: those two codes block, everything else is walkable. Which is why a cheese needs no special handling when eaten: remove it from the ten-slot table and the next redraw simply doesn't draw it.
05 · Placement
Three kinds of thing share the maze with you, and the game places each one a different way. The red rats always start from the same spots. The cats come from a hand-made list that the game thins out at random. The cheese is rolled fresh every round and thrown back until it lands somewhere legal. Pick a round below and watch the game's own placement code lay it out.
The whole maze at the game's own scale, one cell per 16 pixels. The view in
play only ever shows nine cells of it at a time. This is the game's placement code,
ported line for line from the routines at $EE57 and $EEA8, random
number generator included. Starting from the state the game is in when it loads, it
reproduces exactly the cats and cheese found in the captured round 1.
Seven fixed spawn points: five in a row near the bottom of the maze and two near the top. A round uses the first 3 to 7 of them. After every lost life the red rats go back to their spawn points and wait about 1.3 seconds before setting off. The rat always starts in the same cell, two rows above the bottom spawns.
Each maze has a hand-made list of 16 cat positions. A normal round deletes 8 of them at random and keeps the rest, in list order; a SPEED RUN keeps all 16. Cats never move, and they never appear on the radar.
Ten per round, rolled once when the round is set up, not again after a lost life. The game draws a random cell and throws it back if it is inside a wall, in the same radar cell as a cheese it has already placed, exactly on a cat, or in the radar cell of a red rat spawn point. The last one placed is the 2× cheese.
One 16-bit shift register. The game sets it when it loads and only ever advances it while placing cats and cheese, and once per tick while the title screen waits for F1. Never during play. That is why the slider above changes every round except the first.
06 · Difficulty
A handful of tables indexed by one round counter set the whole difficulty curve. The chasing red rats grow from 3 to 7 and get faster from round 8. On rounds 3, 7, 11 and 15 the rules change: the SPEED RUN banner plays, all 16 black cats appear, the red rats are frozen at their spawn points until the clock runs out, you can't drop stars, the rat moves faster and the clock drains twice as fast. After round 15 the counter wraps, and round 16 plays exactly like round 1.
Cat positions come from two fixed 16-cat layouts: a normal round keeps a random 8, a SPEED RUN keeps all 16. The ten cheeses are placed randomly every round, re-rolled if they land in a wall, on a cat, near a red rat spawn point, or in the same radar cell as another cheese. The first round after loading always gets the same layout, because the random generator's state is set before the title screen ever waits for you.
07 · The font
The complete custom character set. Letters and digits are stored inverted: drawn as holes punched in a solid block. That is why the game's text reads as white letters on blue panels: the solid part takes the text colour and the holes show the white background. Past the text you can find the cast, the maze blocks, and the radar dots. Click anything.
08 · Sound
The game stores its music as bare frequency bytes, one per note, written straight into the SID chip's registers. There is no player routine to speak of: no envelopes, no pattern data, just a list of numbers and a countdown. These are those exact numbers, converted to real frequencies with the formula the chip uses.
It's Three Blind Mice. Convert the stored bytes to pitches and phrase A
comes out as G, C, C, B, A, B, C, G, G: note for note, the opening of "they all ran after the
farmer's wife". Phrase B is the descending tail. The interrupt advances one note
every fifth tick, walks each 16-note phrase backwards, and plays A three times then
B once: a 5.4-second loop, running under the entire game.
The last note is held twice as long as the rest. That isn't in the data
as a long note. It is two ordinary entries at the same pitch. Because the engine
never touches the chip's gate, repeating a pitch doesn't restart the note, so the two
entries simply run together. The tune's phrasing is a side effect of what the code
doesn't do.
Frequency = stored byte × 256, then × 985248 ÷ 16777216 (the PAL clock divided by the chip's 24-bit accumulator). A stored 0 is a rest. The tempos are the game's own. The game's clock is not the video frame: it is a timer chip programmed for 16,667 cycles, which on a PAL machine ticks about 59 times a second. The SPEED RUN tune advances every tenth tick, so each note lasts about a sixth of a second. Tunes stored backwards are played here in the order you hear them. The game start fanfare is the opening of the same tune.
09 · Secrets
None of these are in the instructions, and several contradict what has been published about the game. Every one came out of the code; the ones marked as confirmed were then tested in an emulator. Each ends with where to look.
Published descriptions say the cats only slow you down. In this build they kill the rat exactly like a red rat does: one routine checks the rat's position against both, and both set the same "caught" flag. Only the red rats are slowed by cats. Cats never show on the radar, so you only see one when it scrolls into view.
player_collision_check at $F344 · confirmed by putting the rat on a cat
The title screen says BONUS RAT FOR 20000 PTS, and published descriptions turn that into an extra life every 20,000 points. The code gives one per game. It checks whether the middle two digits of the score are exactly "20", and a flag stops it paying out again. That flag is only cleared when the game ends. Every instruction that could write to it has been checked, including the indexed writes that could reach it indirectly: nothing else does.
add_score at $F02A · the flag at $99 is reset only by game_over_reset
The first round's cats and cheese are placed before the title screen appears, so the first game after loading always gets the same round 1. After that the random number generator is only moved on once per tick while the title screen waits for F1, never during play. How long you wait before pressing F1 decides the layout of every later round in the game, and nothing you do in the maze changes it. Try it in the Placement section above.
prng_next at $EE2E is called from placement and the title loop only · round 1 confirmed across two loads
The ROUND counter on screen keeps going up, but the table index behind it wraps after 15. Round 16 has round 1's red rats, cats, speed and maze. Once you have survived 15 rounds the game never gets any harder.
next_round at $F2BF resets the index when it reaches 15
One cheese has a small red "2x" in its corner. It doesn't just double the next slice: from that one on, every slice scores double and X2 flashes on the panel, until you lose a life. Losing a life also resets the slice value to 100. Slices are worth 100, 200, 300… the tenth is worth 1,000, and each unit of TIME left at the end of a round is worth 60 more.
check_meal_eaten at $F0CA · score_doubler at $C0, cleared when a life starts
In rounds 3, 7, 11 and 15 the red rats sit frozen on their spawn points and only start chasing when TIME runs out. There's a catch in the other direction too: if you're caught during a SPEED RUN, it's over. You lose the life and go straight on to the next round.
red_rat_move_countdown = $FF · timeout_handler at $EAC0 · confirmed in the emulator
When the TIME bar empties nothing dramatic happens. You can't drop stars any more, and every two-thirds of a second the rat gets a little slower, until it moves at a third of its normal speed. The rat never stops, either: with no input it keeps going, and at a wall it turns right by itself, or left if right is blocked.
timeout_handler at $EAC0 · move_player at $E78C
No red rat ever plans a route. At each cell it compares its position with yours along one axis and steps toward you if that cell is open; once it is level with you it switches axis. If the way toward you is a wall, it carries straight on, or turns right, or turns left, and only tries again at the next cell. It has no idea that the way round is the other way.
red_rat_chase_direction at $E23C · move_all_red_rats at $E088
The red rats test their next step against the 224-byte maze bitmap. The rat's own movement never touches it: it reads back the characters already painted on screen. The same wall is stored twice, in two formats, and each reader uses the one it can check more cheaply.
red_rat_can_step reads the bitmap · read_screen_in_direction reads screen RAM
When your score passes the high score, the game sets a flag saying so. Nothing ever reads that flag. Whatever it was meant to trigger never made it into this build.
new_hiscore_flag at $0217 · set at $F090, cleared at game over, never read
Three Blind Mice runs continuously under the whole game, and the melody table is read from the last entry to the first. Every tune in the game is stored that way except the round-complete jingle. Counting an index down until it goes negative is a common 6502 idiom: the end test is a single branch on the sign flag.
music_tick at $EA2B · index counts 15 down to 0, reloads, repeats
Tucked in the string table is COMMODORE V.02. The game keeps its text in two
alphabets: ordinary labels use standard screen codes, while the banners (GAME OVER,
SPEED RUN) use a private set of bright glyphs where S is $1B instead of
$13. Four of them, P, L, semicolon and full stop, double as the key diagram on
the title screen.
str_speed_run at $FD33 · str_version at $FD7C