This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
MMU and Numpy API Compatibility Fix
Summary
When running the emulator with the migrated C++ core, two critical errors appeared: (1) `ModuleNotFoundError: No module named 'numpy'` when trying to use the Zero-Copy framebuffer, and (2) `AttributeError: 'viboy_core.PyMMU' object has no attribute 'read_byte'` because the wrapper Cython exposes `read()`/`write()` methods but Python code expects `read_byte()`/`write_byte()`. Compatibility methods (aliases) were implemented in the PyMMU wrapper and the compatibility was verified. installation of numpy to maintain backward compatibility with existing Python code.
Hardware Concept
This fix is not directly related to the Game Boy hardware, but rather to thePython-C++ hybrid architectureof the project. During kernel migration to C++, a Cython wrapper (`PyMMU`) was created that exposes the C++ class `MMU` to Python.
The problem arises when existing Python code (such as `renderer.py`) was written to work with the original Python MMU, which had `read_byte()`, `write_byte()`, `read_word()` methods and `write_word()`. The new Cython wrapper exposed simpler methods (`read()` and `write()`), breaking compatibility with legacy code.
Backwards compatibility in hybrid architectures:When code is migrated from a language to another, it is crucial to keep the public API compatible to avoid having to rewrite the entire code that depends on it. Alias methods are a common technique for maintaining compatibility while refactoring internally.
Implementation
Four support methods were added to the `PyMMU` wrapper in `src/core/cython/mmu.pyx`:
Added Compatibility Methods
read_byte(addr):Aliases ofread(addr)for compatibility with older Python code.write_byte(addr, value):Aliases ofwrite(addr, value).read_word(addr):Reads 16 bits using Little-Endian (LSB in addr, MSB in addr+1).write_word(addr, value):Write 16 bits using Little-Endian.
read_word/write_word implementation
The `read_word()` and `write_word()` methods correctly implement Little-Endian ordering from the Game Boy:
def read_word(self, uint16_t addr):
# Read LSB (least significant) into addr
cdef uint8_t lsb = self.read(addr)
# Read MSB (most significant) into addr+1 (with wrap-around)
cdef uint8_t msb = self.read((addr + 1) & 0xFFFF)
# Combine in Little-Endian: (MSB<< 8) | LSB
return ((msb << 8) | lsb) & 0xFFFF
def write_word(self, uint16_t addr, uint16_t value):
# Extraer LSB y MSB
cdef uint8_t lsb = value & 0xFF
cdef uint8_t msb = (value >> 8) & 0xFF
# Write in Little-Endian order
self.write(addr, lsb)
self.write((addr + 1) & 0xFFFF, msb)
Numpy dependency
Numpy was already in `requirements.txt` but it was not installed in the virtual environment. Its installation has been verified and it is confirmed to be available. Numpy is required for the Zero-Copy framebuffer that allows transferring the pixel buffer from C++ to Pygame no memory copies.
Design Decisions
- Alias instead of renaming:We chose to keep both method names (`read()` and `read_byte()`) so as not to break existing code or new code that uses the simplified API.
- Implementation in Cython:The `read_word()` and `write_word()` methods were implemented in Cython using the base `read()` and `write()` methods, avoiding duplicating logic in C++.
- Static typing:C types (`cdef uint8_t`, `cdef uint16_t`) were used to maintain the overhead-free performance of Python.
Affected Files
src/core/cython/mmu.pyx- Added compatibility methods (read_byte, write_byte, read_word, write_word)requirements.txt- Verified that numpy>=1.24.0 is present
Tests and Verification
The verification was carried out by:
- Successful build:The Cython module was recompiled without errors using
python setup.py build_ext --inplace. - Syntax validation:No linter errors were found in the modified file.
- Installed Numpy:Confirmed that numpy is available in the virtual environment.
Next check:Run the emulator with a ROM to confirm that the renderer can access the MMU without missing attribute errors.
Sources consulted
- Cython Documentation:https://cython.readthedocs.io/
- Pan Docs: Little-Endian byte order (Memory Map section)
Note: This fix is mainly about software architecture and Python-C++ interoperability, not about Game Boy hardware.
Educational Integrity
What I Understand Now
- Backward compatibility in wrappers:When creating a C++ to Python wrapper, It is crucial to keep the API compatible with existing code. Alias methods are a elegant solution that does not add significant overhead.
- Little-Endian on Game Boy:The implementation of `read_word()` and `write_word()` confirms that the Game Boy uses Little-Endian, where the least significant byte is stored in the lowest direction.
- Zero-Copy with Numpy:Numpy is essential for transferring memory buffers between C++ and Python without copies, using memoryviews that point directly to native memory.
What remains to be confirmed
- Alias Performance:Although alias methods are simple wrappers, It would be useful to measure if there is any measurable overhead compared to calling `read()`/`write()` directly.
- Usage in code:Verify that all places that use `read_byte()`/`write_byte()` They now work correctly with the C++ wrapper.
Hypotheses and Assumptions
It is assumed that existing Python code does not use advanced features of the Python MMU that are not available in the C++ version. If more incompatibilities appear, they will be added additional compatibility methods as needed.
Next Steps
- [ ] Run the emulator with a ROM and verify that the renderer works without errors
- [ ] Verify that the Zero-Copy framebuffer works correctly with numpy
- [ ] If more API incompatibilities appear, add additional support methods
- [ ] Consider creating a specific test suite to validate the compatibility of the PyMMU wrapper