1988 · Systems Editoriale (Commodore 64 Club)

Mr. Hat: the source

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

Every routine, table and variable the game uses, with the names and descriptions given to them. Click an operand to follow it; the counter beside a label lists what refers to it. The reference below the fold is rendered from the game's facts file.

Technical reference: memory map, timing, tables, cheats

Current truth for this game. The workflow lives in kit/skills/; how this understanding developed lives in agent-history.md. Every fact names the routine or table it comes from. Unless marked live, a fact comes from reading the code in work/handover.vsf (orientation.md). Live means observed in VICE (vice-mcp v3.13.1, PAL), with the script named. Times assume PAL, 50.12 frames a second.

Build

No version or build string was found. The image is the game as Commodore 64 Club issue 14 shipped it on side B of its disk (orientation.md): a freezer backup of the game on its title screen, loaded by a fast loader. The tape image mrhat.t64, which the contributor took from the GameBase collection, holds the same file byte for byte. The game's own BASIC line survives at $0801: 10 SYS 2157 and the text SYSTEM EDITOR<<<E ** ($0810). Chiola's earlier game Lupenio (SIPE, 1988) left text and dead code in the image ("Leftovers", below), and a copy of the Supermon machine-code monitor sits at $97ED-$9FFF.

Versions

Three copies were compared, each started in VICE and stopped at the game's first instruction (work/versions/bootcmp.py), memory read whole.

The rest is start-up: zero page, the stack page and the KERNAL's variables and vectors ($0287, $028C, $02AF, $0304-$030B; $2D-$32 point at $5843, the end of botowrap's file), RAM the game never uses under I/O and the KERNAL, and tables at $0334-$03CB that are already there before the game starts, so belong to the unpacker; no instruction in the game refers to $0334-$03CB.

Memory layout

ThingWhere
Entry (SYS 2157)$086D: JSR $0876 (sets $01 = $36, JSR $16A3, the title), JSR $9265 (bitmap mode on), JMP $087E (clears $D40F, $F7-$FA, $64F0, $64F1, sets $44E0 = $20, then JMP $6E00, the start of play)
Title$16A3: KERNAL IRQ back to $EA31, clears $2000-$3EFF, JSR $CDC0, sprite colours, JSR $1770 (the fire wait). $16A0 is JSR $A500 and falls into it
Title set-up$CDC0: starts the music ($C09B), copies the font $CE00-$CFFF to $2800, text mode, $D018 = $1B; then $B683 draws the cast, or, when $1018 is set, $BFA8 the end message
Title wordsscreen codes at $C000-$C058 ("Text")
Play screenbitmap at $2000-$3F3F, colours at $0400, hires ($D011 = $3B, $D016 = $C8, never written anywhere in the image)
Status linebitmap rows 23-24: SCORE digits in columns 13-18, ROOM in 24-25, STAGE in 31, each digit two cells high, drawn from the glyphs at $1600 (16 bytes a digit, top cell first)
Mr Hathardware sprite 4: his position is the sprite's own registers $D008/$D009, stepped with INC/DEC in place; no other copy of it is kept live (work/track.py)
Lives markscolour cells $07B9, $07BA, $07E1, $07E2 ($BC shown); a death recolours one to $CC, grey on grey (by the code, a death in room 3 recolours two; see "Lives") live ($07B9 $BC → $CC after one death, work/death1.vsf)
Interruptstitle: $0314 = $C0AF (music); play: each room but room 10 installs its own handler (room 1 $1C00, which jumps to $AAB2), and all end through $8215 → $BF30 → $B530 → $8C00 → $7C70. $01 = $36 throughout
Room codeten blocks for eleven rooms (4 and 7 share one): set-up, interrupt handler (none for room 10), main loop, floor rules (table below)
Shared enginewalking $5340/$5370, ladder step $4B20, collision test $55B0, death $A877, score $41DA, graphics library $A400-$A6FF, object drawers among other shared code at $A702-$ABEF, tiles $AC00-$AFFF
Musicplayer $C080-$C373 (with the vector hooks $CB07, $CB14), data $C374-$CDBF
LeftoversLupenio's text $7E28-$7FE9 and $A1F6-$A35A, a dead ending $7CF6-$7E27, an F1 wait $8000; Supermon $97ED-$9FFF

