This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
MMU-PPU Connection Verification and Debug Cleaning
Summary
It was verified and confirmed that theconnection between MMU and PPUfor reading the registerL.Y.(0xFF44) is correctly implemented. The existing code already handled correctly
reading LY from the PPU when the game accesses address 0xFF44. Prints were removed
temporary debug files that had been added in the previous step (diagnostic probe) to clean
code and improve performance. The testtest_ly_read_from_mmuconfirms that the
functionality is operational.
Hardware Concept
The recordL.Y.(Current line) in the direction0xFF44it's a record
ofread onlyindicating which scan line is currently being drawn
(range 0-153). The games constantly read this log to sync and know when
can safely refresh VRAM (during V-Blank, when LY >= 144).
In real hardware, the LY register is directly connected to the PPU (Pixel Processing Unit), not to RAM memory. When the software reads 0xFF44, the hardware returns the current value of LY from the PPU, not from a memory cell. If the game reads an incorrect value (for example, 0 constant), you can stay in an infinite loop waiting for LY to change.
Fountain:Pan Docs - LCD Timing, LY Register (0xFF44)
Implementation
It was verified that the existing implementation insrc/memory/mmu.pynow drives correctly
LY reading:
# In read_byte(), lines 232-237
if addr == IO_LY:
if self._ppu is not None:
return self._ppu.get_ly() & 0xFF
else:
return 0
The PPU-MMU connection is successfully established onsrc/viboy.pythrough the methodset_ppu()after creating both instances (avoiding circular dependencies).
Debug code cleanup
The temporary debug prints that had been added in the previous step were removed:
- Removed:Print "DEBUG PROBE" every 1000 iterations (lines 313-325)
- Removed:Safety limit with emergency prints (lines 327-332)
- Removed:Print of "V-BLANK DETECTED" (lines 348-354)
- Kept:Heartbeat with
logger.info()every 60 frames (line 364)
These prints were slowing down execution and are no longer necessary now that it has been confirmed that the MMU-PPU connection is working correctly.
Affected Files
src/viboy.py- Removed temporary debug prints from the main loop
Tests and Verification
The existing test that verifies the LY reading from the MMU was executed:
- Command executed:
pytest tests/test_ppu_timing.py::TestPPUTiming::test_ly_read_from_mmu -v - Around:Windows, Python 3.13.5
- Result:
PASSED(1 passed in 0.25s) - What is valid:
- The MMU can read LY from the PPU through register 0xFF44
- The value returned by MMU matches the internal value of the PPU
- The value changes correctly when the PPU advances lines
Test code:
def test_ly_read_from_mmu(self) -> None:
"""Test: The MMU can read LY from the PPU through register 0xFF44."""
mmu = MMU(None)
ppu = PPU(mmu)
mmu.set_ppu(ppu)
# Initially LY must be 0
assert mmu.read_byte(0xFF44) == 0
# Advance some lines
ppu.step(456 * 5) # 5 lines
#LY must be 5
assert ppu.get_ly() == 5
# The MMU must return the same value
assert mmu.read_byte(0xFF44) == 5
Why this test demonstrates something about the hardware:This test verifies that when the software (game) reads address 0xFF44, gets current LY value from PPU, not from the RAM memory. This is critical because games depend on this value to sync and know when they can safely upgrade VRAM. If the MMU returned an incorrect value (e.g. always 0), the game would stay in an infinite loop waiting for LY to change.
Sources consulted
- Bread Docs:LCD Timing, LY Register (0xFF44)
Educational Integrity
What I Understand Now
- LY register (0xFF44):It is a read-only register connected directly to the PPU. When the software reads this address, the hardware returns the current value of LY from the PPU, not from RAM.
- MMU-PPU connection:The MMU needs a reference to the PPU in order to return the correct value of LY when 0xFF44 is read. This connection is established after creating both instances to avoid circular dependencies.
- Critical importance:If the game reads an incorrect value of LY (for example, always 0), it may sit in an infinite loop waiting for LY to change, causing the "eternal white screen" or "emulator frozen" symptom.
What remains to be confirmed
- Behavior on real hardware:According to the documentation, LY is read-only and writing to it has no effect. This is already implemented correctly in the code (it is silently ignored).
- Exact timing:The PPU advances LY every 456 T-Cycles. This is implemented and verified by testing, but may need fine-tuning if synchronization issues are detected in real games.
Hypotheses and Assumptions
Main hypothesis:The "eternal white screen" issue observed in the previous step (0040) NOT due to an MMU-PPU connection problem, since the code is correctly implemented and the tests pass. The problem is probably caused by another factor, such as:
- The game is waiting for a V-Blank interrupt that is not processing correctly
- The game is waiting for a specific time to pass (counting cycles) before continuing
- There is some other I/O register that is not implemented correctly
This hypothesis will be validated in later steps when the emulator is tested with real ROMs and observe the detailed behavior.
Next Steps
- [ ] If the "white screen" issue persists, investigate V-Blank interrupt handling
- [ ] Verify that interrupts are processed correctly when IME is enabled
- [ ] Test the emulator with test ROMs to validate the complete behavior
- [ ] Continue to implement missing features as needed