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

CPU Desalineada: El Caso del Opcode 0x08

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

Resumen

El análisis forense de la traza del "Francotirador" (Step 0228) reveló un error crítico de sincronización: el opcode 0x08 (LD (nn), SP) no estaba implementado. Esto causaba que la CPU interpretara los 2 bytes de dirección siguientes como instrucciones, desalineando completamente el flujo de ejecución y ejecutando "basura" que corrompía los flags y la lógica del juego.

Concepto de Hardware

LD (nn), SP es una instrucción de 3 bytes que guarda el Stack Pointer (SP) en una dirección de memoria absoluta especificada directamente en el código:

  1. Byte 1: Opcode 0x08
  2. Byte 2: Dirección baja (LSB) en formato Little-Endian
  3. Byte 3: Dirección alta (MSB) en formato Little-Endian

La instrucción escribe SP en la dirección especificada en formato Little-Endian: primero el byte bajo (SP & 0xFF) en la dirección nn, luego el byte alto (SP >> 8) en la dirección nn + 1.

El Problema del Desalineamiento:

Si esta instrucción no está implementada, la CPU la trata como una instrucción de 1 byte (probablemente un NOP por defecto o cae en el caso default). Luego, cuando intenta ejecutar la siguiente instrucción, lee los bytes 2 y 3 de la instrucción anterior como si fueran opcodes nuevos. Esto causa:

  • Desalineamiento: El PC no avanza correctamente, saltando sobre datos que deberían ser direcciones.
  • Ejecución de "Basura": Los bytes de datos se interpretan como instrucciones (ej: 0x2F = CPL, 0x3F = CCF).
  • Corrupción de Flags: Las instrucciones ejecutadas por error modifican los flags de forma inesperada.
  • Flujo Roto: El juego pierde completamente el control de la ejecución.

Este tipo de error es especialmente difícil de detectar porque el juego puede seguir "ejecutando" durante mucho tiempo, pero con una lógica completamente corrupta.

Implementación

Implementamos el opcode 0x08 en el método step() de CPU.cpp, siguiendo la especificación de Pan Docs.

Modificación en CPU.cpp

Añadimos el caso 0x08 al switch de opcodes, justo después de NOP (0x00):

// --- Step 0231: FIX DESALINEAMIENTO ---
// LD (nn), SP - Guarda el Stack Pointer en la dirección nn
// Esta instrucción es de 3 bytes. Si falta, la CPU ejecuta los datos
// de la dirección como instrucciones, corrompiendo el flujo.
// Fuente: Pan Docs - LD (nn), SP: 5 M-Cycles
case 0x08:  // LD (nn), SP
    {
        uint16_t addr = fetch_word();  // Consume 2 bytes más (nn en Little-Endian)
        // Escribe SP en formato Little Endian (low byte primero, high byte segundo)
        mmu_->write(addr, regs_->sp & 0xFF);         // Low byte
        mmu_->write(addr + 1, (regs_->sp >> 8) & 0xFF);  // High byte
        cycles_ += 5;  // LD (nn), SP consume 5 M-Cycles
        return 5;
    }
// -------------------------------------

Decisiones de diseño

  • Timing: 5 M-Cycles según Pan Docs (fetch opcode + fetch 2 bytes de dirección + 2 writes a memoria).
  • Little-Endian: La dirección se lee en formato Little-Endian (LSB primero), y SP se escribe también en Little-Endian.
  • Ubicación: Colocado justo después de NOP para mantener el orden lógico de opcodes.

Archivos Afectados

  • src/core/cpp/CPU.cpp - Añadido caso 0x08 en el switch de opcodes del método step()

Tests y Verificación

Para validar la implementación:

  1. Recompilar: .\rebuild_cpp.ps1 o python setup.py build_ext --inplace
  2. Ejecutar: python main.py roms/tetris.gb
  3. Analizar traza: Verificar que el "Francotirador" ya no muestre opcodes 0x2F y 0x3F ejecutándose después de 0x08 en las direcciones esperadas.

Resultado Esperado:

  • La CPU ya no se "comerá" los bytes intermedios después de 0x08.
  • El flujo lógico del juego debería restaurarse.
  • Posibilidad alta: ¡Que el juego arranque y veas gráficos!
  • Posibilidad media: Que avance hasta el siguiente opcode no implementado (pero ya en el camino correcto).

Validación de módulo compilado C++: El opcode se ejecuta directamente en el núcleo C++, sin overhead de Python, garantizando el timing preciso de 5 M-Cycles.

Fuentes Consultadas

  • Pan Docs: Game Boy Pan Docs - Especificación del opcode LD (nn), SP (0x08)
  • Implementación basada en conocimiento general de arquitectura LR35902 y análisis forense de trazas de ejecución.

Integridad Educativa

Lo que Entiendo Ahora

  • Desalineamiento de Instrucciones: Si una instrucción de múltiples bytes no está implementada, la CPU puede interpretar los bytes de datos como código, causando ejecución de "basura".
  • Análisis Forense de Trazas: Comparar la secuencia de opcodes ejecutados con la secuencia esperada puede revelar opcodes faltantes o mal implementados.
  • Little-Endian en Game Boy: Tanto las direcciones como los valores de 16 bits se almacenan en formato Little-Endian (LSB primero).

Lo que Falta Confirmar

  • Otros Opcodes Faltantes: Puede haber más opcodes no implementados que causen problemas similares.
  • Recuperación del Juego: Si este era el único problema, el juego debería arrancar correctamente ahora.

Hipótesis y Suposiciones

Hipótesis Principal: El opcode 0x08 era el causante principal del desalineamiento. Con su implementación, el juego debería poder avanzar correctamente en su secuencia de inicialización. Si aún hay problemas, probablemente sean otros opcodes faltantes o problemas de sincronización de hardware.

Próximos Pasos

  • [ ] Ejecutar el emulador y verificar que el desalineamiento se haya corregido
  • [ ] Analizar si el juego avanza más allá del punto de bloqueo anterior
  • [ ] Verificar si aparecen gráficos o si el juego enciende la pantalla
  • [ ] Si hay nuevos errores, analizar la traza para identificar el siguiente opcode faltante