Close

Reverse engineering the SM511 game logic

A project log for DONKEY KONG JR. TABLE TOP REVIVAL (CJ-71)

Replacing a dead LCD on a 1983 Nintendo Game & Watch with an ILI9488 TFT, driven by an RP2040 — fully playable in the original shell.

kaliceKalice 6 days ago0 Comments

I reversed engineered the game logic and reimplemented the full game state machine in C:

- Player movement
- Enemy spawning patterns (Game A and Game B)
- Score and miss counter
- Game over logic
- Built-in clock and alarm

The original CJ-71 uses a color filter overlay on a monochrome LCD. Each sprite is a fixed
segment. To reproduce this on a TFT:

- Sprites extracted from MAME artwork assets, converted to **RGB332** (1 byte/pixel) stored in flash
- Transparent pixels use `0x00` as sentinel
- Scenery background (471×238) centered on 480×320 display
- XOR blitting over scenery, deferred flush with union bounding box
- DMA double buffering for tear-free rendering

A Python asset pipeline (`gen_sprites.py`) handles extraction, color conversion, and C header generation.

The SM511 melody ROM (256 bytes) was extracted from MAME. Rather than emulating the SM511
audio engine at runtime, I decoded the melody ROM offline into a table of `(freq_hz, duration_ms)`
note pairs — full audio fidelity with no runtime SM511 dependency.

On the Pico, **Core1** is dedicated to audio, generating square waves on GP8 via
`busy_wait_until()` for drift-free timing. All audio code runs from RAM to avoid flash latency.

The original CJ-71 audio circuit uses a PNP transistor (C458) in a negative-supply design to
drive the piezo. On the Pico, a direct GPIO connection proved sufficient — the piezo pressed
against the original plastic shell acts as a natural resonator and amplifier.

Discussions