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.
Debug: Trazado desde PC=0x0100 para Capturar Bucle Oculto
Resumen
El deadlock de `LY=0` persiste, pero no hay warnings de opcodes no implementados, lo que indica que la CPU está en un bucle infinito de instrucciones válidas. El trazado disparado en `PC=0x0300` no se activa porque el PC está atascado antes. Se modifica el sistema de trazado para activarse desde el inicio de la ejecución (`PC=0x0100`) y capturar el bucle infinito en acción.
Concepto de Hardware
En un emulador de Game Boy, cuando la CPU está atrapada en un bucle infinito, el contador de líneas de escaneo (LY) no puede avanzar porque la CPU no está generando suficientes ciclos para que la PPU progrese. Este es un clásico deadlock del huevo y la gallina:
- El juego espera: El código del juego está esperando que la PPU haga algo (ej. entrar en V-Blank, cambiar el estado de STAT).
- La PPU no puede avanzar: La PPU no puede hacer nada porque `LY` no avanza.
- LY no avanza: `LY` no avanza porque la CPU está atrapada y no le da suficientes ciclos.
Si la CPU está ejecutando instrucciones válidas (sin warnings de opcodes desconocidos), pero el sistema no avanza, la única explicación lógica es que la CPU está ejecutando un pequeño bucle de código una y otra vez, tan rápido que el acumulador de ciclos en `viboy.py` nunca llega a los 456 necesarios para avanzar una sola línea de la PPU.
Para identificar este bucle, necesitamos ver qué está haciendo la CPU desde el primer momento. El trazado "disparado" en `PC=0x0300` es inútil si el PC nunca llega allí. Por lo tanto, movemos el trigger al inicio del programa (`PC=0x0100`), que es donde comienza la ejecución de la ROM después del boot.
Implementación
Se modifica el sistema de trazado en `CPU.cpp` para activarse desde el inicio de la ejecución y capturar un mayor número de instrucciones para identificar el patrón del bucle.
Modificaciones en CPU.cpp
Se cambian las constantes de trazado:
- DEBUG_TRIGGER_PC: De `0x0300` a `0x0100` (inicio del programa).
- DEBUG_INSTRUCTION_LIMIT: De `100` a `200` (para ver más instrucciones y capturar el patrón del bucle).
Código Modificado
// Variables estáticas para logging de diagnóstico con sistema "disparado" (triggered)
// Cambiamos el trigger al inicio del programa para ver el bucle oculto
static const uint16_t DEBUG_TRIGGER_PC = 0x0100; // Dirección de activación del trazado (inicio del programa)
static bool debug_trace_activated = false; // Bandera de activación
static int debug_instruction_counter = 0; // Contador post-activación
// Aumentamos un poco el límite para ver el patrón del bucle
static const int DEBUG_INSTRUCTION_LIMIT = 200; // Límite post-activación (aumentado para capturar bucles)
Proceso de Compilación y Ejecución
Para aplicar los cambios:
- Recompilar el módulo C++: Ejecutar `.\rebuild_cpp.ps1` o `python setup.py build_ext --inplace`.
- Ejecutar el emulador: `python main.py roms/tetris.gb` (o cualquier ROM).
- Analizar la traza: La consola mostrará las primeras 200 instrucciones desde el inicio del programa.
Análisis Esperado
Al final de la traza, deberíamos ver un patrón repetitivo de 2 o 3 opcodes/PCs que se repiten una y otra vez. Este será nuestro bucle infinito. Un ejemplo típico sería:
...
[CPU TRACE 195] PC: 0x02B4 | Opcode: 0xFE (CP d8)
[CPU TRACE 196] PC: 0x02B6 | Opcode: 0x28 (JR Z, e)
[CPU TRACE 197] PC: 0x02B4 | Opcode: 0xFE (CP d8)
[CPU TRACE 198] PC: 0x02B6 | Opcode: 0x28 (JR Z, e)
[CPU TRACE 199] PC: 0x02B4 | Opcode: 0xFE (CP d8)
Este patrón nos dirá exactamente qué está esperando el juego. Probablemente está haciendo un `CP` (Compare) contra un registro de hardware (como `STAT`) y esperando que cambie de valor, pero como la PPU está parada, ese valor nunca cambia.
Archivos Modificados
src/core/cpp/CPU.cpp- Modificación de constantes de trazado (líneas 8-11).
Próximos Pasos
- Recompilar y ejecutar: Aplicar los cambios y ejecutar el emulador para obtener la traza completa.
- Identificar el bucle: Analizar la traza para encontrar el patrón repetitivo al final.
- Determinar la causa: Entender qué registro de hardware está esperando el juego y por qué no cambia.
- Implementar la solución: Corregir el problema identificado (puede ser un registro de hardware no implementado, un flag de interrupción, o un problema de sincronización).
Referencias
- Pan Docs: Sección sobre el contador de líneas de escaneo (LY) y sincronización CPU-PPU.
- GBEDG: Documentación sobre el registro STAT y flags de interrupción.