⚠️ 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.

Francotirador: Recarga

Fecha: 2025-12-22 Step ID: 0241 Estado: Draft

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:

  1. Inicializa un registro (ej: HL) con una dirección de inicio.
  2. Lee un byte de memoria en esa dirección.
  3. Compara el valor leído con un valor esperado (ej: CP 0xFD).
  4. Si la comparación falla, reinicia el bucle o aborta.
  5. 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étodo step().

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étodo step()
  • 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:

  1. Recompilar la extensión C++:
    python setup.py build_ext --inplace
  2. Ejecutar el emulador con Tetris:
    python main.py roms/tetris.gb
  3. 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:E645 repetido): 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.

Estado actual: Pendiente de ejecución y análisis de logs.

Fuentes Consultadas

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.