Flow A: SDL startup and main loop
End-to-end trace from process start through ROM load to the perpetual GB_run() loop. This is the reference integration pattern for custom frontends.
Trigger
User runs ./sameboy game.gb or opens SameBoy from the desktop.
Call chain
Step 1 — main() bootstrap
main() initializes SDL subsystems (video, audio, events), parses flags (--model, --fullscreen, --stop-debugger), loads prefs.bin from beside the binary or SDL_GetPrefPath("","SameBoy"), creates the window, and calls run(rom_path).
Configuration lives in global configuration_t (SDL/configuration.c): key bindings, scale factor, color correction, rewind length, rumble mode, etc.
Step 2 — GB_init and callback registration
On first ROM load, run() calls GB_init(&gb, model) which allocates WRAM/VRAM, zeroes state, calls GB_reset(), and loads default border tile data.
SDL then wires the core to OS services:
| Registration | Handler | Purpose |
|---|---|---|
GB_set_boot_rom_load_callback | load_boot_rom | Load dmg_boot.bin etc. from DATA_DIR |
GB_set_vblank_callback | vblank | Flip buffers, render texture, poll input |
GB_set_pixels_output | active_pixel_buffer | Indexed pixel write target |
GB_set_rgb_encode_callback | rgb_encode | Palette index → ARGB32 |
GB_apu_set_sample_callback | gb_audio_callback | Queue PCM to SDL audio |
GB_set_update_input_hint_callback | handle_events | Poll SDL events mid-frame |
GB_set_rumble_callback | rumble | Gamepad rumble |
Step 3 — ROM and peripheral file load
GB_load_rom(&gb, filename) reads the file into gb->rom, computes CRC32, calls GB_configure_cart() to detect MBC type from header byte 0x0147.
Side files loaded by path substitution:
game.sav→GB_load_battery(MBC RAM + RTC)game.cht→GB_load_cheatsgame.sym+ bundledregisters.sym→ debugger symbols
If GBEmulatedModel is "auto", model may change after ROM load (model_to_use()).
Step 4 — Main emulation loop
The infinite loop is not frame-based:
while (true) {
if (paused || rewind_paused) {
SDL_WaitEvent(NULL);
handle_events(&gb);
} else {
if (do_rewind) GB_rewind_pop(&gb);
GB_run(&gb); // one CPU quantum
}
if (handle_pending_command()) goto restart;
}
Validation: CLI model flag, ROM readability (error dialog via log capture).
Business logic: All in Core; SDL only sets pending_command flags for reset/save/load state from hotkeys.
State writes: Core mutates GB_gameboy_t; SDL writes prefs.bin on exit via atexit.
Errors: Load failures show SDL message box; unreadable save path logs warning.
Step 5 — vblank presentation (output path)
When PPU completes a frame, GB_display_vblank sets vblank_just_occured and invokes vblank_callback. SDL's vblank():
- Calls
handle_events(gb)again (input at frame boundary) - Optionally blends previous frame (
configuration.blending_mode) render_texture()→ OpenGL shader or SDL_Renderer blit- Swaps pixel buffers and re-points
GB_set_pixels_output
Audio flows independently via gb_audio_callback on each APU sample batch, with queue backpressure at freq/8.
Failure modes
- Missing boot ROM → callback returns false; game may run without boot animation
- Re-entrant
GB_run→ assertion in debug builds goto restarton model switch — full re-init path, must re-register callbacks ifGB_freecalled