Video summary
Gameboy Emulator Development - Part 12
Main summary
Key takeaways
Summary (Technological concepts & implementation details)
This video is part of a Game Boy emulator development series, focusing on wiring up the PPU LCD registers and creating a PPU LCD mode state machine. It builds infrastructure needed before full pixel rendering.
1) LCD register wiring (PPU ↔ bus interface)
LCD register set added via lcd.h
A new header (lcd.h) is created to define the LCD-related memory-mapped registers in bus order (so read/write can be done by computing an offset rather than many if/else blocks).
Key registers and purposes:
-
LCDC / LCD control at
0xFF40Bits control enabling background/window/sprites, sprite height (8 vs 16), and overall LCD enable (bit masks via macros). -
STAT / LCD status at `0xFF41**
- Low bits indicate the current LCD mode.
- LYC interrupt flag logic: sets/clears when
LY == LYC. - Interrupt enables for specific STAT events (multiple STAT interrupt sources).
- Scroll registers
SCYat0xFF42(scroll Y)SCXat0xFF43(scroll X)
- Line counters
LYat0xFF44= current scanlineLYCat0xFF45= compare register; ifLY == LYC, set STAT flag and potentially raise interrupt.
- DMA already known at
0xFF46 - Palettes
- Background palette at
0xFF47 - Sprite palettes at
0xFF48and0xFF49(OBJ palettes)
- Background palette at
- Window position registers
WX/WYat0xFF4A(window Y/X; described as window position—video notes it will be discussed later)
Palette storage & initialization
The LCD context includes:
- Palette color entries (default colors like white/light gray/dark gray/black)
- Palette update logic based on the hardware rule: each palette byte encodes 4 colors using 2 bits per color index.
- For sprite palettes, the rule differs: color index 0 is transparent (ignored), implemented by not mapping it to a real color like the background does.
Macros for LCDC and STAT bit extraction
Macros are created to extract/identify flags such as:
- Background/window enable (LCDC bit 0)
- Object/sprite enable (LCDC bit 1)
- Object height (LCDC bit 2; selects 8 vs 16)
- LCD enable (LCDC bit 7, referenced via bit macros in comments)
- STAT mode extraction from low bits
- STAT interrupt enable bits (shifted so checking them is straightforward)
2) lcd.c implementation: context, read/write, special handling
LCD context initialization defaults
On lcd_init():
- LCDC set to
0x91 - Scroll X/Y set to
0 LYandLYCset to0- Default palettes:
- Background palette default
0xFC - Sprite palettes default
0xFFeach
- Background palette default
- Video/debug color initialization sets default palette entries (white → black).
Bus reads/writes without branching
Because registers are stored in bus order, the code:
- For reads: computes
offset = address - 0xFF40and returns a byte from the LCD context array. - Avoids many conditional blocks.
Bus write special cases
In lcd_write():
- If offset corresponds to
0xFF46, trigger DMA start. - If offset is within palette register range (
0xFF47–0xFF49), update palette entries by decoding the 2-bit color fields. - Palette update logic assigns palette indices
0–3using shifts and masks.
3) Bus wiring (io.c): route LCD register accesses
In the IO bus layer:
- Reads: if address is between
0xFF40and0xFF4B, route tolcd_read(). - Writes: same range routes to
lcd_write()(replacing a prior hack with proper routing).
This is described as providing a “good start” to test basic emulator behavior.
4) Create a PPU LCD mode state machine
LCD modes enumeration (implemented via state machine header)
A separate ppu_sm.h (planned/created) defines modes:
- Mode OAM (object attribute scan)
- Mode X for pixel transfer (video uses a nonstandard name like “extra/trans”; conceptually corresponds to transfer)
- Mode HBlank
- Mode VBlank
These correspond to the Game Boy PPU LCD state progression.
PPU context additions
Inside the ppu context initialization:
- Game Boy timing constants:
- Lines per frame:
154 - Ticks per line:
456
- Lines per frame:
- New PPU fields:
current_frameline_ticks(ticks elapsed within the current line)video_bufferallocation (resolution × bits-per-pixel; pixel rendering not implemented yet in this part)
PPU tick loop
On each emulator tick:
line_ticks++- Mode transitions call the relevant state handler:
ppu_mode_oamppu_mode_x(transfer)ppu_mode_hblankppu_mode_vblank
Mode transition rules (simplified for now)
Implemented logic is intentionally partial—enough to switch between states correctly before pixel pipeline work.
Key behaviors:
- OAM → Transfer after ~
80ticks (matching OAM scan duration from the pixel FIFO/mode description). - Transfer → HBlank after additional ticks (uses
80 + 172as a threshold; described as covering minimal drawing/transfer time). - VBlank / LY updates
- When enough ticks/lines have elapsed, increment
LY. - If
LY == LYC, set STAT flag. - If corresponding STAT interrupt enable bit is set, request a STAT interrupt.
- Transition to new frame once
LYexceeds the frame’s last visible line count (144 is referenced for visible region;lines per frameis154). - Request VBlank interrupt when entering VBlank at the proper LY boundary.
- When enough ticks/lines have elapsed, increment
- After frame completion:
- Reset
LYto0 - Reset
line_ticks - Return mode to OAM
- Reset
5) Frame timing and UI update optimization
FPS limiting / frame pacing (~60 FPS)
The PPU logic tracks time using a planned get_ticks() helper (SDL-based), implementing:
- Target frame time =
1000/60milliseconds - Delay if the frame finished early
- Every second, compute and print FPS using frame counters
Update UI only once per frame
In emu.c:
- Instead of updating the UI very frequently, it calls
ui_updateonly whenppu.current_framechanges. ppu_tick()is hooked into the emulator’s timer tick path.
Outcomes / testing notes
After wiring LCD registers and PPU ticks:
- Games/apps like Dr. Mario start running enough to show some activity.
- There are “unsupported bus rides/writes” because not all IO/register behavior is implemented yet.
- Some animations/debug output appear, indicating the timing and CPU/PPU integration are functioning at least partially.
The video ends by stating that the next step is the pixel processing pipeline, beginning with background rendering, then sprites/windows later.
Main speakers / sources
- Low Level Devil (channel/series narrator: “low level devil channels game boy emulator development series”)
- Official Game Boy documentation / Pan Docs (referenced throughout for timing and register bit meanings)
- SDL (SDL get ticks) for timing/fps pacing
- Code modules referenced: CPU, IO, DMA, PPU (as implementation sources within the project)