This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Cython Compilation Bug Fixes: setPPU and run_scanline
Summary
After implementing the methodrun_scanline()in C++ and its wrapper in Cython, the build failed with multiple errors related to missing type declarations and methods. This Step documents the systematic correction of these errors: elimination of duplicate declarations ofPyPPU, adding missing methods incpu.pxd (setPPUandrun_scanline), and correction of the order of inclusion innative_core.pyxto resolve dependencies between Cython modules.
Hardware Concept
This Step does not implement new hardware functionality, but rather fixes infrastructure issues in the Python-C++ (Cython) bridge. However, it is critical for the architecture implemented in Step 0175: without these changes, the methodrun_scanline()cannot be compiled and exposed to Python, completely blocking the new cycle-by-cycle emulation architecture.
Importance:Cython requires all C++ classes to be correctly declared in files.pxdto generate the appropriate link code. Forward statements and inclusion order are critical when there are circular dependencies between modules.
Implementation
Identified Problems
The Cython build failed with the following errors:
- Duplicate PyPPU declaration:The class
PyPPUwas declared both incpu.pyx(as forward declaration) as inppu.pyx(full definition), causing conflict during compilation. - Missing methods in cpu.pxd:The methods
setPPUandrun_scanlinewere implemented in C++ but not declared incpu.pxd, so Cython couldn't generate the binding code. - Lack of PPU forward declaration:The file
cpu.pxdI didn't have a forward declaration of the classPPU, required for the methodsetPPU(PPU* ppu). - Incorrect inclusion order:In
native_core.pyx,cpu.pyxwas included beforeppu.pyx, butcpu.pyxI needed access toPyPPUdefined inppu.pyx.
Corrections Applied
A. Duplicate Declaration Removal (cpu.pyx)
The forward declaration was removedPyPPUincpu.pyx, since the complete definition inppu.pyxwill be accessible when both modules are included innative_core.pyx.
# BEFORE (caused conflict):
cdef class PyPPU:
cdef ppu.PPU* _ppu
# AFTER (removed, PyPPU available from ppu.pyx):
# PyPPU will be available when ppu.pyx is included before cpu.pyx
B. Update cpu.pxd
Added the forward declaration ofPPUand the missing methods:
# Forward declaration of PPU
cdef extern from "PPU.hpp":
cdef cppclass PPU:
pass
# Methods added to CPU class
cdef cppclass CPU:
#...existing methods...
void setPPU(PPU* ppu) # NEW
void run_scanline() # NEW
C. Inclusion Order Correction (native_core.pyx)
The order was changed so thatppu.pyxbe included beforecpu.pyx, ensuring thatPyPPUis available whencpu.pyxcompiles:
# BEFORE:
include "mmu.pyx"
include "registers.pyx"
include "cpu.pyx"
include "ppu.pyx"
# AFTER:
include "mmu.pyx"
include "registers.pyx"
include "ppu.pyx" # Moved before cpu.pyx
include "cpu.pyx" # You can now use PyPPU
D. Correction of the set_ppu Method (cpu.pyx)
The method was adjustedset_pputo declare the variablecdef PyPPU ppu_objat the beginning of the method (outside conditional blocks), complying with Cython rules:
cpdef set_ppu(self, object ppu_wrapper):
cdef PyPPU ppu_obj # Declaration at the beginning
if ppu_wrapper is None:
self._cpu.setPPU(NULL)
else:
ppu_obj = <PyPPU>ppu_wrapper
self._cpu.setPPU(ppu_obj._ppu)
Modified Components
src/core/cython/cpu.pyx: Removed duplicate PyPPU declaration, fixed set_ppu method.src/core/cython/cpu.pxd: Added PPU forward declaration and setPPU/run_scanline methods.src/core/cython/native_core.pyx: Corrected inclusion order (ppu.pyx before cpu.pyx).
Affected Files
src/core/cython/cpu.pyx- Removed duplicate forward declaration from PyPPU, fixed set_ppu methodsrc/core/cython/cpu.pxd- Added PPU forward declaration and setPPU/run_scanline methodssrc/core/cython/native_core.pyx- Corrected order of inclusion of modules
Tests and Verification
Verification was performed by successful compilation of the C++ module:
- Compilation:
python setup.py build_ext --inplacesuccessfully completed - Rebuild script:
.\rebuild_cpp.ps1executed without errors - Fixed bugs:All Cython build errors were removed
- Warnings:Only minor warnings related to pointer truncation (normal in Cython generated code)
C++ Compiled Module Validation
The moduleviboy_core.cp313-win_amd64.pydwas generated correctly, confirming that:
- The methods
setPPUandrun_scanlineare correctly linked - The class
PyPPUis accessible fromPyCPU - No circular dependencies blocking the build
Sources consulted
- Official Cython Documentation- Type declarations, forward declarations, inclusion order
- Cython: Sharing Declarations Between Cython Modules- Forward declaration mechanisms
Educational Integrity
What I Understand Now
- Forward declarations in Cython:When a Cython module needs to reference a class defined in another module, there are two options: use a forward declaration (if only the type is needed for pointers) or ensure that the module that defines the class is included first in the file that groups them together.
- .pxd files:The files
.pxdThey are critical for declaring the interface of C++ classes to Cython. Without declaring methods in.pxd, Cython cannot generate the correct binding code, even if the methods are implemented in C++. - Order of Inclusion:In Cython, when using directives
include, order matters. Yeahmodule_a.pyxneed types ofmodule_b.pyx,module_b.pyxmust be included first. - cdef declarations in cpdef methods:The variables
cdefin methodscpdefThey must be declared at the beginning of the method, outside of conditional blocks, to comply with Cython rules.
What remains to be confirmed
- Full functionality:Although the module compiles correctly, we still need to verify that
run_scanline()works correctly at runtime and resolves the synchronization deadlock.
Hypotheses and Assumptions
There are no critical assumptions. All fixes are based on official Cython documentation and standard build rules.
Next Steps
- [ ] Verify that
run_scanline()works correctly at runtime - [ ] Update
viboy.pyto use the new methodrun_scanline()instead of Python loop - [ ] Test the emulator with Tetris to confirm that the sync deadlock is resolved
- [ ] Verify that the PPU is updated correctly after each CPU instruction