Video summary

Gameboy Emulator Development - Part 04

Main summary

Key takeaways

Technology

Technological focus: Game Boy CPU emulator — instruction fetch/addressing + LD (load) implementation

  • The presenter continues from prior parts where some CPU instruction execution and instruction data fetching were already implemented.
  • This part concentrates on:
    1. Fetching the operand/data for all addressing modes (“fetch data function” expansion).
    2. Implementing all load instructions (the Game Boy LD / ld instruction family) and wiring them into the instruction dispatch array.

1) Operand/addressing-mode fetching (cpu fetch.c)

A new file, cpu_fetch.c, is created and the existing CPU context/code is moved/copied into it.

Key additions to operand fetching logic

Handling various operand forms, including:

  • Register + immediate 16-bit addressing: rd16

    • Reads immediate bytes as a 16-bit value from the instruction stream (via program-counter patterns).
  • Register-indirect forms

    • rr: fetch operand from a second register.
    • mmrr / memory destinations: loading a register value into memory at an address derived from registers.
      • The fetched data stores whether the destination is memory (dest_is_mem = true) so the later LD execute step knows to write via the bus.
  • Special LDH handling involving high memory offsets (FF00 area)

    • There’s a special-case check for one register type (subtitle says “rtc” but context implies it’s the C register special LDH behavior).
    • Notes about masking/OR-ing with 0xFF00 to form a 16-bit address from an 8-bit value.
    • Uses bus reads and increments CPU cycles when a bus access occurs.
  • HL auto-increment/decrement modes

    • HLI: read from memory at HL, then increment HL, and update bus cycles appropriately.
    • HLD: read from memory at HL, then decrement HL.
  • A8 immediate / absolute 16-bit related addressing

    • Fetch operand via program counter and increment PC.
    • Covers both directions:
      • moving into address/register based on an 8-bit immediate (offset forms)
      • moving to/from A16 absolute address forms
  • Special stack-relative instruction

    • Implements operand fetch for LDHL SP+r8 (load HL = SP + signed 8-bit immediate).
    • Reads an 8-bit immediate from PC, advances PC, updates cycles.
  • General immediate byte/word fetching

    • d8: fetch one byte from PC
    • d16: fetch a 16-bit immediate from PC (low then high bytes combined)

2) Execute-time LD implementation (writing to registers/memory + flags)

A function to perform LD is added/implemented, along with utility helpers.

New helper: cpu_set_reg

Implements register assignment for different register sizes:

  • 8-bit registers: writes value with & 0xFF
  • 16-bit registers: preserves full word behavior and uses appropriate splitting/reversal logic

bus_read_16 and bus_write_16

In bus.c, the presenter adds:

  • bus_read_16(addr)

    • Reads low byte at addr, then high byte at addr + 1, combines into a u16.
  • bus_write_16(addr, value)

    • Writes high byte and low byte to addr and addr + 1 in the correct order (as implemented).

Main LD behavior

In the LD execution logic:

  • Default case

    • Writes fetched operand into a destination register.
  • Special case: destination is memory (dest_is_mem)

    • Performs a 16-bit bus write when the destination type indicates a 16-bit register/address scenario.
    • Uses the fetched value and the computed memory destination via bus.
  • Special case: LDHL SP+r8 flag updates

    • Computes flags H and C using low-nibble and unsigned carry rules (bitmask checks on SP and the immediate).
    • Updates CPU flags (H, C) and writes registers accordingly:
      • HL = SP + signed(r8) (with explicit casting for signed behavior)
  • Ensures cycle increments occur when memory/bus operations happen.


3) Instruction dispatch table: adding all LD variants

After implementing addressing modes and LD operand fetching/execution, the presenter updates the instruction array to include LD opcodes.

  • Uses the Game Boy opcode table approach:

    • Finds all instructions matching ld (searching “ld” in an instruction set page).
    • Populates opcode entries with:
      • opcode numeric mapping
      • addressing mode identifiers (e.g., rd16, rr, mmrr, amr, etc.)
      • register type fields such as bc, de, hl, a, sp, etc.
  • Notes:

    • Many LD opcodes follow systematic patterns (rows/columns); the presenter fills them in programmatically by copying patterns and adjusting register types.
    • Includes sections like:
      • 0x40–0x7F style register-to-register loads (patterns like b,c,d,e,h,l,m,a, where m refers to memory at HL)
      • 0xE0–0xF style special LDH / absolute address variants (e.g., LDH, LD (a16),A, LD A,(a16))

4) Testing progress / stopping point

  • The presenter runs the emulator and adds temporary logging for bus operations.
  • Observes that execution reaches an “unsupported bus right” beyond the supported range, confirming:

    • the LD/addressing-mode work is being exercised
    • the emulator correctly fails/halts when encountering bus operations not yet implemented for those ranges
  • Concludes:

    • addressing modes and the majority of LD instruction handling are integrated
    • remaining unsupported/unknown instructions exist (e.g., around e0 / next unknown opcode region)

Declares a “good stopping point” after wiring the load instructions and dispatch table.


Main speakers/sources

  • Main speaker: The YouTube presenter (“low-level devil” host) developing a Game Boy emulator in a series titled “Gameboy Emulator Development - Part 04”.

Original video