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.
Silencio Total: El Despegue
Resumen
Los logs del "Francotirador" (Step 0228) confirmaron que el hardware funciona correctamente:
el registro LY avanza de 26 a 38, la CPU lee correctamente el registro, y no hay deadlock.
El aparente bloqueo era causado por la latencia extrema de imprimir logs en cada ciclo de CPU.
Se procedió a eliminar toda la instrumentación de depuración en C++ para permitir que el emulador
alcance su velocidad nativa (60 FPS) y supere el bucle de espera de V-Blank en tiempo real.
Concepto de Hardware
En un emulador de Game Boy, el bucle principal de emulación ejecuta millones de instrucciones por segundo. Cada instrucción de CPU consume entre 1 y 6 M-Cycles (4-24 T-Cycles). Para alcanzar 60 FPS, el emulador debe ejecutar aproximadamente 70,224 ciclos por frame, lo que significa ejecutar miles de instrucciones en menos de 16.67 milisegundos.
El Efecto del Observador: Imprimir texto en la consola (printf) es una operación
extremadamente lenta comparada con la ejecución de una instrucción de CPU. Una llamada a printf
puede tomar cientos o miles de microsegundos, mientras que una instrucción de CPU se ejecuta en nanosegundos.
Si imprimimos un log en cada ciclo de CPU, el emulador se ralentiza miles de veces, haciendo que parezca
que está colgado cuando en realidad solo está ejecutándose a "cámara super-lenta".
En el caso del Step 0228, el juego estaba esperando a que LY llegara a 144 (V-Blank).
Para llegar de la línea 38 a la 144, la Game Boy necesita aproximadamente 4 milisegundos en hardware real.
Con los logs activados imprimiendo cada paso, esos 4 milisegundos se convertían en minutos, haciendo que
pareciera que el emulador estaba bloqueado cuando en realidad solo estaba avanzando muy lentamente.
Regla de Oro: En el bucle crítico de emulación (el que se ejecuta millones de veces por segundo), cualquier operación de I/O (impresión, escritura de archivos, llamadas al sistema) debe eliminarse completamente o redirigirse a buffers en memoria que se vuelquen periódicamente. El I/O mata el rendimiento.
Implementación
Se procedió a eliminar o comentar todos los bloques de logging en el núcleo C++:
Componentes modificados
- CPU.cpp: Comentado el bloque del "Francotirador" (Step 0228) que imprimía logs cuando PC estaba en 0x2B10-0x2B20. También se comentó el
#include <cstdio>. - MMU.cpp: Comentado el "Sensor de VRAM" (Step 0204) que imprimía cuando se detectaba la primera escritura en VRAM.
Decisiones de diseño
En lugar de eliminar completamente el código de logging, se optó por comentarlo. Esto permite:
- Reactivación rápida: Si necesitamos depurar en el futuro, podemos descomentar fácilmente.
- Historial: Los comentarios documentan qué se desactivó y por qué.
- Compilación limpia: El código comentado no afecta el rendimiento ni el tamaño del binario.
Todos los bloques comentados incluyen una referencia al Step 0229 explicando por qué se desactivaron.
Archivos Afectados
src/core/cpp/CPU.cpp- Comentado bloque del Francotirador y#include <cstdio>src/core/cpp/MMU.cpp- Comentado bloque del Sensor de VRAM
Tests y Verificación
Validación de la limpieza de logs:
- Compilación:
.\rebuild_cpp.ps1opython setup.py build_ext --inplace - Ejecución:
python main.py roms/tetris.gb - Resultado esperado: El emulador debe arrancar a velocidad nativa (60 FPS) y el juego debe avanzar más allá del bucle de espera de V-Blank.
- Validación: Sin logs en la consola durante la ejecución normal. El contador
LYdebe volar de 38 a 144 en milisegundos, permitiendo que el juego continúe.
Evidencia de Tests:
# Comando ejecutado:
python main.py roms/tetris.gb
# Resultado esperado:
# - Sin logs [SNIPER] en la consola
# - Sin logs [VRAM WRITE DETECTED!] en la consola
# - El juego arranca y muestra la pantalla de título (o el logo de Nintendo)
# - FPS estable a 60 FPS
Fuentes Consultadas
- Pan Docs: System Clock, Timing
- Documentación técnica: Principios de optimización de bucles críticos en sistemas embebidos
Integridad Educativa
Lo que Entiendo Ahora
- Rendimiento en bucles críticos: Cualquier operación de I/O en un bucle que se ejecuta millones de veces por segundo tiene un impacto devastador en el rendimiento. Un
printfpuede ralentizar el emulador miles de veces. - El Efecto del Observador: Instrumentar código para depurar puede cambiar completamente el comportamiento observado, haciendo que parezca que hay un bug cuando en realidad solo hay una ralentización extrema.
- Timing en emulación: El timing es crítico. Si el emulador no puede ejecutar instrucciones a la velocidad correcta, los bucles de espera del juego nunca terminan, haciendo que parezca que está colgado.
Lo que Falta Confirmar
- Arranque completo del juego: Verificar que el juego arranca completamente y muestra la pantalla de título después de eliminar los logs.
- Rendimiento sostenido: Confirmar que el emulador mantiene 60 FPS durante la ejecución normal del juego.
Hipótesis y Suposiciones
Hipótesis principal: Al eliminar los logs, el emulador debería alcanzar velocidad nativa y el juego debería arrancar completamente, mostrando la pantalla de título o el logo de Nintendo. El bucle de espera de V-Blank que parecía infinito debería completarse en milisegundos.
Próximos Pasos
- [ ] Verificar que el juego arranca completamente después de eliminar los logs
- [ ] Confirmar que el emulador mantiene 60 FPS durante la ejecución
- [ ] Si el juego aún no arranca, investigar otros posibles bloqueos (Timer, Interrupciones, Joypad)
- [ ] Implementar sistema de logging condicional (solo en builds de debug) para futuras depuraciones