This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
The State of GENESIS: Post-BIOS CPU Register Initialization
Summary
The emulator is fully synchronized, but the screen is still blank because the CPU goes into an error loop. Final diagnosis reveals that this is due to an incorrect initial CPU state. Our emulator does not initialize the CPU registers (especially the Flags, F register) to the specific values that the official Boot ROM would have left, causing the game's first few conditional checks to fail.
This Step implements the "Post-BIOS" state directly in the constructor.CoreRegistersin C++, ensuring that the emulator boots with a CPU state identical to that of a real Game Boy. Critical values include the active Z flag (Z=1), which is essential for the first conditional instructions in the startup code to take the correct path.
Hardware Concept: The State of the CPU Post-Boot ROM
The Game Boy's 256-byte Boot ROM not only initializes the peripherals (PPU, Timer, Joypad), but also leaves the CPU registers in a very specific state before transferring control to the cartridge code at address0x0100.
On a real Game Boy, when the console is turned on:
- The Boot ROM runs from
0x0000until0x00FF. - The Boot ROM performs hardware checksums (cartridge checksum, timer, joypad).
- Boot ROM initializes the CPU registers to specific values.
- The Boot ROM transfers control to the cartridge code in
0x0100through a jump.
The Fundamental Problem:Our emulator does not run a Boot ROM. Instead, we initialize the CPU registers to zero (or simple values). The game code, when booting into0x0100, immediately executes conditional statements likeJR Z, some_error_loopthat expect the Z flag to be in a specific state (e.g.Z=1) that the BIOS would have left. Since our registers start in a "clean" and incorrect state, the jump condition fails, and the CPU is sent to a section of code other than the one for displaying the logo. Goes into a "safe fail" loop, turns off the background (LCDC=0x80), and it stays there, waiting indefinitely.
Post-BIOS values for DMG (according to Pan Docs - "Power Up Sequence"):
AF=0x01B0(that is to say,A = 0x01andF = 0xB0).F=0xB0meansZ=1,N=0,H=1,C=1.BC=0x0013DE = 0x00D8HL=0x014DSP = 0xFFFEPC = 0x0100
The initial state ofFlag Z (Z=1)is probably the most critical, since the first instructions are usually conditional jumps based on this flag. If the Z flag is not in the correct state, the game may enter an error loop instead of executing the normal boot routine.
Implementation
The solution is to move the Post-BIOS initialization directly to the constructor.CoreRegistersin C++, eliminating the need for manual initialization in Python. This ensures that each instance ofPyRegistersis automatically created with the correct status.
Modifying the CoreRegisters Constructor
The builder ofCoreRegistersinsrc/core/cpp/Registers.cppnow initialize all registers with Post-BIOS values:
CoreRegisters::CoreRegisters() :
a(0x01),
b(0x00),
c(0x13),
d(0x00),
e(0xD8),
h(0x01),
l(0x4D),
f(0xB0), // Flags: Z=1, N=0, H=1, C=1 (0xB0 = 10110000)
pc(0x0100),
sp(0xFFFE)
{
// Post-BIOS initialization completed in the initialization list
// These values simulate the exact state that the Boot ROM leaves on the CPU
// before transferring control to the cartridge code at 0x0100
}
Simplification of _initialize_post_boot_state in viboy.py
The method_initialize_post_boot_stateinsrc/viboy.pynow it just checks that the Post-BIOS state was set correctly, removing all redundant mappings. The registers are already initialized correctly in the C++ constructor.
Design Decisions
- Initialization in the Constructor:Post-BIOS initialization is done in the C++ constructor to ensure that it is always set correctly, without relying on Python code that could be forgotten or executed in the wrong order.
- DMG values:We use Post-BIOS values for DMG (Game Boy Classic) because our C++ PPU only supports DMG for now. The record
A=0x01indicates DMG, which makes games behave like they do on a gray Game Boy. - Flag Z Critical:The Z flag is explicitly set to
1because it is essential for the first conditional checks of the game's boot code.
Affected Files
src/core/cpp/Registers.cpp- Modified the constructor to initialize registers with Post-BIOS DMG valuessrc/viboy.py- Simplified_initialize_post_boot_stateto eliminate redundant initializationtests/test_core_registers_initial_state.py- New test file to validate the initial Post-BIOS state
Tests and Verification
A new tests file was createdtests/test_core_registers_initial_state.pywith three tests that validate the initial Post-BIOS state:
- test_registers_post_bios_state:Verify that all registers are initialized with the correct Post-BIOS values.
- test_registers_post_bios_state_consistency:Verifies that the values of the individual registers are consistent with the 16-bit pairs.
- test_registers_flag_z_critical:Specifically check that the Z flag is set, as it is critical for the first few conditional checks.
Test results:
$ pytest tests/test_core_registers_initial_state.py -v
============================= test session starts =============================
platform win32 -- Python 3.13.5, pytest-9.0.2, pluggy-1.6.0
collecting ... collected 3 items
tests/test_core_registers_initial_state.py::test_registers_post_bios_state PASSED [ 33%]
tests/test_core_registers_initial_state.py::test_registers_post_bios_state_consistency PASSED [ 66%]
tests/test_core_registers_initial_state.py::test_registers_flag_z_critical PASSED [100%]
============================== 3 passed in 0.06s ==============================
Compiled C++ module validation:The tests directly validate the compiled C++ module (viboy_core), verifying that the constructor ofCoreRegisterscorrectly initializes registers with Post-BIOS values.
Key test code:
def test_registers_post_bios_state():
"""Verify that the CPU registers are initialized with their Post-BIOS values for DMG."""
regs = PyRegisters()
# Check individual 8-bit registers
assert regs.a == 0x01
assert regs.f == 0xB0
# Check 16-bit pairs
assert regs.af == 0x01B0
assert regs.bc == 0x0013
assert regs.de == 0x00D8
assert regs.hl == 0x014D
# Check 16-bit registers
assert regs.sp == 0xFFFE
assert regs.pc == 0x0100
# Check individual flags
assert regs.flag_z is True
assert regs.flag_n is False
assert regs.flag_h is True
assert regs.flag_c is True
Sources consulted
- Bread Docs:Power Up Sequence- Post-BIOS values of CPU registers
- Bread Docs:CPU Registers and Flags- Structure of registers and flags
Educational Integrity
What I Understand Now
- Post-BIOS Status:Boot ROM leaves the CPU registers in a very specific state before transferring control to the cartridge code. This state is not arbitrary; games depend on it for their first conditional checks.
- Flag Z Critical:The Z flag is especially important because many of the first instructions in the startup code are conditional jumps based on this flag. If the Z flag is not in the correct state, the game may enter an error loop.
- Initialization in the Constructor:Moving Post-BIOS initialization to the C++ constructor ensures that it is always set correctly, without relying on Python code that could be forgotten or executed in the wrong order.
What remains to be confirmed
- Real Emulator Behavior:We need to run the emulator with a real ROM (ex: Tetris) to verify that the correct Post-BIOS state allows the game to run the normal boot routine instead of entering an error loop.
- Post-BIOS values for CGB:Post-BIOS settings for Game Boy Color (CGB) are different. When we implement full support for CGB, we will need to initialize the registers with different values (ex:
A=0x11for CGB).
Hypotheses and Assumptions
Main Hypothesis:With the correct Post-BIOS state, the emulator should be able to execute the game's boot code correctly, passing all conditional checks and finally reaching the routine that copies the logo graphics to VRAM. This is the final piece of the puzzle that should solve the persistent white screen issue.
Next Steps
- [ ] Run the emulator with a real ROM (ex: Tetris) to verify that the correct Post-BIOS state allows the game to run the normal boot routine
- [ ] Verify that the Nintendo logo appears on the screen (if the Post-BIOS status is correct, the game should copy the graphics to the VRAM and activate bit 0 of the LCDC)
- [ ] If the logo appears, celebrate success and document the result in the next Step
- [ ] If the screen is still blank, investigate other possible problems (e.g. graphics copy routine, LCDC activation, etc.)