segagenesisrecomp + Sonic the Hedgehog tech demo
- segagenesisrecomp: https://github.com/mstan/segagenesisrecomp
- Sonic runner: https://github.com/mstan/SonicTheHedgehogRecomp
Sonic the Hedgehog native tech demo using genesisrecomp
Context
In my past articles, I've been exploring the feasibility of using Claude's AI to aid in the development recompiles and better my understanding in this space. After relative success with the Playstation and NES tech demos; I set my sights to try the Sega Genesis next.
Unlike the my previous demos however, the the Genesis has, overall, proven less fruitful than other attempts due to its architecture. While I did get the game to boot and render and play some basic functions, the process was overall a rocky one.
Ghidra Didn’t Translate Well
On PS1, Ghidra was essential. It gave reliable function boundaries and control flow, and worked well as a reference during codegen.
On the Genesis, that broke down.
The 68000 code is full of:
- jump tables
- mid-function branches
- indirect jumps through registers
Ghidra can disassemble it, but it doesn’t produce clean function-level structure. That made it much less useful as a source of truth compared to the PS1 workflow.
What Worked: Dual Execution
The approach that actually scaled was running native code alongside an interpreter Using clownmdemu as the base, native functions that were compiled were written in parallel and hooked at certain states of the interpreter's execution. Using the same state at the type of execution, this allowed me to determine if native equivalents existed (and flag dispatch misses where they did not), and for ones that did, whether they computed the end result in the same fashion.
Overall, this worked worked well:
- ~133 verified functions in ~1 day
- ~112 functions validated clean across 200k+ frames
It also surfaced real issues quickly:
- CMP not setting X flag correctly
- VDP control writes needing atomic 32-bit behavior
- BTST truncation issues
Without this setup, those would’ve been much harder to isolate.
Fully Native Runner
After I was able to achieve a reasonable amount of native function execution integrity, (~337 functions), I switched the primary runner from interpreted + cpu emulation to a native execution. Doing so immediately began to expose the brittleness of native vs the hardware configuration of the Genesis.
- Dark palettes
VBlank handler clobbered registers, causing fade-in/fade-out sequences to end prematurely
→ fix: manual full register save/restore - PLC softlock
Nemesis decompression ran in wrong context
→ fix: run it from fiber yield - Jump → restart loop
Stack overflow corrupted timer memory
→ fix: block writes at $FFFE00
Plus a few more stack/handler edge cases.
~6 runtime fixes total.
The Real Problem: Timing
The core issue isn’t just codegen — it’s timing.
On real hardware:
- 68K execution is interleaved with VDP scanlines
- VBlank fires mid-execution
The native runner executes logic in large uninterrupted chunks. That difference breaks behavior. For example, one bug I attempted to (unsuccessfuly) debug was Sonic's Jumping. It's my belief that the below is a consequence of the timing changes going from interpreted to native – the result being that Sonic can't jump. But I was unable to determine a solution during my spike into this recompiler.
- Sonic_Jump sets velocity
- Sonic_Move later clears it
- On real hardware, VBlank interrupts between them
- In native mode, both run back-to-back → velocity gets zeroed
Tried fixes:
- per-instruction cycle counting
- scanline interleaving
- injecting interrupts mid-run
- cycle-aware codegen
Cycle tracking seemed accurate, but behavior still diverges (likely input/interrupt timing edge cases).
Current State
- Game boots
- SEGA logo works
- Title screen renders
- Green Hill loads
- Basic Movement, enemies, rings, HUD all work
Known issues:
- Jumping is broken
- Audio timing issues (Z80 not advancing correctly)
- Some sprite glitches
- Missing functions (picking up fallen rings after taking damage)
Takeaway
Static recompilation on the Genesis works...to an extent
Dual-execution validation works. But getting a holistic behaviorla match has proven much harder. While a foundation exists, it's hopeful that others can using this as a jumping off point for a more capable recompiler for the Genesis.