⚠️ Clean-Room / Educativo

Este proyecto es educativo y Open Source. No se copia código de otros emuladores. Implementación basada únicamente en documentación técnica y tests permitidas.

Corrección de Errores de Compilación Cython: setPPU y run_scanline

Fecha: 2025-12-20 Step ID: 0176 Estado: ✅ VERIFIED

Resumen

Después de implementar el método run_scanline() en C++ y su wrapper en Cython, la compilación falló con múltiples errores relacionados con declaraciones de tipos y métodos faltantes. Este Step documenta la corrección sistemática de estos errores: eliminación de declaraciones duplicadas de PyPPU, adición de métodos faltantes en cpu.pxd (setPPU y run_scanline), y corrección del orden de inclusión en native_core.pyx para resolver dependencias entre módulos Cython.

Concepto de Hardware

Este Step no implementa nueva funcionalidad de hardware, sino que corrige problemas de infraestructura en el puente Python-C++ (Cython). Sin embargo, es crítico para la arquitectura implementada en el Step 0175: sin estos cambios, el método run_scanline() no puede ser compilado y expuesto a Python, bloqueando completamente la nueva arquitectura de emulación ciclo a ciclo.

Importancia: Cython requiere que todas las clases C++ estén correctamente declaradas en archivos .pxd para generar el código de enlace apropiado. Las declaraciones forward y el orden de inclusión son críticos cuando hay dependencias circulares entre módulos.

Implementación

Problemas Identificados

La compilación de Cython falló con los siguientes errores:

  1. Declaración duplicada de PyPPU: La clase PyPPU estaba declarada tanto en cpu.pyx (como forward declaration) como en ppu.pyx (definición completa), causando conflicto durante la compilación.
  2. Métodos faltantes en cpu.pxd: Los métodos setPPU y run_scanline estaban implementados en C++ pero no declarados en cpu.pxd, por lo que Cython no podía generar el código de enlace.
  3. Falta de forward declaration de PPU: El archivo cpu.pxd no tenía una forward declaration de la clase PPU, necesaria para el método setPPU(PPU* ppu).
  4. Orden de inclusión incorrecto: En native_core.pyx, cpu.pyx se incluía antes que ppu.pyx, pero cpu.pyx necesitaba acceso a PyPPU definido en ppu.pyx.

Correcciones Aplicadas

A. Eliminación de Declaración Duplicada (cpu.pyx)

Se eliminó la forward declaration de PyPPU en cpu.pyx, ya que la definición completa en ppu.pyx será accesible cuando ambos módulos se incluyan en native_core.pyx.

# ANTES (causaba conflicto):
cdef class PyPPU:
    cdef ppu.PPU* _ppu

# DESPUÉS (eliminado, PyPPU disponible desde ppu.pyx):
# PyPPU estará disponible cuando ppu.pyx se incluya antes que cpu.pyx

B. Actualización de cpu.pxd

Se añadieron la forward declaration de PPU y los métodos faltantes:

# Forward declaration de PPU
cdef extern from "PPU.hpp":
    cdef cppclass PPU:
        pass

# Métodos añadidos a la clase CPU
cdef cppclass CPU:
    # ... métodos existentes ...
    void setPPU(PPU* ppu)           # NUEVO
    void run_scanline()              # NUEVO

C. Corrección del Orden de Inclusión (native_core.pyx)

Se cambió el orden para que ppu.pyx se incluya antes de cpu.pyx, asegurando que PyPPU esté disponible cuando cpu.pyx se compile:

# ANTES:
include "mmu.pyx"
include "registers.pyx"
include "cpu.pyx"
include "ppu.pyx"

# DESPUÉS:
include "mmu.pyx"
include "registers.pyx"
include "ppu.pyx"    # Movido antes de cpu.pyx
include "cpu.pyx"    # Ahora puede usar PyPPU

D. Corrección del Método set_ppu (cpu.pyx)

Se ajustó el método set_ppu para declarar la variable cdef PyPPU ppu_obj al principio del método (fuera de bloques condicionales), cumpliendo con las reglas de Cython:

cpdef set_ppu(self, object ppu_wrapper):
    cdef PyPPU ppu_obj  # Declaración al principio
    if ppu_wrapper is None:
        self._cpu.setPPU(NULL)
    else:
        ppu_obj = <PyPPU>ppu_wrapper
        self._cpu.setPPU(ppu_obj._ppu)

Componentes Modificados

  • src/core/cython/cpu.pyx: Eliminada declaración duplicada de PyPPU, corregido método set_ppu.
  • src/core/cython/cpu.pxd: Añadida forward declaration de PPU y métodos setPPU/run_scanline.
  • src/core/cython/native_core.pyx: Corregido orden de inclusión (ppu.pyx antes de cpu.pyx).

Archivos Afectados

  • src/core/cython/cpu.pyx - Eliminada forward declaration duplicada de PyPPU, corregido método set_ppu
  • src/core/cython/cpu.pxd - Añadida forward declaration de PPU y métodos setPPU/run_scanline
  • src/core/cython/native_core.pyx - Corregido orden de inclusión de módulos

Tests y Verificación

La verificación se realizó mediante compilación exitosa del módulo C++:

  • Compilación: python setup.py build_ext --inplace completada exitosamente
  • Script de rebuild: .\rebuild_cpp.ps1 ejecutado sin errores
  • Errores resueltos: Todos los errores de compilación de Cython fueron eliminados
  • Warnings: Solo warnings menores relacionados con truncamiento de punteros (normales en código generado por Cython)

Validación de Módulo Compilado C++

El módulo viboy_core.cp313-win_amd64.pyd se generó correctamente, confirmando que:

  • Los métodos setPPU y run_scanline están correctamente enlazados
  • La clase PyPPU es accesible desde PyCPU
  • No hay dependencias circulares que bloqueen la compilación

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Declaraciones Forward en Cython: Cuando un módulo Cython necesita referenciar una clase definida en otro módulo, hay dos opciones: usar una forward declaration (si solo se necesita el tipo para punteros) o asegurar que el módulo que define la clase se incluya antes en el archivo que los agrupa.
  • Archivos .pxd: Los archivos .pxd son críticos para declarar la interfaz de clases C++ a Cython. Sin declarar métodos en .pxd, Cython no puede generar el código de enlace correcto, incluso si los métodos están implementados en C++.
  • Orden de Inclusión: En Cython, cuando se usan directivas include, el orden importa. Si módulo_a.pyx necesita tipos de módulo_b.pyx, módulo_b.pyx debe incluirse primero.
  • Declaraciones cdef en Métodos cpdef: Las variables cdef en métodos cpdef deben declararse al principio del método, fuera de bloques condicionales, para cumplir con las reglas de Cython.

Lo que Falta Confirmar

  • Funcionalidad completa: Aunque el módulo compila correctamente, aún falta verificar que run_scanline() funciona correctamente en tiempo de ejecución y resuelve el deadlock de sincronización.

Hipótesis y Suposiciones

No hay suposiciones críticas. Todas las correcciones se basan en documentación oficial de Cython y reglas estándar de compilación.

Próximos Pasos

  • [ ] Verificar que run_scanline() funciona correctamente en tiempo de ejecución
  • [ ] Actualizar viboy.py para usar el nuevo método run_scanline() en lugar del bucle Python
  • [ ] Probar el emulador con Tetris para confirmar que el deadlock de sincronización está resuelto
  • [ ] Verificar que la PPU se actualiza correctamente después de cada instrucción de la CPU