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

Debug: Implementación de Trazado de CPU "Disparado" (Triggered)

Fecha: 2025-12-20 Step ID: 0157 Estado: 🔍 DRAFT

Resumen

El análisis de la traza de 2000 instrucciones (Step 0156) demostró que el método de trazado de longitud fija es ineficiente para superar las largas rutinas de inicialización de la ROM. Se implementó un sistema de trazado "disparado" (triggered) que se activa automáticamente cuando el Program Counter (PC) supera una dirección específica (0x0300), evitando así registrar miles de instrucciones de bucles de inicialización y permitiendo capturar directamente el código crítico que se ejecuta después.

Concepto de Hardware

Las rutinas de inicialización de las ROMs de Game Boy pueden ejecutar miles de instrucciones en bucles de limpieza de memoria antes de llegar al código principal del juego. El trazado de todas estas instrucciones es ineficiente y genera logs masivos que dificultan el análisis.

Una estrategia más eficiente es usar trazado condicional o trazado disparado: en lugar de registrar las primeras N instrucciones desde el inicio, el sistema espera hasta que el Program Counter alcance una dirección específica (el "trigger") y solo entonces comienza a registrar. Esto permite:

  • Evitar ruido: No se registran miles de instrucciones de bucles de inicialización.
  • Enfoque dirigido: Se captura directamente el código crítico que se ejecuta después de la inicialización.
  • Eficiencia: Se reduce drásticamente el tamaño de los logs y el tiempo de análisis.

Basándonos en el análisis previo, sabemos que los bucles de inicialización de Tetris terminan alrededor de la dirección 0x0297-0x0298. Establecer el trigger en 0x0300 garantiza que capturaremos el código que se ejecuta inmediatamente después de que todos los bucles hayan finalizado.

Fuente: Técnica de depuración estándar en emulación - Conditional Breakpoints / Triggered Tracing

Implementación

Se reemplazó el sistema de trazado de longitud fija por un sistema de trazado disparado basado en el Program Counter. El sistema ahora incluye:

  • Dirección de activación (trigger): DEBUG_TRIGGER_PC = 0x0300
  • Bandera de activación: debug_trace_activated que se activa cuando el PC supera el trigger
  • Contador post-activación: Registra hasta 100 instrucciones después de la activación

Componentes modificados

  • CPU.cpp: Reemplazo completo de la lógica de trazado con sistema disparado.

Cambios realizados

Variables estáticas actualizadas:

// Variables estáticas para logging de diagnóstico con sistema "disparado" (triggered)
static const uint16_t DEBUG_TRIGGER_PC = 0x0300; // Dirección de activación del trazado
static bool debug_trace_activated = false;      // Bandera de activación
static int debug_instruction_counter = 0;       // Contador post-activación
static const int DEBUG_INSTRUCTION_LIMIT = 100; // Límite post-activación (reducido porque ahora es dirigido)

Constructor actualizado:

CPU::CPU(MMU* mmu, CoreRegisters* registers)
    : mmu_(mmu), regs_(registers), cycles_(0), ime_(false), halted_(false), ime_scheduled_(false) {
    // ...
    // Resetear contador y bandera de debug al crear nueva instancia
    debug_trace_activated = false;
    debug_instruction_counter = 0;
}

Lógica de trazado en step():

// --- NUEVA LÓGICA DE TRAZADO DISPARADO (TRIGGERED) ---
// 1. Comprobar si debemos activar la traza cuando el PC supera el trigger
if (!debug_trace_activated && current_pc >= DEBUG_TRIGGER_PC) {
    printf("--- [CPU TRACE TRIGGERED at PC: 0x%04X] ---\n", current_pc);
    debug_trace_activated = true;
}

// 2. Si la traza está activa, registrar hasta el límite
if (debug_trace_activated && debug_instruction_counter < DEBUG_INSTRUCTION_LIMIT) {
    printf("[CPU TRACE %d] PC: 0x%04X | Opcode: 0x%02X\n",
           debug_instruction_counter, current_pc, opcode);
    debug_instruction_counter++;
}
// --- FIN DE LA NUEVA LÓGICA DE TRAZADO DISPARADO ---

