1985 · Beyond Software
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.
| 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 14 wrong, so between 11 and 27 % of the listing’s comments are likely wrong. How listings are checked |
| Build analysed | Beyond cassette, 1985 (Ultimate Tape Archive Shadowfire.tap, SHA-1 30554555c373cf3d59939377f138ddf9cbc0a721) |
| Copy | agent-draft |
| Tools | emulator: release v3.13.2, v3.13.2-linux-x86_64-gui.zip, disassembler: regenerator2000 0.9.20, host: Linux x86_64 (Ubuntu 24.04.5, cloud container) |
| Model | claude-opus-5-5[1m] |
| Kit version | 0.0.224 |
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 1,610 comments. | |
| Sample 1 | claude-opus-5-5[1m] | 14 of 80 comments wrong, so between 11 and 27 % of the listing is likely wrong. All 14 were then corrected. 60 hand-written comments (13 wrong) and 20 generated (1 wrong); 15 more corrected with them, those that shared their beliefs |
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:
uta_Shadowfire_1985_Beyond_2440, and of a cracked copy's play (Shadowfire_1985_Beyond_Software_cr_Yak_Society), archive.org, read 22 October 2026. Saved in reference/ as web-*.png.Shadowfire and Shadow_Fire on c64-wiki.com and c64-wiki.de were empty on 22 October 2026). Lemon64 refused the fetch (a browser check).| Feature | Status | Where |
|---|---|---|
| Title screen shown while the tape loads ("Beyond © 1985, Shadowfire by Denton Designs") | live | the loader's blocks 2-5 put it up (orientation.md); the game never shows it |
| Control menu: F1 keyboard, F3 digital joystick, F5 analog joystick (paddles), F7 light pen | live | restart ($40A3) at $41BD; fkey_codes $42CE, input_handlers $42C6. Any key on the same keyboard row line as an F key chooses it too |
| Keyboard control: keys by row move the cursor; F7 or any top-row key enters a command | traced | input_keyboard $5630: the joystick's code with keyboard row-line masks (input_masks $6825) |
| Joystick moves the cursor over the icons; fire enters | live | input_joystick $562C, control port 1 only; a held direction speeds up from 2 to 6 |
| Analog joystick (paddles) moves the cursor | traced | input_paddles $5526: paddle 1 the row, paddle 2 the column |
| Light pen moves the cursor; its button or any key enters | traced | input_lightpen $54F1: any key is fire |
| Self-display (demonstration) mode starts after the first screen; any key leaves it | live | starts when the title music reaches tune 2 ($4216); input_demo $5570, demo_script $1571; it ends by clicking Quit twice |
| Scrolling welcome message in the command panel ("Welcome ally of the Empire...") | live | queued by restart; the ticker $5FEF, ticker_shift $63D7 |
| Start menu: Old game, New game, Load game, Save game | live | start_menu $74B9, screen 7's icons at $7C78 |
| Start menu summary: surviving Enigma Force, enemy killed or captured, mission status, Kryxix rescued or not | live | summary_text $CF4B, filled by start_menu; SUCCEEDED and FAILED shown by poking $CA05 |
| New game starts the real-time countdown: one hour forty minutes | live | the clock starts at 99:58 and counts down a second each 50 frames ($CA00) |
| Save and load to cassette or disc, with a file name spelt with on-screen letters, a RUB icon and a carriage-return icon; Run/Stop on a tape failure | traced | name_screen $765E, add_letter $7717, rub_out $772D, start_file $773E: saves $CA00-$CFFE through the KERNAL. Not run live |
| Enigma team screen: the surviving characters, chosen with the cursor | live | team_screen $6DCB, screen 0's icons at $78AE |
| Up and down arrows on the team screen turn the background music on and off | differs | music_up $7884, music_down $7891: they step the in-game tune's pulse width ($1603) between 0 and $40, a step for every pass of the loop while fire is held, so the music fades in and out. It starts at 0, silent. Live |
| Four screens per character: Status, Objects, Movement, Battle, reached by green, yellow and red monitor icons; Quit goes back or cancels | traced | screens 1, 2, 4 and 5 ($789C); quit_choice $7106 cancels a two-step choice |
| Status screen: agility, strength, stamina and weight bars; weight bar red when overloaded; weakened characters may recover | traced | bars at $5761; the weight is drawn red and the strength figure green over it, so red shows the excess; recovery $58F8 |
| Objects screen: objects in the location (left), carried (middle), weight bar; Pick-up, Drop, Ready for use, Activate | traced | list_objects_here $4CFA, list_objects_carried $4E77, pick_up $6FD9, drop $7011, ready $7056, activate $7063 |
| Weapons fire automatically in combat, but only one held "ready for use"; otherwise bare hands | traced | combat $5BFF reads the object held ready (+8) through weapon_strength $CF15; $20 for bare hands |
| Manto's transporter: Pick-up, Transporter, Activate, Ballistic control beams Manto to the Fighter Bay or the top or bottom Shuttle Bay | traced | activate_ballistic $7406: locations $16 (FIGHTER LAUNCH TUBE ONE), $08 and $A0 (both SHUTTLE BAY) |
| Beaming the rest of the team: Drop, Transporter, Activate, Transporter, then the character | traced | activate_beacon $734D: someone at one beacon goes to the other in a second |
| Movement screen: filled arrows show the directions possible; a text description of the location | traced | build_directions; the description icon at $79E8 |
| A character refuses to move at a locked door without a key card, or when about to be attacked | traced | a locked exit's arrow cannot be chosen; a defender, or an attacker off the battle screen, gets "YOU CANNOT MOVE IN COMBAT MODE" (move_by_exit $6E65) |
| Battle screen: Location scan (characters in this or the next location), Attack, Defend, Retreat | traced | battle_screen $7119, location_scan $7166, attack $71D2 |
| Battles in the same location or between adjacent ones; weapons differ in strength and range; "the computer will tell you if a battle cannot be resolved" | differs | combat $5BE6: each weapon has a strength for its own location (high nibble of weapon_strength, $CF15) and one for adjacent ones (low nibble), but only the first is ever applied (damage, $68D7, read with X 0 at $5CB3), so range makes no difference. Simulated on the game's code; "NO ENEMY TO ATTACK IN THAT DIRECTION" |
| A successful attacker advances into the enemy's location | traced | $5D54 |
| Mission command panel above every screen: status icons, view screen, character in play | live | rows 0-12 of the bitmap as loaded, panel_row_0 $2000 onwards |
| Status icon colours: white inactive, blue moving, purple attacking, orange defending, yellow retreating, green picking a lock; grey when weak, black when dying | traced | status_colours $68B7, $5896; grey below strength $80, black below $40 ($58CB) |
| View screen: an iris opens on the chosen character; icons flash for each other character in the location | traced | iris_open $45E2; the map icons from entity_pictures $6CFE, one cell updated each frame |
| Six characters with different speed, strength, stamina and carrying: Zark Montor, Syylk, Sevrina Maris, Torik, Maul, Manto | traced | strengths at $CA0E, agility_base $CFB7, strength_factor $CF85 |
| Zark Montor is the translator: Kryxix follows only him (Wikipedia) | traced | $5B9A, $5D93 |
| Syylk's hatred of Zoff "could prove a weakness" | open | nothing found: no comparison of entity 0 with Zoff in the combat ($5BE6), the triggers or the arrival code, and Zoff can be harmed by nobody |
| Sevrina picks locks (with the tool kit) | traced | activate_tool_kit $7301, $5A08: about 32 seconds a lock |
| Torik is the fastest; Maul slow and well protected; Manto the only transporter | traced | agility base $FF for Torik, $40 for Maul ($CFB7); Maul's strength and factor are $FF; nothing reduces the damage he takes. Only character 2, Manto, gets the beacons' and the transporter's flags (list_objects_here) |
| Enemies: General Zoff (purple icon), Captain Chur (white or grey), Marshalls, Squad leaders ("Targs"), Troopers ("Eugs"), humanoid and robotic | traced | 42 enemy records ($CA9E onwards) with seven kinds of figure (entity_pictures $6CFE); the names are not in the game's text, except Marshalls Cavns and Xator in location names |
| Ambassador Kryxix appears as a green icon; he must be located and beamed off | traced | map_icon_kryxix $0AE3; rescue at $5A68 |
| The Phantom, Zoff's invisible pet, attacks characters at random. Steve Cain of Denton Designs (Sinclair User 40, July 1985, page 58) says it was a bug in the C64 version, one wrong mnemonic, since taken out | open | every attacker is one of the 50 entities, which the interrupt runs in turn (irq_entity $66D1, $6197); each has a map icon and shows in the list of people in a location, and combat ($5BE6) runs only for one attacking, defending or retreating. Nothing found that attacks unseen. Six C64 copies hold the same program: Beyond's tape and disk, and the Yak Society (dated 30 April 1985), TBC, Radwar and TCI cracks (two more, cracks by Tuerk and FCG, were not compared). The only search past the end of the people, enemy_at ($7250), makes phantoms that never attack (discoveries.html#phantoms) |
| Objects: projectile pistol, lazer rifle, rocket launcher, grenade, smoke bomb, tool kit, colour-coded key cards, self-destruct card, self-destruct unit, time delay unit, transporter beacon | traced | 54 object records at $CD98: 18 other objects and 36 weapons in 18 pictures. The game names none of them |
| Rocket launcher best fired into an adjacent location; grenade and smoke bomb in the same location | differs | weapon_strength $CF15 has an adjacent strength in its low nibble, which combat never uses ($68D7): every weapon hits adjacent locations with its same-location strength. Which picture is which weapon is not in the game |
| Time delay unit: the character who activates it is the only thing not held in a time lock | traced | activate_time_delay $7282: 120 seconds, once |
| Self-destruct unit, armed with the self-destruct card, counts down four minutes to the end of Zoff V | differs | activate_destruct_unit $747F: 256 seconds. Live. The card only makes the unit usable, and only in the unit's location, $A2 ($746C) |
| Transporter beacon: only Manto operates it, and only after it is dropped in a location | traced | list_team_beacon1 $50E0: a carried beacon's list is blank |
| Objectives in any order: rescue Kryxix, capture Zoff, capture or destroy Zoff V | live | mission $CA05, read by start_menu |
| Music by Fred Gray | traced | the music driver at $1606 and nine tunes; the credit is the manual's |
Found in the code, not in the manual.
$5BFF).$5E22).$27 is meant to patrol but never moves, its patrol byte missing bit 7 ($CCCC). Live.$629F, clock_wraps $6473). Live.$1603). Live.$789C).$CF15).damage, $68D7).show_portrait, $48B1). Live.enemy_at ($7250) reads eight records past the entity table, which puts living "enemies" in locations $80, $0C, $A2, $08 and $54 as loaded: nobody can retreat into them.find_view_locations $6BB7).shake_screen $4AD7).enemy_at ($7250) scans fifty records from entity 8, so its last eight are the object records read as if they were people.$0FE8, $13E8, $15DF).How to get from the contributor's own copy to the analysed state. Someone else must be able to follow this exactly.
Shadowfire.tap, 627,772 bytes, SHA-1 30554555c373cf3d59939377f138ddf9cbc0a721, from the Ultimate Tape Archive's item uta_Shadowfire_1985_Beyond_2440 on archive.org, beside scans of the cassette, its inlay and the instructions. The header reads C64-TAPE-RAW, version 1: a recording of the cassette's pulses, 260 seconds of tape. It is the Beyond cassette of 1985, not a crack: no intro, trainer or menu comes up, and the blocks it loads are listed below.
A second image was looked at and not used: a D64 from archive.org's d64_Shadowfire_1985_Beyond_Software, SHA-1 fcb48d4bacc967418a26b88751a397944f919033. Its disk name ends ASS PRESENTS: and it holds two programs, SHADOWFIRE (160 blocks, loads at $0801) and SHADOWFIRE TUNER (113 blocks), the add-on the manual announces: a crack, packed into one file. It was not run.
tools.py vice), hard-reset it, and autostart work/shadowfire.tap with autostart() in kit/c64/vice.py. VICE presses PLAY and types the load. Turn warp on with warp(rpc, True): the load then takes about 240 seconds on a four-core container. The border flashes in colour while each block loads; from about the fourth block a picture fills the screen (the Beyond and Shadowfire title, "by Denton Designs").$081B; it masks the CIAs' interrupts, reads both interrupt registers to clear them, and jumps to $4000, the game's first instruction. A stopping checkpoint on $4000 armed after the reset and before the autostart catches it: work/entry.vsf was saved there, with SP $FA, $00 = $2F and $01 = $27 (the motor off; BASIC, I/O and KERNAL in). work/scripts/entry.py does all of this.$1C puts it on the New game icon; fire starts the game.work/play-team.vsf was saved on the team screen that follows, the six Enigma characters in a row and the mission clock at 99:56. Left alone on the control menu, the game plays its own demonstration.$01 = $25 in play, which is the $35 banking with the cassette motor bit set: RAM at $A000 and $E000, I/O at $D000. $00 = $2F.$FFFE is $549B, with the raster interrupt enabled ($D01A = $F1). The handler saves the main program's zero-page temporaries and several variables on the stack before it does anything else, so it is a second thread of the game, not just a frame tick.$FFFA and $0318 is $427D. RESTORE with RUN/STOP held resets the stack and returns into the address held at $42D6/$42D7, a restart; without RUN/STOP it returns.$2000 ($D011 = $3B, $D016 = $D8), its colour matrix at $0C00 ($D018 = $39), VIC bank 0 ($DD00 = $97). A frame recorded with frame.py capture on the team screen had no register writes at all, and its rebuild matched all 104,448 pixels of the emulator's picture. Five sprites are on: sprite 0, multicolour, is the cursor; sprites 1-4 are doubled in both directions.$4000 copies $CA00-$CFFF into the RAM under the I/O chips at $D000-$D5FF with $01 = $14 (all RAM), copies $1000-$13FF into colour RAM, and fills $0400-$07FF with spaces. So $CA00-$CFFF in the hand-over holds what the game later reads at $D000; the RAM there is unwritten until then.The hand-over snapshot work/entry.vsf is the image the listing is built from: it holds every byte the tape loaded before the start-up moves any of it.
Decoded from the pulses by work/scripts/tap.py. The first two blocks are in the Commodore ROM loader's format: a header and a short program. The rest is a turbo format: pulses of about 272 cycles are 0 bits and about 528 cycles 1 bits, least significant bit first; a block is a pilot of $E3 bytes, the sync byte $ED, its first address and the address after its last (low byte first), the bytes, and one more byte, which was $44 in every block. Each block's bytes were compared with the hand-over snapshot.
| Block | Lands at | Bytes | In the hand-over |
|---|---|---|---|
| ROM header and program | tape buffer, and $044E-$0617 (the header's addresses) | the header is still in the tape buffer; the program's bytes were overwritten | |
| 1 | $0300-$030B | 12 | equal: BASIC's six vectors, at their default values |
| 2 | $0C00-$0FFF | 1,024 | 349 equal: the loading picture's colour matrix, later overwritten by block 9 |
| 3 | colour RAM $D800-$DBFF | 1,024 | the picture's colour RAM |
| 4 | $1400-$3FFF | 11,264 | 3,475 equal: the loading picture's bitmap at $2000-$3F3F, and $1400-$1FFF beside it, all overwritten by block 9 |
| 5 | $D000-$D02F | 48 | the video chip's registers: multicolour bitmap at $2000, colours at $0C00, which puts the picture up |
| 6 | $E000-$FFF7 | 8,184 | equal, every byte |
| 7 | $4000-$CFFE | 36,863 | equal, every byte |
| 8 | $D000-$D02F | 48 | the video chip's registers again: the display off ($D011 = $0B), the default text screen |
| 9 | $0900-$3FFE | 14,079 | equal, every byte |
| 10 | $081B-$082D | 19 | equal: the hand-over stub |
So the game is $0900-$3FFE, $4000-$CFFE and $E000-$FFF7, all of it loaded as it stands. The loading picture (blocks 2 to 5) is the loader's: the game never shows it, and block 9 loads over it.
The ROM loader reads a header and a program; the header's name and spare bytes, which stay in the tape buffer, are not 6502 code as they stand, and neither are the loader's bytes in pages 2 and 3 at the hand-over, so the loader is stored scrambled and unscrambles itself as it runs (the program counter sat at $03E5 and $03E8 while blocks loaded). It then reads the ten turbo blocks above in order, putting up the loading picture first so there is something to look at for the four minutes the rest takes, blanking the screen again before the last big block loads over the picture, and finally runs the stub at $081B, which starts the game at $4000. Not annotated further, by policy.
The symbol map for this game is symbols.json; the listing behind the Source tab is listing.json. Write-ups, facts and symbol maps are CC BY-SA 4.0. The game itself is not hosted here.