The rooms

$100C holds the room number, stored by each room's set-up (in BCD: room 10 is $10, room 11 $11). $4ACD is the entry code: the room Mr Hat leaves writes it, and the next room's set-up reads it to place him. The table at $A817-$A85E holds ten trampolines, rooms 4 and 7 sharing one, each JSR $A84E (screen back on) then JMP to a main loop.

RoomStage$100CSet-upIRQMain loop
110$6E00 (via $17DF), stores at $6F21$1C00$42EF
222$4D80, stores at $4EAF$510A$51F4
323$4642, stores at $478E$48B0$4910
424$9000 with $4ACD = $10 or $28 (it also tests $07, which nothing writes)$B9AD$9320
525$573D$596A$5A60
636$5E10$608A$6220
737$9000 with $4ACD = $40 or $48 ($95A0)$B9AD$9320
838$65E0$683D$6A20
939$7140$7600$76B0
104$10$8300none$8410
114$11$8480$8660$86B0

Live: each set-up, started from play (work/goroom.py <set-up> <entry code>), draws its room with its ROOM digit, and every set-up but the one rooms 4 and 7 share draws the STAGE digit too; those two keep the digit of the room before (2 and 3 in play; started from room 1, the references show STAGE 1) (reference/room02-setup.png to room11-setup.png).

The main loops are copies of one template. Rooms 6 and 8's loops ($6220-$6335, $6A20-$6B35) are 278 bytes each and differ in 54, all but one of them call, jump and branch operands, the last a JSR at $6333 where room 8 has a JMP at $6B33 (checked byte for byte). Room 9's loop matches room 11's instruction for instruction; rooms 2 and 6 match it but for the last jump, and rooms 5 and 8 differ in their last two or three instructions. Rooms 1, 3 and 4/7 differ more, and room 10's loop is a ladder loop of its own. What each room adds is its own floor rules, hazards, guardians and objects.

Exits, from the room code: room 1 to room 3 ($44C0 → $4722); room 2 right to 3 ($4642); room 3 to room 2 with entry code $08 and to room 4 with $10 ($4C34, dispatched at $483A), and room 4 back to room 3 ($94FB-$9500, JMP $4642); room 2 down its ladder to 6 ($5E10); room 4 right to 5 ($954B); room 5 left to 4 and down its ladder to 9; room 7 left to 6 ($9510) and right to 8 ($955B); room 6's ladder up to 2, bottom right to 7; room 8 to 7, to 9 and down its shaft to 10; room 9 left to 8, up to 5, down to 11; room 10 up its ladder to 8; room 11 up its ladder to 9.

Controls

| $DC00 | Action | |---|---| | $6F (fire alone) | jump, the way Mr Hat faces ($438D room 1, $5227, $76E3, $86E3 and the other loops) | | $67 / $6B (fire with right / left) | jump, the way he faces, from standing or straight after a jump ($444F room 1, $4A69 room 3, $52F0 room 2) live: fire with right from standing jumped at once (work/track.py death1) | | $77 / $7B | walk right / left | | $7D (down) | crouch (frame $2A), climb down, and take a treasure | | $7E (up) | climb a ladder or ride a lift up; nothing on a floor |

Live: from standing in room 1 (work/death1.vsf), fire held: sprite Y 204 to 183, held 13 frames, back to 204, and again while fire stays down; stick up held 20 frames: no movement (work/track.py).

Mechanics

Graphics

Guardians

