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
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:
- Byte 1: Opcode
0x08 - Byte 2: Dirección baja (LSB) en formato Little-Endian
- 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
NOPpara mantener el orden lógico de opcodes.
Archivos Afectados
src/core/cpp/CPU.cpp- Añadido caso0x08en el switch de opcodes del métodostep()
Tests y Verificación
Para validar la implementación:
- Recompilar:
.\rebuild_cpp.ps1opython setup.py build_ext --inplace - Ejecutar:
python main.py roms/tetris.gb - Analizar traza: Verificar que el "Francotirador" ya no muestre opcodes
0x2Fy0x3Fejecutándose después de0x08en 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