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.
Francotirador: Recarga
Resumen
Tras implementar Echo RAM (Step 0239) y el monitor GPS (Step 0240), el análisis del GPS revela que la CPU sigue atrapada en la zona 0x2B24. Aunque la lógica de Echo RAM está implementada, el juego sigue fallando la validación de memoria. Se reactiva el "Francotirador" (traza detallada) en el rango 0x2B20-0x2B30 para observar el comportamiento dinámico del bucle y determinar si HL avanza (escaneando memoria) o se reinicia constantemente.
Concepto de Hardware
Análisis Dinámico de Bucles de Verificación: Cuando un juego verifica la integridad de la memoria, típicamente ejecuta un bucle que:
- Inicializa un registro (ej: HL) con una dirección de inicio.
- Lee un byte de memoria en esa dirección.
- Compara el valor leído con un valor esperado (ej:
CP 0xFD). - Si la comparación falla, reinicia el bucle o aborta.
- Si la comparación pasa, incrementa HL y repite hasta cubrir todo el rango.
Si el bucle está avanzando (HL incrementa), significa que la verificación está progresando pero es lenta (puede estar escaneando toda la RAM). Si el bucle está estático (HL se reinicia constantemente), significa que falla en el primer byte y nunca avanza.
El "Francotirador" es una técnica de debugging que consiste en activar trazas detalladas solo en un rango específico de direcciones de memoria. Esto permite observar el comportamiento de un bucle sin saturar la consola con logs masivos de toda la ejecución.
Implementación
Se reactiva el bloque de debug del Francotirador en CPU.cpp, justo antes del fetch del opcode, para capturar cada instrucción ejecutada en el rango crítico.
Componentes modificados
src/core/cpp/CPU.cpp: Añadido bloque de debug del Francotirador en el métodostep().
Decisiones de diseño
El bloque de debug se coloca antes del fetch_byte() para capturar el PC antes de que se incremente. Esto permite ver la dirección exacta donde se ejecuta cada instrucción. El formato del log incluye:
- PC: Program Counter (dirección de la instrucción actual).
- OP: Opcode de la instrucción.
- A: Valor del acumulador (para ver qué valor se está comparando).
- HL: Valor del par de registros HL (para ver si avanza o se reinicia).
Nota importante: Este debug es temporal y debe desactivarse una vez que identifiquemos el problema, ya que los printf ralentizan significativamente la ejecución.
Código añadido
// --- Step 0241: FRANCOTIRADOR RECARGADO ---
// Reactivamos el debug del Francotirador para analizar el comportamiento
// dinámico del bucle en 0x2B20-0x2B30. Necesitamos ver si HL avanza
// (escaneando memoria) o se reinicia constantemente.
if (regs_->pc >= 0x2B20 && regs_->pc <= 0x2B30) {
uint8_t opcode = mmu_->read(regs_->pc);
printf("[SNIPER] PC:%04X | OP:%02X | A:%02X | HL:%04X\n",
regs_->pc, opcode, regs_->a, regs_->get_hl());
}
// ------------------------------------------
Archivos Afectados
src/core/cpp/CPU.cpp- Reactivación del bloque de debug del Francotirador en el métodostep()docs/bitacora/entries/2025-12-22__0241__francotirador-recarga.html- Entrada de bitácora
Tests y Verificación
Para validar esta implementación, se debe:
- Recompilar la extensión C++:
python setup.py build_ext --inplace - Ejecutar el emulador con Tetris:
python main.py roms/tetris.gb - Analizar los logs del Francotirador:
- Si HL avanza (ej:
HL:E645,HL:E646,HL:E647...): El bucle está escaneando toda la RAM y solo es lento. Dejar correr o optimizar los logs. - Si HL es estático (ej:
HL:E645repetido): El bucle falla en el primer byte y se reinicia. Hay un problema de datos en RAM. - Si A cambia: Ver qué valor se está comparando y si coincide con lo esperado.
- Si HL avanza (ej:
Estado actual: Pendiente de ejecución y análisis de logs.
Fuentes Consultadas
- Pan Docs: Game Boy Pan Docs - Referencia general de arquitectura LR35902
Nota: Esta implementación es una técnica de debugging estándar para análisis dinámico de bucles.
Integridad Educativa
Lo que Entiendo Ahora
- Análisis Dinámico: Observar el comportamiento de un bucle en tiempo de ejecución es crucial para entender por qué falla. Los logs estáticos (dumps de memoria) no muestran el flujo de ejecución.
- Debugging Selectivo: Activar trazas solo en un rango específico de direcciones permite obtener información detallada sin saturar la consola con logs masivos.
- Patrones de Verificación: Los juegos suelen verificar la integridad de la memoria escaneando byte a byte. Si el bucle avanza, la verificación está progresando; si se reinicia, hay un fallo temprano.
Lo que Falta Confirmar
- Comportamiento de HL: ¿HL avanza o se reinicia? Esto determinará si el problema es un fallo temprano o un bucle lento.
- Valor de A: ¿Qué valor se está comparando? ¿Es el esperado (
0xFD) o hay una discrepancia? - Estado de la memoria: ¿La memoria WRAM contiene los valores correctos? ¿La Echo RAM está funcionando correctamente?
Hipótesis y Suposiciones
Hipótesis principal: Aunque implementamos Echo RAM, es posible que:
- La memoria WRAM no se inicializó correctamente antes de la verificación.
- El juego escribió valores en WRAM que no se reflejaron correctamente en Echo RAM.
- Hay otro problema de inicialización que causa que la verificación falle en un byte diferente.
Los logs del Francotirador nos dirán cuál de estas hipótesis es correcta.
Próximos Pasos
- [ ] Recompilar la extensión C++ con el debug del Francotirador activado.
- [ ] Ejecutar Tetris y capturar los logs del Francotirador.
- [ ] Analizar los logs para determinar si HL avanza o se reinicia.
- [ ] Si HL avanza: Dejar correr el bucle o optimizar los logs para que termine más rápido.
- [ ] Si HL es estático: Investigar por qué la memoria no contiene los valores esperados.
- [ ] Una vez identificado el problema, desactivar el debug del Francotirador para no ralentizar la ejecución.