Every room's interrupt handler but room 10's (it has none) moves its guardians, often by patching its own INC/DEC opcodes to turn round: room 6's patroller ($608A, X $80-$FE, $6096), room 8's diagonal bouncer ($683D), room 9's floor patroller ($7627/$762A), its chaser on the upper floor that follows Mr Hat one pixel an interrupt, about 62 a second ($7B70), and the drops that fall from its lamps (sprites 6, 7); room 11 moves sprite 3 towards Mr Hat on its top floor ($8660); room 8's interrupt also sways sprite 7 one pixel left and right with frames $3D/$3E ($B993 → $B97A → $B92D). $7000 flips the frames of sprites 0 and 1 for rooms 1, 2, 5 and 6.

Sound

Text

The game's words are screen codes, in the order of the C64's own set. The title's font at $CE00 is drawn two cells high: glyph 0 blank, 1-26 the top halves of A-Z, 33-58 their bottom halves (code + 32), 29 and 61 "!", 59 a full stop, 60 a comma. The title's words are at $C000-$C058: MR HAT, OCTOPUS, SNAILY, DYNKY (so spelt, as on the screen), KNIFFY, AND ALL OTHERS, SYSTEMS PRESENTS A NEW GAME WITH. Nothing reads DOUBLE at $C007 or the SY at $C015 that would make OCTOPUSSY. SCORE, ROOM and STAGE are bitmap graphics, not text; the end message is at $B7D8.

Leftovers

Corner cases

Hardware register census

RegisterUseWhere
$D008/$D009Mr Hat's X and Y, stepped in placeabout 400 instructions across the room code
$D000-$D00F, $D010the guardians', the lift's and the X high bitsroom code, $1B04-$1BE3, $1C06-$1C81
$D015sprite enable, also compared as a whole byte, mostly as thresholds$16CA, $1B04, $1C1C-$1C7B, $7A30
$D01Esprite-sprite collision, the only way of dying by touchthe tests $55B0, $1F70, $5028, $B666/$4C40; read elsewhere only to clear it ($170C-$173D, $A8DC)
$D011, $D018text for the title, bitmap for play ($9265, $CDC0); $D011 bit 4 blanks the screen during set-up ($A4FD)
$D012raster line read as a random number ($A8F0), a pitch ($B4C0), and for timing ($A944, $A960, $B54B)
$D016never written
$D020, $D021, $D022border; $D021 and $D022 written but not shown in hires bitmap mode
$D025-$D02Esprite colours; $D025/$D026 stepped during immunity ($BF41, $BF44), $D02B flashed at a death ($A92D)
$D400-$D418the music player ($C11D, $C1FC), the in-play tune ($8C00), effects ($B4C0-$B653, $41B0)
$DC00the joystick, read at 63 places as exact values; written at six
$DC04/$DC05, $DC0ECIA 1 timer A, the music's tempo$C106-$C1A0
$DC08-$DC0Bthe time-of-day clock, zeroed by the music's first interrupt $C2EB, never read

Live tests

Cheats

Pokes that change the game within its own parameters. Each one names the variable it changes and whether it has been tested live. Untested pokes are labelled as candidates.

EffectPokeStatus
Rooms 6 and 9 lit, as if room 2's candle had been taken$22 = $40 before the room's set-up runs ($5E10, $7140)live: both rooms drawn lit with their objects (work/goroom.py ... 40)
Go to any roomstop the machine in play, $4ACD = the entry code, program counter = the room's set-up (facts.md, "The rooms"), stack pointer $F0live for all eleven rooms (work/goroom.py); the room is drawn and runs, but in room 11 Mr Hat would not stay where he was poked, so play after the jump is not guaranteed
Immunity for good$A877 = $60 and $4C52 = $60, the RTS bytes that $1250 writes for a whilecandidate: what the game's own immunity does, not tried as a poke
Title tune silent in room 1F1 during play ($1019 = 1)live (work/pausetest.py); the game's own key, not a poke
routines tables variables strings branch labels

Loading listing…