Video summary

Gameboy Emulator Development - Part 10

Main summary

Key takeaways

Technology

What this video focuses on (Game Boy emulator, Part 10)

  • After previously finishing CPU instruction decoding/execution, the author now:
    • fixes bugs found along the way, and
    • adds infrastructure to validate CPU correctness using blarg Game Boy CPU test ROMs.
  • It implements a timer so that interrupt-related tests can pass, and adds serial-output debugging to read test output.

Key fixes and CPU logging improvements

1) Missing/incorrect instruction implementations in instructions.c

Adds/ensures instructions exist for:

  • DAA opcode 0x27
  • CPL opcode 0x37
  • SCF opcode 0x3F
  • CCF opcode 0x3F

    Note: subtitles suggest possible confusion, but the intent is “these missing opcodes”.

Also fixes addressing mode mappings and register type selection, including cases involving am (rd8) and specifying register type a for certain immediate-style encodings.

2) Better instruction disassembly/logging for debug output

Instead of logging only an instruction name, it introduces a function to convert an instruction + context into a more readable “disassembled-ish” string (not fully accurate disassembly).

  • Uses register-type lookup (none, A, B/C/D/E/H/L, and implied forms) to build the log line.
  • Improves understanding of what the emulator executed when comparing behavior against tests.

3) One CPU flag bug discovered during opcode tests

During the opcode handler for ADD SP, r8, the half-carry/carry condition check had a comparison error:

  • Needed to use >= instead of > when checking the carry behavior.

This fix was required for the related opcode test (E8) to pass.


Serial debugging for blarg tests (I/O + debugger)

New serial-capable I/O implementation

  • Adds io.h / io.c for memory-mapped I/O handling.
  • Implements serial port reads/writes at:
    • FF01 (serial data)
    • FF02 (serial control)

Based on serial control bits (“pan docs”):

  • If FF01 is read: return the buffered serial byte.
  • If FF02 is read: indicate readiness/data-available and whether transfer state is set.
  • Writes buffer into FF01’s backing storage.

Debug message capture (dbg.h / dbg.c)

  • Maintains a 1KB character buffer for the currently received debug message from the serial output.
  • On each serial-ready read:

    • appends the incoming character to the buffer
    • clears the “ready” bit by writing 0 back to FF0?

      Subtitles mention FF0 in one spot; intent is clearing the serial control flag so the same character isn’t read repeatedly.

  • Prints the accumulated debug message once it’s non-empty.

Result

CPU test ROMs output human-readable status like “zero one … passed” through the serial console.


Timer implementation (required for interrupt tests)

Implements timer support so interrupt timing tests can work.

Timer context (timer.h / timer.c)

Timer registers modeled as fields:

  • div
  • tima
  • tma
  • tac

Timer initialization sets:

  • div default to 0xAC00

Timer ticking logic (timer_tick)

Each CPU cycle should advance the timer:

  • “For every cpu cycle tick the timer four times” (described via updating a tick counter / integrating an “emu cycles” model)

Increment/update logic:

  • Uses tac to select the divider rate.
  • Detects timer update events by checking whether a specific bit in div changes across increments (subtitles describe correlating bit transitions with TAC settings).
  • If tima overflows past 0xFF:
    • reloads from tma
    • requests a timer interrupt (via interrupt request logic)

Timer reads/writes via bus/I-O

  • FF04 (DIV):
    • writing resets div to 0
  • FF05 (TIMA): writing sets tima
  • FF06 (TMA): writing sets tma
  • FF07 (TAC): writing sets tac

Timer reads:

  • FF04 returns the upper byte of div (div >> 8)
  • FF05/FF06/FF07 return tima/tma/tac

Interrupt flag handling integration

Adds logic in cpu.c to request an interrupt by OR-ing the corresponding interrupt request bit into the pending interrupt flags register.

Outcome

After timer + interrupt integration, blarg interrupt test (test 02) changes from failing to passing.


Running blarg CPU test suite and results

  • The author runs blarg tests and checks pass/fail:
    • Test 01: passed
    • Test 02 (interrupts): failed initially (missing timer), later passed after timer implementation
    • Additional tests run sequentially:
      • opcode
      • immediate instructions
      • jumps/calls/returns
      • bit operations (CB)
      • HL-related tests
  • Eventually:
    • All blarg CPU tests reported “passed” (tests 1 through 11)

Note on tooling: debug output is extremely large; the log file can grow very quickly (subtitles mention gigabytes when debug logging is enabled and the run is long).


Main technical progression in this part

  1. Fix instruction/addressing/register-type bugs.
  2. Improve logging by producing a “better disassembled” instruction string.
  3. Add serial I/O and a small debugger to capture blarg test output.
  4. Implement the timer + timer memory-mapped registers + interrupt triggering.
  5. Re-run blarg tests and fix remaining flag logic bug (ADD SP, r8 carry condition).

Main speakers / sources

  • Speaker/author: The developer presenting the “low level dev / game boy emulator development series” (referred to as “low level devil channels” in subtitles).
  • Reference/source mentioned: pan docs (used for serial register bit meanings and expected I/O behavior).
  • Test source: blarg Game Boy CPU test ROMs (serial-output-based test suite).

Original video