1988 · Thalamus
Every byte of the C64's 64 KB, one pixel each, 512 to a row. Lit pixels are what this game is: its code, graphics, level data, sound, text, tables and variables, exactly where they sit in memory. Dim areas are screen and working memory; dark is unused.
This game is in 11 parts. The map is of The engine, which stays in memory; the band marked as varying holds whichever of the 9 parts loaded over it is there. The other part is not on this map. The Source tab has the listing of each part that has one.
| Tier | silver |
|---|---|
| Coverage | 100 % of the bytes the game uses have a description |
| Comments checked | Not audited. No audit has checked every comment, and the last sample of 80 comments had 5 wrong, so between 3 and 14 % of the listing’s comments are likely wrong. How listings are checked |
| Build analysed | Thalamus disk release, two G64 side images (a verified dump, marked (!)) from the C64 Preservation Project's 10th Anniversary Collection on the Internet Archive; sha256 of each side in orientation.md |
| Copy | agent-draft |
| Tools | host: Linux 6.18 x86_64, Ubuntu 24.04 cloud container with four cores (Intel Xeon) and no display, emulator: VICE, release v3.13.2, v3.13.2-linux-x86_64-gui.zip (vice-mcp), run under Xvfb and driven through kit/c64/vice.py; check-emulator 57 of 57, disassembler: regenerator2000 0.9.20, one session for each part, publication: Python 3; symbols_export.py and listing.py, python: 3.13.16, node: 22.22.0, browser: Chromium 141.0.7390.37 (Playwright 1.56.1's), for the page checks, simulator: kit/cpu/cpu6502.js, for the tests of the generator port, of the music driver's port (tests/music.js) and of the music demo's driver (tests/warbles.js), and to run the demo's unpacker against the emulator's result, subagents: two checker agents on the Agent tool's opus alias for 60-verify, one for each sample of 80 comments; all other work by one agent |
| Model | claude-opus-5-5[1m] |
| Kit version | 0.0.106 |
Every step that measured the Source tab’s comments or made them more accurate, in the order it happened, with the models that did it. Accuracy of the Source explains each step and what the row above makes of them.
| Step | By | What it found | Of the listing |
|---|---|---|---|
| Research | claude-opus-5-5[1m] | Looked the game up online (its manual, reviews or wiki pages) before reading its code. | |
| Annotation | claude-opus-5-5[1m] | Wrote the listing’s comments. 100 % of the program is described, in 2,725 comments. | |
| Sample 1 | claude-opus-5-5[1m] | 13 of 80 comments wrong, so between 10 and 26 % of the listing is likely wrong. Whether they were then corrected was not recorded. 60 hand-written and 20 decoder comments | |
| Audit 1 | claude-opus-5-5[1m] | Checked 656 of 2,584 comments and corrected 656. partial, for the frame-rate error: every reader of $68 followed; 16 engine and all 640 path comments rewritten | |
| Sample 2 | claude-opus-5-5[1m] | 5 of 80 comments wrong, so between 3 and 14 % of the listing is likely wrong. All 5 were then corrected. 60 hand-written and 20 decoder comments, from those the first did not draw |
likely wrong the sample’s range corrected right, or likely right not yet measured
Read this before annotating code. What the game is documented to do, with verification status against the binary. Statuses: open (documented, not found yet), traced (in the code, could not be exercised; say what was tried), confirmed (in the code, consistent with the emulator), live (observed directly), differs (the code does something else). "Absent" is not a status.
Sources:
Addresses are the engine's (parts/engine) unless a part is named.
| Feature | Status | Where |
|---|---|---|
| Disk menu on side 1: 1 Armalyte, 2 Loading picture, 3 Walker's Warbles | live | menu at boot (reference/disk-menu.png); the first loads the engine, the third a music demo (parts/warbles, section 16), and the second was not followed |
| The third menu entry is a music demo of Martin Walker's, dated 1988, with eight tunes in it and four on keys | live | parts/warbles/facts.md; its screen is reference/warbles.png, and the page plays all eight through a port of its driver that tests/warbles.js checks against it |
| "Turn disk to side B & press fire" after the first load | live | wrong_disk ($B5CC): the engine asks for level 1's file, does not find it on side 1 and shows the prompt (reference/turn-disk-side-b.png) |
| Title screen cycles credits, high scores, ship enhancements and keyboard controls | live | seven pages, title_pages ($B953) (reference/title-*.png) |
| Fire starts the game (title) | live | fire on either joystick, title_start_keys ($B58A) |
| F1 on the title: one- or two-player mode | live | title_function_keys ($BC55), $B026; left and right on the stick choose too |
| F3 on the title: switch between three starfields | live | $B02F steps 2, 0, 1; stars_animate ($F72F) |
| F7 on the title: demo mode | live | $B01A: both ships out of play, tune 1 plays, and fire or the tune's end goes back to the title (joysticks_read, $ABD9) |
| The ship follows the joystick | live | cell to cell on an 8-pixel grid, gliding between cells (ships_move, $E59C; ships_glide, $E90B) |
| A short press of fire fires the normal weapon; holding fire "for a few seconds" (wiki: about 1.5 s) fires the super weapon | differs | the super weapon fires once fire has been held for 15 frames, 0.3 seconds (ship1_fire, $EC87; live: the charge fell at the fifteenth frame); pressing fire and letting go each fire the normal weapons |
Player 1 changes super weapon with a key: the manual and the wiki say the Commodore key, this build's title screen says @ | live | the Commodore key (keys_play, $AB53): the title draws it as character $2A of its font, a C with a bar through it, not @; it steps A, B, C and round |
? changes player 2's super weapon | live | the / key, which carries ? on the keyboard (keys_play) |
| Space toggles the remote (drone) tracking on and off, one-player mode only | live | $74, only with the drone (keys_play) |
| RUN/STOP pauses; fire restarts | live | the pause starts at the end of a scroll step; fire on either stick ends it (pause_and_fire, $AF7E) |
| Q quits (from the pause, per the wiki) | live | only while paused, to the front end (keys_play) |
| S in the pause goes to the next level | differs | live: S held in the pause does nothing; keys_play reads only RUN/STOP, Q, the Commodore key, / and SPACE |
| Eight levels, each with a mid-level boss and an end boss; the scrolling stops for the end boss | differs | levels 5 and 7 have no mid-level boss and level 8 meets its mid-level boss twice, at tile columns 167 and 242 (each level's script, $9754); every boss, mid-level ones too, is fought with the scroll stopped, and blows up by itself when its time runs out (level_script, $A93E; live in level 1) |
| Scenery destroys the ship on contact | traced | characters $21-$F5 under the ship's nose (players_hit_scenery, $9E05) |
| Munitions pods change shape each time they are shot, to show the enhancement they would give; the more shots, the better the weapon | traced | a pod moves on to the next of seven enhancements after 11, 11, 11, 11, 17, 17 and 32 hits, five fewer in a two-player game (pod_next_type, $A5F4); the order is fixed (forward fire, tail gun, vertical cannon, trident, converge, generator, battery), not from worse to better |
| Collecting a pod before it has changed makes the ship invulnerable for five seconds (the ship flashes) | traced | 255 frames, 5.1 seconds, flashing through six colours (p1_collect_pod, $9A94; ship1_invulnerable, $EA77); a full enhancement, converge, a generator and a battery give the same |
| Invulnerable from a pod, the ship can fly through walls that block the way | traced | the scenery test only looks at a ship in normal play, state 2, and an invulnerable ship is state 3 (players_hit_scenery, $9E05) |
| Extra forward fire: two forward shots at the start of each level, increased to four | traced | $4B + 1 shots in flight: 2 at the start, 4 with two pods, 6 with converge (ship1_normal_fire, $F1C3) |
| Tail fire: backward-firing bullets | traced | a pair, a row above and below the ship, backwards ($4D) |
| Vertical cannon: fire up and down | traced | a pair, up and down ($4F) |
| Trident: two flanking guns beside the main weapon | traced | a pair one column behind, a row above and below, firing forwards ($47) |
| Converge: two more shots to the main guns, diverting the tail gun's ammunition; needs two extra forward-fire enhancements first | traced | needs the tail gun as well as forward fire at 3; it adds two forward shots in the tail gun's slots (p1_collect_pod, $9A94; $B2) |
| Battery: one more energy cell, at most four, each storing six charges | traced | four slots, $B010-$B013 (player 2 $B014-$B017) (generators_recharge, $EB00) |
| Generator: stores six energy charges and rebuilds to full after firing; energy beyond that goes to a battery (Wikipedia: increases the recharge rate) | confirmed | both hold: each generator, up to four, shortens the rebuild tick from 61 frames to 51, 41, 31 and 19; eight ticks fill one battery at a time and, once none is left to fill, the generator's own charge of six (generators_recharge, $EB00; live: from empty, the charge reached 6 after 488 frames with no generator and 152 with four) |
| Super weapon A: long sustained blast through solid matter, two charges, low availability | live | ten shots across the ship's row, starting beyond any wall; two charges (super_weapon_patterns, $F07D; live: a full generator dropped by 2) |
| Super weapon B: swarm of laser fire around the craft, from two directions, one charge, medium availability | differs | costs two charges, not one (costs at $F486; live: a full generator dropped by 2); five shots, two ahead, one back and two from behind |
| Super weapon C: rapid burst of pulsed energy, one charge, very high availability | live | two shots side by side, one charge (live: a full generator dropped by 1) |
| One-player mode: the second ship is an automatic drone that follows the ship, fires as it does and shields against most enemy shots; it does not hurt enemies (wiki) | differs | the drone's shots kill: hits are read from the shot characters on the screen, whoever fired them (enemies_hit_by_fire, $A54C); it absorbs enemy shots, is never hit by an object and collects nothing (p2_hit_by_shot, $9CD3; drone_fire, $F0C4) |
| Detached, the drone "glides along its own trajectory" (manual) or "stays stationary" (wiki) until recalled | differs | from the manual: live, the detached drone stays where it is on the screen, as the wiki says (drone_follow, $9E78) |
| The drone sits a short distance from the ship on the side opposite its last movement | live | opposite the way the stick is pushed, stepping a cell at a time; with the stick released it stays where it is (drone_follow, offsets at $9EF8/$9F03) |
| Two players at once: the second ship is red, of a different design, and controllable | confirmed | two-player games started live (F1); ship 2 reads joystick port 1 (joysticks_read, $ABD9); its colour and design were not checked |
| Two-player mode has more pods (manual: "additional"; Wikipedia: doubled) | traced | 174 pods in every game and 123 more only in a two-player game, across the eight levels (type 6 entries, waves_spawn, $E3FF; the levels' facts.md): additional, not doubled |
| Players push each other when they collide | live | a ship that moves into the other pushes it a cell (ships_relative, $E531) |
| A destroyed ship respawns at once and keeps its enhancements; the next level takes them all away except batteries and generators | differs | a hit ship comes back where it was, invulnerable, keeping its enhancements (ship1_hit_sequence, $E9BD); each load takes every weapon (zero page, cleared by level_setup_vars, $A1DE) and also two batteries and two generators from each player (next_level_losses, $B723, run twice by after_level_load, $B3CC) |
| Scoreboard: battery status, score, generator status, generator type, converge status, super weapon indicator, craft in reserve | traced | the eight hardware sprites in the lower border, redrawn one item a frame (panel_update, $99FA) from the front-end font's glyphs: batteries, score, the generator's rebuild box and count, weapon-level bars, the super weapon's letter, and each player's lives at either end |
| Three craft in reserve at the start | live | three lives each (title_start_keys, $B58A; reference/level-1-start.png) |
| High-score table of six entries with three-letter names | live | high_score_check ($BA72), names entered with the stick (high_score_name_entry, $BB3C) (reference/title-high-scores.png) |
| Music on the title and loading screens only; sound effects in play | traced | tunes 0 title, 1 demo, 2 high scores and the ending's pictures, 3 loading screen (tune_headers, $C765); in play only the 32 effects (sfx_voice_step, $FA3C); the How it works page plays the four tunes through a port of the driver that tests/music.js checks against it |
| Exploding enemies carry on along their path | traced | enemies_follow_paths ($EE8D) moves exploding objects too |
| More than 30 sprites on screen at once, without flicker, at 50 frames a second | differs | at most 30 in a frame: 20 objects multiplexed on six sprites, the two ships, and eight scoreboard sprites in the border (multiplexer_start, $A305); live, the main loop made one pass a frame through all of level 1 |
Cheat: POKE 59891,173 ($E9F3) "cheat mode" | differs | it turns the DEC of player 1's life into an LDA, and anti_cheat_check ($F77D) notices and hangs the machine (live: frozen within five frames) |
Cheat: POKE 53792,96 ($D220, an RTS) "unlimited energy" | differs | with the I/O chips in, $D220 is a mirror of the border colour register, so 96 ($60) sets the border to black, which it already is (live: a 5 there turned the border green); the RAM under it holds a level's characters ($60-$7F), copied once at the level's start, and no code runs there (level $D200) |
Found in the code, not in the manual.
level_end, $AC20; live in level 1). The limits run from 29 seconds (level 8's first mid-level fight) to 245 (facts.md, "Timing").level_script, $A970; ending $0923).level_setup_vars, $A260; live: 103 and 205).$98F0).title_pages, $B953).pod_next_type, $A5FE; traced).group_destroyed, $A62F); a kill scores 100 to 500 by the enemies' speed, plus 300 in a two-player game (kill_points_by_speed, $A9DE).level_complete, $AE25).$F77D), the stack page against freezer cartridges (stack_check, $AFAE) and the PAL frame time on the title (timing_check, $BDC8).level_setup_vars, $A2AC; live).$3934; parts/level-4/facts.md, parts/level-7/facts.md).facts.md; live).cyberdos must fetch them from tracks of its own; which tracks was not traced (parts/warbles/facts.md, "How it is reached").Each part of the game has its own symbol map and listing, parts/<id>/symbols.json and parts/<id>/listing.json; the ids are the ones in its Source pages’ addresses. Write-ups, facts and symbol maps are CC BY-SA 4.0. The game itself is not hosted here.