Video summary

Gameboy Emulator Development - Part 06

Main summary

Key takeaways

Technology

Main focus of the video (Game Boy emulator dev – Part 06)

This part implements control-flow instructions related to jumping and subroutines, centered around adding a correct CPU stack mechanism first. The emulator needs this to support instructions that push/pop the program counter (PC) for CALL, RET, interrupts, and RST.


1) Stack implementation (core dependency)

The tutorial adds a new stack module:

  • New files: stack.c, stack.h
  • Stack operations implemented:
    • stack_push(8-bit)
    • stack_push(16-bit) (pushes high byte then low byte)
    • stack_pop(8-bit)
    • stack_pop(16-bit) (pops high then low, reconstructs high<<8 | low)

How stack uses emulator state

  • Stack pointer (SP) is obtained via a new helper:
    • cpu_get_regs returning the CPU register context (ctx->regs)
  • Memory access: uses bus reads/writes (bus.h)
  • Push behavior:
    • SP--
    • write byte to memory at new SP
  • Pop behavior:
    • read byte at SP
    • then SP++

Conceptual example explained

  • Stack starts at the bottom of working RAM (using dffe-style addresses).
  • Pushing values decrements SP and writes values into RAM.
  • Popping increments SP and effectively invalidates the popped location (though the memory contents remain).

2) CPU-level PUSH/POP instructions for registers (16-bit)

In cpu_proc.c, the tutorial implements 16-bit register operations:

  • proc_pop and proc_push (only for 16-bit pairs)
  • Special register-pair behavior for AF:
    • When popping into F/AF, only lower bits (nibble/three-nibble behavior) are kept (masking via a “bottom three nibbles” comment)
  • Cycle accuracy:
    • cycles increment for each byte transferred
    • stack byte operations are called accordingly

Instruction table updates

Adds register-pair variants into the opcode table:

  • POP for pairs like BC, DE, HL, AF
  • PUSH for pairs like BC, DE, HL, AF

3) Generalized jump helper to handle PC pushing

A reusable helper is introduced:

  • go_to_adder(ctx, address, push_pc_flag)
    • performs conditional jump logic
    • if push_pc_flag is set, it pushes the current PC onto the stack (16-bit)
    • adds cycle cost: 2 cycles (one per pushed PC byte)

4) Implementing jump/call/return instructions

Multiple control-flow operations are updated/implemented using the helper:

Jump: JP a16

  • Updated to call go_to_adder(..., false) (does not push PC)

Call: CALL a16

  • Uses the same helper with push_pc_flag = true (pushes PC)

Relative jump: JR r8

  • Implements signed relative offset:
    • casts the fetched relative value to char so it can be negative
  • target computation:
    • regs.pc + rel
  • jumps via go_to_adder(..., false)

Return: RET / RETI

  • RET
    • checks the condition (if not ct none)
    • if the condition passes:
      • pops the return address from the stack
      • sets pc
    • cycle accuracy matters:
      • explicitly mentions popping each byte individually to match timing
      • improper cycle behavior can break some games
  • RETI
    • calls RET, then re-enables interrupts
      • sets master_enable = true
    • uses a reti = red/rat pattern (based on how it routes to RET logic)

5) Instruction table completion for control-flow opcodes

The tutorial adds conditional variants into the opcode instruction array, including:

  • CALL conditionals:
    • e.g., NZ, NC, Z, C, plus unconditional
  • JR conditionals:
    • e.g., NZ, NC, Z, C
  • JP conditionals plus special JP (HL) case:
    • mentions opcode where the target comes from an address in HL
    • aligned with Game Boy’s JP (HL) behavior
  • RET conditionals plus RET and RETI variants

6) Interrupt-like restart: RST

Implements RST instructions:

  • RST behaves like a forced jump to a fixed vector:
    • vectors like 0x00, 0x08, 0x10, … 0x38
  • implemented similarly to CALL:
    • pushes PC then jumps to the fixed target
  • opcodes added:
    • C7–F7, CF–FF (full set for RST)

7) Validation via ROM testing (behavior checks)

The author tests with known ROMs and observes expected control-flow halts:

  • One ROM stops at CB → implies missing INC/DEC (specifically mentions HL increment/decrement weren’t implemented yet)
  • Tetris ROM stops on decrement path → points to missing decrement implementation
  • DMG Acid ROM stops at CB → indicates additional missing instruction(s) (mentions CB behavior and the need to implement missing instruction(s))

They conclude Part 06 reaches a good stopping point due to the complexity of jump/push/pop work.


Main speakers / sources

  • Primary speaker: “low level devil” (YouTube channel host, presenting Gameboy Emulator Development - Part 06)
  • Technical source referenced implicitly:
    • emulator codebase modules: cpu.h, cpu_proc.c, stack.c/.h, bus.h
    • Game Boy CPU behavior specs for RET/RETI/CALL/JR/RST semantics

Original video