Decisiones de diseño

  • Dirección de trigger (0x0300): Elegida basándose en el análisis previo que mostró que los bucles terminan alrededor de 0x0297-0x0298. 0x0300 proporciona un margen de seguridad.
  • Límite reducido (100 instrucciones): Como ahora el trazado es dirigido y captura código crítico, 100 instrucciones deberían ser suficientes para identificar el siguiente opcode faltante.
  • Bandera estática: Se usa una bandera estática para mantener el estado entre llamadas a step(), permitiendo que el sistema se active una sola vez y luego registre las siguientes instrucciones.

Archivos Afectados

  • src/core/cpp/CPU.cpp - Reemplazo completo del sistema de trazado con lógica disparada basada en PC.

Tests y Verificación

La verificación se realizará mediante ejecución del emulador con una ROM de prueba:

  • Comando de compilación: .\rebuild_cpp.ps1
  • Comando de ejecución: python main.py roms/tetris.gb
  • Resultado esperado:
    • La consola debería permanecer en silencio durante los bucles de inicialización.
    • Cuando el PC alcance 0x0300, debería aparecer: --- [CPU TRACE TRIGGERED at PC: 0x0300] ---
    • A continuación, deberían aparecer las 100 instrucciones críticas que se ejecutan después.
  • Validación: La nueva traza debería ser radicalmente diferente a la anterior, sin mostrar ninguna instrucción del bucle 0x0293-0x0295, y capturando directamente el código de la siguiente fase de inicialización.

Nota: La validación completa requiere ejecutar el emulador y analizar la salida de la consola. Este paso documenta la implementación del sistema de trazado disparado.

Fuentes Consultadas

  • Técnica de depuración estándar: Conditional Breakpoints / Triggered Tracing
  • Análisis previo de trazas (Steps 0155 y 0156) que identificaron la ubicación de los bucles de inicialización

Nota: Esta es una técnica de depuración estándar en emulación, no específica del hardware Game Boy.

Integridad Educativa

Lo que Entiendo Ahora

  • Trazado condicional: El trazado de todas las instrucciones desde el inicio es ineficiente cuando hay rutinas de inicialización largas. El trazado condicional permite enfocarse en el código crítico.
  • Estrategia de depuración: En lugar de usar "fuerza bruta" (aumentar el límite indefinidamente), es mejor usar una estrategia dirigida que capture solo lo relevante.
  • Análisis de trazas previas: El análisis de las trazas anteriores nos permitió identificar dónde terminan los bucles de inicialización, lo que nos permitió elegir una dirección de trigger apropiada.

Lo que Falta Confirmar

  • Efectividad del trigger: Necesitamos verificar que 0x0300 es una dirección apropiada y que captura el código crítico sin perder información importante.
  • Límite de 100 instrucciones: Necesitamos confirmar que 100 instrucciones son suficientes para identificar el siguiente opcode faltante.
  • Análisis de la nueva traza: Una vez ejecutado el emulador, necesitamos analizar la nueva traza para identificar el siguiente opcode que debemos implementar.

Hipótesis y Suposiciones

Hipótesis principal: El código que se ejecuta después de 0x0300 contendrá el siguiente opcode faltante que necesitamos implementar para que el juego continúe. Esta hipótesis se basa en el análisis previo que mostró que los bucles de inicialización terminan alrededor de 0x0297-0x0298.

Próximos Pasos

  • [ ] Recompilar el módulo C++ con .\rebuild_cpp.ps1
  • [ ] Ejecutar el emulador con python main.py roms/tetris.gb
  • [ ] Analizar la nueva traza dirigida para identificar el siguiente opcode faltante
  • [ ] Implementar el opcode identificado si es necesario
  • [ ] Verificar que el juego puede continuar más allá de la inicialización