Flow E: Libretro host-driven frame

The libretro core inverts control: RetroArch calls retro_run() once per host frame, and SameBoy must produce exactly one emulated frame of video and audio per call.

Trigger

RetroArch (or any libretro frontend) calls retro_run() at display refresh rate.

Lifecycle vs SDL

SDL model: Libretro model: ───────────── ───────────────── while (true) retro_run() { // once per host frame GB_run() // partial poll input } GB_run_frame() OR sync GB_run x2 vblank_callback presents video_cb(frame) audio_batch_cb(samples) }
Initialization — retro_load_game
  1. retro_init() — one-time setup
  2. retro_load_game(info) — allocate gameboy[0], GB_init, load ROM from info->data
  3. init_for_current_model() — register callbacks, set options from check_variables()
  4. Boot ROM from retro_system_directory or embedded C arrays (dmg_boot.c, etc.)
  5. retro_load_game_special — 2-player link: emulated_devices = 2, serial callbacks wired

Libretro build disables rewind, debugger, cheats via compile flags in libretro/Makefile.common.

libretro.c

retro_run — single player
void retro_run(void) {
    if (variable_update) check_variables();
    GB_update_keys_status();  // input_poll_cb + input_state_cb

    GB_run_frame(&gameboy[0]);  // loops GB_run until vblank

    video_cb(frame_buf, width, height, pitch);
    upload_output_audio_buffer();  // audio_batch_cb
}

GB_run_frame (gb.c:1228) temporarily enables turbo without frame skip, loops GB_run until vblank_just_occured, returns nanoseconds elapsed.

Pixels written during emulation to frame_buf slice via GB_set_pixels_output.

GB_run_frame

retro_run — link cable (2 players)

Alternates GB_run on GB0 and GB1 using cycle debt (delta) until both vblank1_occurred and vblank2_occurred:

while (!vblank1_occurred || !vblank2_occurred) {
    if (delta >= 0) delta -= GB_run(&gameboy[0]);
    else            delta += GB_run(&gameboy[1]);
}

Serial bit callbacks connect the two instances for link cable games. Frame buffer composited side-by-side into frame_buf.

Configuration — core options

Options defined in libretro_core_options.inc, read in check_variables():

  • Model (DMG/CGB/SGB/auto)
  • Color correction, palette, border mode
  • RTC mode, link cable enable
  • Audio output (mono/stereo), rumble
  • Screen layout (1×, 2× side-by-side)

Applied via GB_set_* API — same as other frontends, different config source.

Audio and video output

Video: rgb_encode_callback writes to frame_buf; after frame, video_cb hands buffer to RetroArch (with pitch). lcd_status_callback retains last frame when LCD turned off.

Audio: audio_callback accumulates interleaved int16 samples in output_audio_buffer; flushed at end of retro_run via audio_batch_cb.

Rumble: retro_rumble_interface queried in rumble_callback.

Failure modes

  • Host calls retro_run without retro_load_game → undefined (guarded by static state)
  • Link cable with only one loaded game → serial callbacks no-op
  • Option change mid-frame → applied at start of next retro_run when GET_VARIABLE_UPDATE