1985 · Melbourne House

About this page

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

Footprint

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.

WhatBytesOf 64 KB
Program61,34893.6 %
Code12,59419.2 %
Graphics39,65660.5 %
Level data20.0 %
Sound5,2007.9 %
Text40.0 %
Tables3,7055.7 %
Variables1870.3 %
Screen, bitmap, colour, stack, I/O4,1856.4 %
Unused30.0 %

Status

Tiersilver
Coverage100 % of the bytes the game uses have a description
Build analysed
Copyagent-draft
Toolsemulator: 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
Modelclaude-opus-5-5
Kit version0.0.54

Contributors

Other places to love this game

What the game does, and where we found it

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:

Features

FeatureStatusWhere
Attract mode: the computer fights itself under DEMO, cycling through the backdropsconfirmed$1CD2; live, reference/demo*.png
F1 starts a game; F5 abandons it and returns to the attract modeconfirmed$1E64; live
F3 switches between 1 PLAYER and 2 PLAYERconfirmed$1EA8, $AE; live, reference/options-2player.png
F7 switches between JOYSTICK and KEYBOARD controlconfirmed$1EC5, $C6; live, reference/options-keyboard.png
Fire starts a one-player game from the attract modeconfirmed$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 fireconfirmedstick_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 joysticktraced$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 eachconfirmed$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 boutconfirmedquality 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 decidesdiffersthe 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 scoreconfirmed$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 lastconfirmed$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 statueconfirmed$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 blowconfirmedbull_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 backconfirmedai_think $22CF: blocks, nine scripted plans, attacks by distance; live, reference/play-novice-knockdown.png
A shadow under a fighter in the airconfirmedthe 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.tsoundopenthe 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 Brennanconfirmedthe 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)confirmedDEL flips $CF ($1ED8), which switches the speech samples off ($3186); live, $CF 0 to 1

Beyond the documentation

Found in the code, not in the manual.

Open questions

How the analysed state was reached

How to get from the contributor's own copy to the analysed state. Someone else must be able to follow this exactly.

The emulator

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

  1. check-emulator: 56 of 57 passed; the one failure is

pause-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.

The image

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:

EntryBlocksLoads atWhat it is
menu2$02BCthe 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...
fist1(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)14not read
!!1USR file, not read
data c8USR file, not read
m.prog125$040031,744 bytes of program
game4$C500885 bytes
m.shapes84$100021,248 bytes
m.chset7$63001,536 bytes
m.prerun3$C700512 bytes
m.picture40$600010,000 bytes
loader2$9000272 bytes
m.spchtbl4$9800768 bytes
m.tsound23$20005,632 bytes
m.xsprites11$10002,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):

FileIn memoryBytes equal
m.prog$0400-$7FFF31,744 of 31,744 at the hand-over
m.shapesfrom $800016,851 of 21,248; the shape cells $8000-$BFFF match, the rest was not followed
m.chset$E000-$E5FFall
m.xsprites$F540-$FFEFall: the speech samples
m.prerun$C700-$C8FF at the hand-over, copied by the game to $0200-$03FFall: the speech period table
m.picture$E000 on3,943 of 10,000; not followed
m.spchtbl, m.tsound, game, loadernot found as they standThe 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.

From power-on to play

  1. Hard reset (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.
  2. The Melbourne House menu appears within about four minutes of warp on this host: 1. --> exploding fist / press the appropriate key (reference/menu.png). Saved as work/menu_clean.vsf.
  3. Press 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.
  4. Turn warp off and run. The attract mode starts: the fighters spar on their own under DEMO / JOYSTICK 1 PLAYER (reference/demo.png).
  5. Press fire on joystick port 2 (vice_joystick_set, port 2, held 0.6 s). NOVICE / 1 PLAYER replaces DEMO and the bout begins, timer at
  6. Wait 1.2 s and save on the running machine: work/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.

Hand-over or play snapshot

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.

Steady state

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:

The loader, in a paragraph

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.