Repository navigation
v0.5.0 - Gamepad input + dispatch audit + auto-discovery codegen
Production release (Release x64 build, not debug).
What's new
SDL_GameController gamepad input
Plug-and-play support for Xbox controllers (XInput backend on Windows) and HID gamepads (PS / Switch Pro / generic). Keyboard still works simultaneously — both layered via OR.
Default Xbox mapping:
| Input | Genesis action |
|---|---|
| D-pad / left stick | D-pad (8000 axis deadzone) |
| A / Y (face) | Genesis A |
| B (face) | Genesis B |
| X (face) | Genesis C |
| Start (Menu) | Start |
| Back (View) | Turbo (parallel to Tab) |
| LB | Quicksave slot 1 (parallel to Shift+F1) |
| RB | Quickload slot 1 (parallel to F1) |
Recompiler: auto-discovered JMP-table dispatch
The recompiler now recognises two JMP (PC,Dn.W) dispatch patterns directly from the JMP base bytes, without needing per-site disasm seeds:
- Duff's-device (8+ consecutive identical 2-byte opcodes from base): emits an in-function
switch+goto label_XXXXXXor promotes targets to function entries depending on whether they're in the parent function or land past the next adjacent function. - Offset table (
dc.w (target - base)rows pointing to interior labels): same — in-function switch when local, function-entry promotion when cross-function.
Eliminated all four INTERIOR_UNRESOLVED dispatch sites the dispatch audit was flagging in Sonic 1:
Deform_SLZat$00647C— Star Light Zone background deformation Duff's device.Elev_Typesat$010BE6— elevator object state dispatcher.Circ_Typesat$010DFC— circular-platform state dispatcher.SStom_Moveat$00BAD2— sub-stomper object handler.
Before this release these sites silently failed at runtime (the dispatch lookup couldn't find the interior PC, function returned short, scroll/object handling did nothing). Now they all dispatch correctly via either an in-function switch or call_by_address.
Build-time dispatch audit
Every recompiler run now writes generated/sonic_dispatch_audit.log next to the generated C. Every JMP (PC,Dn.W) and JSR/BSR (PC,...) site is classified:
static_target/in_function_switch/dynamic_register— safe.fallback_hybrid/function_entry,bra_w_trampoline,offset_table— runtime resolution viacall_by_address, safe.fallback_hybrid/INTERIOR_UNRESOLVED— the silent-failure class the CPZ-scroll fix surfaced. Sonic 1 now has zero of these.
If a future recompiler change introduces a new INTERIOR_UNRESOLVED site, it shows up in the audit log at build time instead of as a user-visible bug.
Observability: interior-label dispatch miss logging
Runner-side counterpart to the audit. interior_label_misses.log now captures every silent dispatch failure with addr / in_func / frame, so any latent silent-failure class shows up at runtime rather than hiding.
Under the hood
- New
runner/gamepad.{h,c}— SDL_GameController integration. recompiler/src/code_generator.c—seed_pc_idx_dispatch_targetsauto-discovery helper, in-functionswitchemit for Duff's-device + offset-table patterns, dispatch audit.recompiler/src/game_config.{h,c}— newextra_seedsfield for disasm local-label PCs (Sonic 2 only; Sonic 1 has no .lst checked in).tests/tools/gen_disasm_labels.py— captures asm68k anonymous labels (+:,-:).
Usage
Drop a legally-acquired Sonic 1 ROM (sonic.bin or .md) anywhere Windows can find it; the exe will prompt for one on first launch.
SonicTheHedgehogRecomp.exe <rom.bin> # normal play
SonicTheHedgehogRecomp.exe <rom.bin> --turbo # no-wait (for captures)
SonicTheHedgehogRecomp.exe <rom.bin> --wav out.wav # audio capture