⚠️ Clean-Room / Educational

This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.

Viboy Color Project Log

← Return to index

Step 0427: Tests Align Post-Boot Registers + EI Delay

📋 Executive Summary

Alignment of tests with the real behavior of the core: Post-Boot State (DMG) and EI delayed IME. Correction of 4 tests (3 Registers + 1 CPU Control) that assumed zero-init or immediate activation of IME, when the core correctly implements the hardware-accurate behavior according to Pan Docs.Without touching the core, only updating tests to reflect the project's Post-Boot policy.

Result:
  • ✅ 4 tests fixed (test_program_counter, test_stack_pointer, test_initializacion_por_defecto, test_di_ei_sequence)
  • ✅ 267 tests passing (vs 263 before)
  • ✅ 10 remaining bugs (6 PPU + 4 pre-existing unrelated ones)
  • ✅ Clean base for Step 0428 (fix PPU framebuffer swap)

🔧 Hardware Concept

1. Post-Boot State (Pan Docs - Power Up Sequence)

When the Game Boy is turned on, the Boot ROM runs an initialization sequence and leaves the records in aspecific statebefore jumping to the cartridge code (0x0100). This project implementsDefault Post-Boot State(skip-boot), simulating the state that the official Boot ROM leaves on the CPU.

Post-Boot Values ​​(DMG mode):
A = 0x01 (identifies hardware as DMG)
F = 0xB0 (flags: Z=1, N=0, H=1, C=1)
B = 0x00
C = 0x13
D = 0x00
E = 0xD8
H = 0x01
L = 0x4D
PC = 0x0100 (cartridge code start)
SP = 0xFFFE

16-bit pairs:
AF=0x01B0
BC=0x0013
DE = 0x00D8
HL=0x014D

Fountain: src/core/cpp/Registers.cpp:24-47- BuilderCoreRegisters::CoreRegisters()with detailed comments on Post-BIOS status.

2. EI Delay (Pan Docs - CPU Instruction Set)

The instructionEI (Enable Interrupts)has critical behavior:1 instruction delay. This means that IME is not activated immediately, butafter executing the following statement.

Implementation in the core:
def _op_ei(self) -> int:
    """EI (Enable Interrupts) - Opcode 0xFB"""
    # DO NOT activate IME immediately, program it
    self.ime_scheduled = True
    return 1

# In step() (at the beginning, before executing the statement):
if self.ime_scheduled:
    self.ime = True
    self.ime_scheduled = False

Hardware-accurate behavior: The statement following EI is executed with IME still False, and then IME is automatically activated. This is critical for patterns likeEI + RETIused in interrupt handlers.

Fountain: src/cpu/core.py:2382-2408(function_op_ei) andsrc/cpu/core.py:585-588(activation instep()).

⚙️ Technical Implementation

Task T1: Evidence of Actual Defaults (Read Only)

Commands executed:

cd /media/fabini/8CD1-4C30/ViboyColor

# Find Registers defaults
grep -n "CoreRegisters::CoreRegisters\|apply_post_boot_state\|pc(\|sp(\|a(\|f(" \
  src/core/cpp/Registers.cpp src/core/cpp/Registers.hpp
nl -ba src/core/cpp/Registers.cpp | sed -n '1,180p'

# Search EI behavior
grep -n "\bEI\b\|ime_scheduled\|IME" src/cpu/core.py | head -n 30
nl -ba src/cpu/core.py | sed -n '2360,2460p'

Captured evidence:

  • Registers: Constructor initializes with Post-Boot State (PC=0x0100, SP=0xFFFE, A=0x01, F=0xB0, etc.)
  • EI: Setsime_scheduled = True, IME is activated at the start of the nextstep()

Task T2: Fix Tests Registers Post-Boot

Modified file: tests/test_core_registers.py

Changes applied:

  1. test_program_counter: Updatedassert reg.pc == 0x0100(was== 0)
  2. test_stack_pointer: Updatedassert reg.sp == 0xFFFE(was== 0)
  3. test_initialization_by_default: Completely rewritten to validate Post-Boot State:
    • A=0x01, F=0xB0, B=0x00, C=0x13, D=0x00, E=0xD8, H=0x01, L=0x4D
    • PC=0x0100, SP=0xFFFE
    • Pairs: AF=0x01B0, BC=0x0013, DE=0x00D8, HL=0x014D
    • Flags: Z=True, N=False, H=True, C=True

Comment added in tests:

# This project uses post-boot defaults (skip boot ROM)
# PC starts at 0x0100 according to Pan Docs - Power Up Sequence

Task T3: Fix Test EI Delay (CPU Control)

Modified file: tests/test_cpu_control.py

Changes applied to test_di_ei_sequence:

  1. After runningcpu._op_ei():
    • Validateassert cpu.ime is False(IME does not activate immediately)
    • Validateassert cpu.ime_scheduled is True(it is programmed for the next step)
  2. Execute an instruction (NOP) withcpu.step()
  3. Validateassert cpu.ime is True(IME activated after instruction)
  4. Validateassert cpu.ime_scheduled is False(scheduling flag cleared)

