1985 · Melbourne House
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.
| What | Bytes | Of 64 KB |
|---|---|---|
| Program | 61,348 | 93.6 % |
| Code | 12,594 | 19.2 % |
| Graphics | 39,656 | 60.5 % |
| Level data | 2 | 0.0 % |
| Sound | 5,200 | 7.9 % |
| Text | 4 | 0.0 % |
| Tables | 3,705 | 5.7 % |
| Variables | 187 | 0.3 % |
| Screen, bitmap, colour, stack, I/O | 4,185 | 6.4 % |
| Unused | 3 | 0.0 % |
| Tier | silver |
|---|---|
| Coverage | 100 % of the bytes the game uses have a description |
| Build analysed | |
| Copy | agent-draft |
| Tools | emulator: release v3.13.1, v3.13.1-linux-x86_64-gui.zip (vice-mcp), run under Xvfb; check-emulator 56 of 57 (pause-at-instruction failed), disassembler: regenerator2000 0.9.20, host: Linux 6.18 x86_64, Ubuntu 24.04 cloud container with four cores and no display, python: 3.11.15, widget tests: node 22.22.0 (the page's ports checked against the snapshot and the game's code in kit/c64/cpu6502.js); Chromium through Playwright 1.56.1 for the page check, subagents: six annotation agents on disjoint address ranges, started with the harness model alias opus |
| Model | claude-opus-5-5 |
| Kit version | 0.0.54 |
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:
https://www.c64-wiki.com/wiki/The_Way_of_the_Exploding_Fist; Wikipedia, https://en.wikipedia.org/wiki/The_Way_of_the_Exploding_Fist; Lemon64's manual page, https://www.lemon64.com/doc/way-of-the-exploding-fist/665; the World of Spectrum inlay (a Spectrum document); https://frgcb.blogspot.com/2021/01/the-way-of-exploding-fist-melbourne.html. They are second-hand: leads, weaker than a page.reference/), and the code (facts.md).| Feature | Status | Where |
|---|---|---|
Attract mode: the computer fights itself under DEMO, cycling through the backdrops | confirmed | $1CD2; live, reference/demo*.png |
| F1 starts a game; F5 abandons it and returns to the attract mode | confirmed | $1E64; live |
F3 switches between 1 PLAYER and 2 PLAYER | confirmed | $1EA8, $AE; live, reference/options-2player.png |
F7 switches between JOYSTICK and KEYBOARD control | confirmed | $1EC5, $C6; live, reference/options-keyboard.png |
| Fire starts a one-player game from the attract mode | confirmed | $1E64; live on port 2, and on port 1, which shares $DC01 with the keyboard columns |
| 18 movements from the joystick, eight directions with and without fire | confirmed | stick_to_move $286C; live, all sixteen inputs read back as move numbers (facts.md, Controls). Which named kick or punch each fire move is was judged from screenshots only for some |
| Keyboard controls, the alternative to the joystick | traced | $28B2-$29B3: Q W E / A D / Z X C and S or left SHIFT for player 1; P @ * / L ; / , . / and : or right SHIFT for player 2. Not typed live |
| Two players fight each other, one stick each | confirmed | $1B09; fighter 0 on port 2, fighter 1 on port 1 ($2851); live |
| Scoring with yin-yang symbols: half or whole by the quality of the blow; two whole symbols win the bout | confirmed | quality is distance: $2B70-$2C40 against the reach profiles at $7200; four halves ($8A,X) win ($1D5F); live, a whole point at a distance of 12 |
| A bout lasts 30 seconds on a counter; when time runs out the referee decides | differs | the counter ($212F) counts 30 units of 50 passes of the exchange loop, measured at 50 to 66 PAL frames a unit, so 30 to 40 seconds; the decision ($1C72, $1F93) goes to more halves, then more points; live, the judge's sprites at the end of a two-player game |
| Points scored per blow, shown for each player, and a high score | confirmed | $12A5, $206C, $200A; a high score per mode and a five-place table with names ($146A); live, 200 points for a whole point with move $0C |
| Ranks from novice to tenth dan, each opponent harder than the last | confirmed | $B4, $22C3, $1C2D; the computer's level rises with each bout won and sets its timing masks ($2593); two bout wins per rank ($B5) |
| The backdrop changes with progress: Fuji with a pagoda and a torii, a lake under a volcano, a dojo, a Buddha statue | confirmed | $AF AND 3; all four packed in memory ($3F5F-$71FF) and unpacked by $17BB/$1849; decoded and rendered from the image |
| A bonus round in which a bull charges and must be felled with one blow | confirmed | bull_round $132C, after a rank cleared on the Buddha backdrop; only move 7 (down-forward from a crouch) at a distance of $19-$1D fells it, for 3000 points; live, reference/bull-charge.png, reference/bull-felled.png |
| A computer opponent that fights back | confirmed | ai_think $22CF: blocks, nine scripted plans, attacks by distance; live, reference/play-novice-knockdown.png |
| A shadow under a fighter in the air | confirmed | the line-$D9 handler draws fixed sprites from $D480 at Y $DA; live, reference/jump-shadow.png |
A shout ("kiai") during loading; the disk carries m.spchtbl and m.tsound | open | the game itself has a one-bit speech player ($311A) with four samples at $F540, started on attacks and hits; its samples are the disk file m.xsprites (at $F540) and its period table m.prerun. The loader's shout is the loader's, not read by policy; m.spchtbl and m.tsound are not in the game's memory as they stand on disk |
| Music by Neil Brennan | confirmed | the driver at $09A5 and four songs; song 2 live in a one-player bout; tuned for NTSC, so two-thirds of a semitone flat on PAL (facts.md, Sound) |
| Sound effects on and off with DEL (the menu program's text) | confirmed | DEL flips $CF ($1ED8), which switches the speech samples off ($3186); live, $CF 0 to 1 |
Found in the code, not in the manual.
...... ($1472).$2CAE; live).$1C65).m.tsound (5,632 bytes) and m.spchtbl (768) are not in the game's memory as they stand on disk (searched in 16-byte pieces in both snapshots); they may be the loader's shout, not checked.blow_reaction tests CIA 2's timer A ($2CAE) is not known.$15 lines between the fighters' tops ask two row changes of the same raster line ($32EA, $32FD); whether a row then shows a frame late was not tested.How to get from the contributor's own copy to the analysed state. Someone else must be able to follow this exactly.
release v3.13.1, v3.13.1-linux-x86_64-gui.zip (vice-mcp), on Linux x86_64 with no display (run under xvfb-run by the launcher), 2 October
check-emulator: 56 of 57 passed; the one failure ispause-at-instruction, so every stop is made with pause() in kit/c64/vice.py, never vice_execution_pause alone. PAL (6569 VIC-II), 6581 SID. VICE's default drive settings (true drive emulation) load the image; no vicerc was written.
way_of_the_exploding_fistmelbourne_house_1985pal.g64, 317,884 bytes. The header reads GCR-1541, version 0, 84 half-track slots (42 tracks), so it is a GCR image of a disk, not a sector image. Disk name melbourne house, id disk!. The directory, as c1541 (from the emulator build) lists it:
| Entry | Blocks | Loads at | What it is |
|---|---|---|---|
menu | 2 | $02BC | the boot file: autostart loads it first. It lands over the BASIC vectors at $0300, so it runs itself, and carries the text WELCOME. / SEARCHING / LOADING / PLEASE WAIT... |
fist | 1 | (BASIC) | one line: POKE198,1:POKE631,13:PRINT"{CLR}...LOAD"+CHR$(34)+"MENU"+CHR$(34)+",8,8", a stub that loads menu for anyone who types LOAD"FIST",8 |
| (empty name) | 14 | not read | |
!! | 1 | USR file, not read | |
data c | 8 | USR file, not read | |
m.prog | 125 | $0400 | 31,744 bytes of program |
game | 4 | $C500 | 885 bytes |
m.shapes | 84 | $1000 | 21,248 bytes |
m.chset | 7 | $6300 | 1,536 bytes |
m.prerun | 3 | $C700 | 512 bytes |
m.picture | 40 | $6000 | 10,000 bytes |
loader | 2 | $9000 | 272 bytes |
m.spchtbl | 4 | $9800 | 768 bytes |
m.tsound | 23 | $2000 | 5,632 bytes |
m.xsprites | 11 | $1000 | 2,736 bytes |
The load addresses in the files' headers overlap, so the loader places them itself. Searching the snapshots for each file's bytes says where they end up (16-byte pieces, then a byte-for-byte count at the best offset):
| File | In memory | Bytes equal | |
|---|---|---|---|
m.prog | $0400-$7FFF | 31,744 of 31,744 at the hand-over | |
m.shapes | from $8000 | 16,851 of 21,248; the shape cells $8000-$BFFF match, the rest was not followed | |
m.chset | $E000-$E5FF | all | |
m.xsprites | $F540-$FFEF | all: the speech samples | |
m.prerun | $C700-$C8FF at the hand-over, copied by the game to $0200-$03FF | all: the speech period table | |
m.picture | $E000 on | 3,943 of 10,000; not followed | |
m.spchtbl, m.tsound, game, loader | not found as they stand | The disk |
is the Melbourne House release: the menu reads Melbourne House presents and offers one game, with no trainer or crack text anywhere on it.
vice_machine_reset, mode: "hard"), make sure the machine is running, and autostart the image (vice_autostart with the path; VICE loads *, which is menu). Turn warp on to save time.1. --> exploding fist / press the appropriate key (reference/menu.png). Saved as work/menu_clean.vsf.1 (vice_keyboard_matrix, key 1, held a second). The game files load; about 16 seconds in warp (two minutes at normal speed) later the loader enters the game through BASIC's SYS (the return address on the stack is $E146, inside BASIC's SYS handler), at $1AED: JSR $18E8 (machine set-up) / JMP $1EE1. A stopping checkpoint at $1AED armed before the key press catches it: work/entry.vsf, the hand-over, $01 = $36 there.DEMO / JOYSTICK 1 PLAYER (reference/demo.png).vice_joystick_set, port 2, held 0.6 s). NOVICE / 1 PLAYER replaces DEMO and the bout begins, timer atwork/play_round1.vsf, the start of the first bout, both fighters standing (reference/play-novice-bout1.png). This is the snapshot the disassembler and the listing are built from.Left idle, the computer opponent wins the bout in a few seconds and the game returns to the attract mode, so a play snapshot has to be taken straight after the fire press.
work/entry.vsf and work/play_round1.vsf agree byte for byte except in these ranges: zero page, the stack, $0200-$03FF, a few bytes in $0A00-$3EFF (variables), $C000-$C9FF, $CC00-$CFFF, $E600-$E7FF (55 bytes) and $FF00. At the hand-over $C000-$C7FF holds the menu's own code (it begins JSR $C386 / JSR $C4D2 and carries DEL - SOUND EFFECTS ON/OFF), $0200 holds leftover text (1, C, NG FIST) and $0300 the vectors; in play the game has replaced the first two with its own data and zeroed the third. None of the game's code exists only at the hand-over, so the play snapshot is the image: it holds the data the game builds for itself.
Recorded with kit/c64/frame.py capture (work/frame-play.json) a few seconds after play_round1.vsf was saved, and by reading the vectors in the attract mode:
$01 = $15 (bits 0-2 %101: RAM at $A000 and $E000, I/O at $D000), set at $1956. The KERNAL is banked out and the game owns the hardware vectors: NMI $2FF2, RESET $1990, IRQ chained (below). The RAM vectors at $0314 are zero in play.$FFFE: $2FB0 (line 0), $2D62 (line 90), $2F02 (160), $2E11 (181), $2FFF (202), $30E5 (218), $31F2 (255). The ones from line 160 on move sprites down the screen and change their pointers (sprite multiplexing). Line 90 sets $D011 to $3B (bitmap mode) and $D021; line 255 sets it back to $1B (text) for the score panel at the top.$C000-$FFFF): screen at $CC00, sprite pointers at $CFF8, $D018 = $39 in the text band.20-features.menu is a short boot file that loads over the BASIC vectors so that it starts itself as soon as autostart has loaded it, prints its welcome text and loads the Melbourne House menu program, which sits at $C000-$C7FF at the hand-over. The menu offers the one game, and on 1 loads the m.* files and starts the game through BASIC with SYS 6893 ($1AED). The disk is a GCR image, but no loading failure or protection check was seen under VICE's true drive emulation; the loader's code was not read 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.