Flow C: PPU frame rendering
The Picture Processing Unit (implemented entirely in display.c) is advanced by GB_display_run on every GB_advance_cycles call. A complete frame ends at GB_display_vblank.
Trigger
Implicit — any CPU bus cycle advances the PPU by the same T-cycle count via GB_advance_cycles → GB_display_run(gb, cycles, false).
LCD timing overview
GB_display_run — state machine entry
GB_display_run (display.c:1533) is the PPU heartbeat:
- Handles window Y trigger scheduling (
wy_check_scheduled) - Splits cycles at line boundaries when batch would cross
LINE_LENGTH - DMG STOP mode: PPU frozen except artificial vblank for timing
- Runs
GB_BATCHABLE_STATE_MACHINE(gb, display, cycles, 2, !force)— states 1–17+ for pixel pipeline
The state machine implements: tile fetch (2 bytes per tile), BG FIFO, OAM sprite sorting, priority mixing, CGB palette indexing, and STAT interrupt line updates.
Pixel output path
Rendered pixels write to gb->screen (uint32_t ARGB buffer allocated by frontend or core):
- PPU computes palette index per pixel
rgb_encode_callbackconverts (or internalGB_convert_rgb15for CGB)- Border tiles composited when
border_mode != NEVER(DMG/CGB/SGB borders) - SGB HLE may render border and palettes via
GB_sgb_renderin vblank
Screen dimensions: 160×144 game area; bordered modes use larger buffer (BORDERED_WIDTH × BORDERED_HEIGHT).
Frontends call GB_set_pixels_output(gb, buffer) to set write pointer; updated each vblank after buffer swap.
GB_display_vblank — frame completion
At frame end (display.c:173):
- Sets
vblank_just_occured = true(consumed byGB_run) - Updates debugger CPU usage counters
- SGB:
GB_sgb_renderif HLE SGB active - Turbo:
GB_timing_sync_turbomay skip render and fireGB_VBLANK_TYPE_SKIPPED_FRAME - LCD off: fill screen white/black per model
- Normal path: invoke
vblank_callback(gb, type) GB_handle_rumble,GB_timing_sync
Vblank types: NORMAL_FRAME, SKIPPED_FRAME, REPEAT (CGB frame repeat), LCD_OFF, ARTIFICIAL (STOP mode DMG).
Key video section fields
| Field | Role |
|---|---|
bg_fifo, oam_fifo | 8-entry pixel FIFOs |
visible_objects[10] | Sprites on current line |
current_line, cycles_for_line | LY and intra-line position |
display_state | State machine phase (1–22+) |
background_palettes[32] | CGB BG palette RAM |
oam[160] | Sprite attribute table |
frame_skip_state | LCD enable/disable edge handling |
Failure modes and accuracy risks
- STAT mode delay TODO — simplifying PPU states may break STAT IRQ tests
- Mode 3 duration varies with sprite count — wrong object limit → graphical glitches
- HDMA during mode 3 conflicts —
dma_ppu_vram_conflicttracks bus fights - Disabling rendering (
disable_rendering) used by tester — must not skip vblank flag incorrectly
External reference: Pan Docs — LCD Timing