Circus Charlie

A whole game living inside the interrupt, five circus acts chosen by a single byte, and the ring drawn from the cartridge's tables, frame by frame, without one byte differing from the emulator.

Konami - (c) Konami 1984 - An RC-712 16 KB cartridge - MSX1 - page 1 - Dump 89e1bada...

Not one capture here. Every picture is drawn from the bytes of the ROM: the scenery with the cartridge's own machines rewritten in Python, and the characters with the game frame written from its tables, checked against openMSX down to zero bytes. The listing and the numbers are reproducible with make, and reassembling gives back the ROM byte for byte.

The cartridge in numbers

100%of the binary explained
576named routines
47.1%of the listing commented
7,894bytes of code
8,490bytes of data
0bytes unidentified

What turned up when we took it apart

The whole game lives inside the interrupt

INIT (0x407B) hooks H.KEYI with a jp 0x402C, clears the one kilobyte of RAM and then sits forever on the jr $ at 0x40A9. Everything else happens in the hook, once per frame: sound, controls and the current scene. The lock at 0xE005 counts overlapping interrupts and serves the one that came in meanwhile without going back through the stack.

The ring, from the tables

The characters and obstacles are not placed by the scenery set-up: the game places them frame by frame. tools/pista.py rewrites that frame from the cartridge's tables -moving objects 0x549B, poses 0x5116, start-ups 0x6161- and against 43 openMSX dumps it gives 0 bytes different.

Five acts, one byte

(0xE052) says which act it is, 0 to 4, and the cartridge dispatches on it through four tables stuck right behind their calls: set-up (0x5FEF), start (0x60B6), frame (0x4C93) and the moving objects table (0x549B). Three of them were labelled off by one in the listing; the RAM measured at the start of each act gave it away.

Scenery that repeats every 240 bytes

0x458F reads through DE and writes through HL, the other way round from what it looks like. That is why 0x7477 copies 0x2AC0 over 0x2BB0, overlapping, and the horse act's scenery repeats by itself.

The 1P blink watches the clock

The ld hl,(0xE002) brings the flags in L and the frame counter in H. The bit 5,h is not the number of players: it flips the mask between drawing and erasing every 32 frames.

Four sprites per line

The VDP does not draw the fifth sprite crossing a line. On the trapeze six of them cross lines 48 to 52, and two are dropped. With that limit the drawing matches the emulator's picture in 49,152 dots out of 49,152.

What the cartridge draws

Under each picture is where it comes from and what is on it.

The trapeze, with the trampoline below. Drawn from the tables, not a capture.
The trapeze, with the trampoline below. Drawn from the tables, not a capture.
The lion and the ring of fire, which is drawn with background tiles.
The lion and the ring of fire, which is drawn with background tiles.
The tightrope and the monkeys.
The tightrope and the monkeys.
The balls.
The balls.
The horse.
The horse.
The title screen, built from the ROM and checked down to zero bytes.
The title screen, built from the ROM and checked down to zero bytes.
The company screen. It shares its scenery with the title screen byte for byte; only the name table changes.
The company screen. It shares its scenery with the title screen byte for byte; only the name table changes.
The sprite patterns, from 0x61BA and 0x5FCF. On the title screen they are all zero: the cartridge only uploads them when an act begins.
The sprite patterns, from 0x61BA and 0x5FCF. On the title screen they are all zero: the cartridge only uploads them when an act begins.