Comment added to the test:

"""
CRITICAL BEHAVIOR: EI has a delay of 1 instruction.
After executing EI, IME is not activated immediately, but is programmed
(ime_scheduled=True) and is activated at the start of the next step().
"""

Design Decision: Post-Boot Defaults

This project adoptsPost-Boot State as official policy:

  • Advantage: Simplifies development (does not require implementing full Boot ROM)
  • Advantage: Behavior consistent with most modern emulators
  • Advantage: Commercial games assume this initial state (they do not depend on Boot ROM)
  • ⚠️ Limitation: Cannot emulate the official boot sequence (Nintendo logo, scroll, etc.)

Future: If optional Boot ROM support is implemented (Step 0401 mentions this), the PC will be set to 0x0000 from the frontend before starting emulation.

✅ Tests and Verification

Compilation and Build

python3 setup.py build_ext --inplace > /tmp/viboy_0427_build.log 2>&1
echo BUILD_EXIT=$?
#BUILD_EXIT=0

python3 test_build.py > /tmp/viboy_0427_test_build.log 2>&1
echo TEST_BUILD_EXIT=$?
#TEST_BUILD_EXIT=0

Fixed Specific Tests

pytest -q tests/test_core_registers.py
#14 passed in 0.41s
# REGS_EXIT=0

pytest -q tests/test_cpu_control.py
#13 passed in 0.37s
# CPUCTRL_EXIT=0

# Individual verification of the 4 fixed tests:
pytest -vv \
  tests/test_core_registers.py::TestPyRegistersPCSP::test_program_counter \
  tests/test_core_registers.py::TestPyRegistersPCSP::test_stack_pointer \
  tests/test_core_registers.py::TestPyRegistersInitialization::test_initialization_by_default \
  tests/test_cpu_control.py::TestCPUControl::test_di_ei_sequence
#4 passed in 0.15s ✅

Complete Suite

pytest -q > /tmp/viboy_0427_all.log 2>&1
echo PYTEST_EXIT=$?
# PYTEST_EXIT=1 (expected, PPU failures remaining)

# Summary:
#10 failed, 267 passed in 0.54s

Analysis of Remaining Failures

10 total failures:

  1. 6 PPU faults(Cluster A of Step 0426):
    • test_bg_rendering_simple_tile
    • test_signed_addressing_fix
    • test_sprite_rendering_simple
    • test_sprite_transparency
    • test_sprite_x_flip
    • test_sprite_palette_selection
  2. 4 pre-existing faults(unrelated, not included in triage Step 0426):
    • test_unimplemented_opcode_raises
    • test_ldh_write_boundary
    • test_ld_c_a_write_stat
    • test_ld_a_c_read

Success criterion met:The 4 specific tests for Cluster B (Registers) and Cluster C (CPU Control) from Step 0426 now pass correctly. The 6 PPU faults (Cluster A) remain for Step 0428.

C++ Compiled Module Validation

✅ The teststest_core_registers.pydirectly validate the compiled C++ module (viboy_core.PyRegisters), confirming that the Cython wrapper correctly exposes the Post-Boot State of the native core.

📁 Affected Files

  • tests/test_core_registers.py- 3 updated tests (PC, SP, initialization)
  • tests/test_cpu_control.py- 1 updated test (test_di_ei_sequence)
  • docs/bitacora/entries/2026-01-02__0427__tests-align-postboot-registers-and-ei-delay.html- New entry
  • docs/bitacora/index.html- Updated with new entry
  • docs/report_phase_2/part_01_steps_0412_0450.md- Updated with Step 0427

🔜 Next Steps

Step 0428: Fix PPU Framebuffer Swap (Cluster A)

  • Investigate the swap bug inrenderer.py
  • Verify that the back buffer has correct pixels
  • Correct the back → front copy at the end of the frame (LY=144)
  • Validate the 6 PPU tests (BG rendering + sprites)
  • Goal: 0 bugs in the entire suite (except the 4 pre-existing unrelated ones)

💡 Lessons Learned

  1. Tests must reflect the real behavior of the core: If the core implements Post-Boot State (hardware-accurate), the tests must validate that state, not assume zero-init.
  2. Document design policies: The decision to use Post-Boot State by default must be explicit in tests and documentation.
  3. Delay is critical: The original test assumed immediate activation of IME, but the real hardware has a delay of 1 instruction. Validateime_scheduledIt is essential.
  4. Evidence before changes: Reading the core code (read only) before modifying tests ensures that the changes are correct and not invented.
  5. Prior triage is valuable: Step 0426 correctly identified the 3 clusters, allowing an atomic and orderly fix (foundation-first).

📚 References

  • Bread Docs- Power Up Sequence (Post-Boot State DMG/CGB)
  • Bread Docs- CPU Instruction Set (EI behavior, delay of 1 instruction)
  • src/core/cpp/Registers.cpp:24-47- Builder with Post-Boot State
  • src/cpu/core.py:2382-2408- Implementation of EI with delay
  • src/cpu/core.py:585-588- IME activation in step()
  • Step 0426- Triage and clustering of the 10 failures