Video summary
Gameboy Emulator Development - Part 09
Main summary
Key takeaways
Overview
This video is the 9th part of a low-level device / dev series where the creator builds a Game Boy emulator CPU. The focus is:
- Completing remaining CPU instruction implementations
- Adding interrupt handling
- Running the CPU in a separate thread
- Providing a minimal SDL UI so the emulator can be closed cleanly
1) Remaining CPU instructions implemented (mainly bit/flag ops + misc ops)
The speaker begins by collecting leftover opcodes that are not implemented yet, then removes entries that are undefined. They then implement several key remaining instructions:
Rotate/shift accumulator variants
- RLCA / RRCA / RLA / RRA
- Implemented by shifting the A register and updating the carry flag (C) appropriately.
- They compute the bit shifted out:
- Bit 7 for left rotates
- Bit 0 for right rotates
- Flag behavior:
- For RLCA/RRCA: flags are updated with C set from the outgoing bit.
- For RLA/RRA: rotation incorporates carry behavior depending on the specific instruction variant.
DAA (Decimal Adjust Accumulator)
- A special BCD-like correction instruction.
- Uses temporary arithmetic based on:
- Flags N/H/C
- The lower nibble of A
- Updates flags after adjustment, including carry behavior and which flags are set/cleared.
CPL (Complement Accumulator)
- Sets A = ~A
- Updates flags in a fixed pattern according to the instruction rules (including N and H behavior).
SCF (Set Carry Flag)
- Forces carry to 1
- Clears N and H
CCF (Complement Carry Flag)
- Toggles the carry flag using XOR logic.
HALT behavior
- Implemented as:
- Set a halted state
- Remain halted until an interrupt condition occurs
STOP
- Added, but explicitly treated as not fully supported
- Includes a placeholder behavior that terminates/prints “stopping”
- The creator notes uncertainty about correctness and that more research is needed to see whether games use it.
2) Additional instruction coverage (math/stack-ish + interrupt enable/disable)
The creator also adds missing opcode mappings in previously incomplete ranges, including:
-
Sub/SBC immediate variants
- Example: D6 (SUB A, d8)
- Related: SBC A, d8 (decrement with carry)
-
Memory/register load specials
- LD (HL), SP+imm8-style instruction mentioned for opcode F8
- Uses addressing where HL = SP + signed immediate
- Also maps LD SP, HL and other special register transfers (e.g., opcode F9)
- LD (HL), SP+imm8-style instruction mentioned for opcode F8
-
Interrupt control
- Adds EI (Enable Interrupts) and DI (Disable Interrupts)
- Important Game Boy nuance:
- EI does not immediately enable IME behavior in the same instant (there’s a latency/extra cycle, and IME isn’t set right away like DI).
3) Bus/IO missing pieces (needed to run test programs)
When testing begins, the emulator encounters unimplemented bus reads, such as:
- A stubbed “not implemented” read at an address like 0xFF44 (likely an LCD-related register, e.g., LCD interrupt flag register)
To keep CPU testing moving:
- They temporarily return 0 / stub behavior
- They later mention additional bus read/write gaps, but CPU progress continues.
4) Interrupt system added (VBlank/LCD/Timer/Serial/Joypad)
A dedicated interrupt module is introduced:
New files
interrupt.hinterrupt.c
Interrupt types
Interrupts are defined via an enum, including:
- VBlank
- LCD STAT
- Timer
- Serial
- Joypad
Core logic and CPU integration
- HALT checks interrupt pending flags:
- If any interrupt is pending, it un-halts
-
The CPU context gains accessors for:
- IF (interrupt flags register)
- IE (interrupt enable register)
-
CPU interrupt handling:
- Checks IF bits & IE bits
- If an enabled interrupt is pending:
- Pushes current PC onto the stack
- Jumps PC to the interrupt vector address
- Clears the serviced interrupt bit in IF
- Clears IME/master enable after servicing
-
Interrupt vectors used (as stated):
- VBlank → 0x40
- LCD STAT → 0x48
- Timer → 0x50
- Serial → 0x58
- Joypad → 0x60
The handler processes one interrupt at a time and stops once one is handled.
5) Running the CPU in a separate thread + SDL window for controlled exit
To avoid SDL blocking development/testing (they previously had an infinite loop that couldn’t be stopped with Ctrl+C), they implement:
Minimal UI
- New files:
ui.hui.c
- The SDL window is primarily for controlled termination:
- Closing the window sets a
dieflag in the emulator context
- Closing the window sets a
Separate CPU execution
- They implement a CPU run function intended for a separate thread:
- Loops until
ctx.dieis set - Sleeps briefly (e.g., ~1000 microseconds)
- Calls UI event handling indirectly (cycle timing is mentioned as a planned follow-up)
- Loops until
Threading
- Adds pthread usage via
p_thread.h - Notes:
- POSIX-only
- TODO exists for Windows compatibility
6) Testing plan and next step
- They confirm the emulator now runs far enough to:
- Open/close the UI
- Exercise HALT/interrupt behavior
Next video goals
- Find bugs by running test ROMs covering CPU instructions
- After CPU stabilizes, begin PPU work to render graphics
Main speakers/sources
- Primary speaker: the video creator/developer presenting the “Gameboy Emulator Development” series (no named individual provided in subtitles)
- Codebase being modified (modules/files referenced):
cpubusinterruptui- threading via
pthread(throughp_thread.h)