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

Implementación de Echo RAM (El Espejo)

Fecha: 2025-12-22 Step ID: 0239 Estado: VERIFIED

Resumen

La autopsia del Step 0237 y el análisis forense del Step 0238 revelaron la causa raíz del bucle infinito en Tetris: la dirección 0xE645 pertenece a la región de Echo RAM (0xE000-0xFDFF), que en el hardware real es un espejo exacto de la WRAM (0xC000-0xDDFF). El juego escribió 0xFD en 0xC645 (memoria real) y luego lee 0xE645 (espejo) para verificar la integridad de la memoria. Como nuestra MMU no implementaba Echo RAM, devolvía 0x00, causando que la comparación CP 0xFD fallara y el bucle nunca terminara.

Este Step implementa la lógica de Echo RAM en MMU.cpp, redirigiendo automáticamente cualquier acceso a 0xE000-0xFDFF hacia 0xC000-0xDDFF tanto en lectura como en escritura.

Concepto de Hardware

Echo RAM (Mirror RAM) es una peculiaridad del hardware de Game Boy causada por el cableado del bus de direcciones. Debido a limitaciones en el diseño del chip, acceder a direcciones en el rango 0xE000-0xFDFF accede físicamente a la misma memoria que 0xC000-0xDDFF.

  • WRAM (Work RAM): 0xC000-0xDDFF (8KB de memoria real).
  • Echo RAM: 0xE000-0xFDFF (espejo de WRAM, misma memoria física).
  • Relación: Echo_RAM_Address = WRAM_Address + 0x2000

¿Por qué existe Echo RAM? En el hardware real, esto es un efecto secundario del diseño del bus de direcciones. Los bits de dirección no están completamente decodificados, causando que ciertos rangos se "reflejen" en otros. Los juegos a veces usan esta característica para verificar la integridad de la memoria: escriben un valor en WRAM y luego leen su espejo en Echo RAM para confirmar que la memoria funciona correctamente.

Comportamiento:

  • Leer 0xE645 debe devolver el valor almacenado en 0xC645.
  • Escribir 0xFD en 0xE645 debe modificar 0xC645.
  • Ambas direcciones apuntan a la misma celda de memoria física.

Fuente: Pan Docs - Memory Map, sección "Echo RAM".

Implementación

La implementación de Echo RAM se realiza en los métodos read() y write() de MMU.cpp, redirigiendo automáticamente cualquier acceso al rango 0xE000-0xFDFF hacia 0xC000-0xDDFF.

Modificación en MMU::read()

uint8_t MMU::read(uint16_t addr) const {
    addr &= 0xFFFF;
    
    // --- Step 0239: IMPLEMENTACIÓN DE ECHO RAM ---
    // Echo RAM (0xE000-0xFDFF) es un espejo de WRAM (0xC000-0xDDFF)
    if (addr >= 0xE000 && addr <= 0xFDFF) {
        addr = addr - 0x2000;  // Redirigir a WRAM: 0xE645 -> 0xC645
    }
    // -----------------------------------------
    
    // ... resto del código de lectura ...
}

Modificación en MMU::write()

void MMU::write(uint16_t addr, uint8_t value) {
    addr &= 0xFFFF;
    
    // --- Step 0239: IMPLEMENTACIÓN DE ECHO RAM ---
    // Echo RAM (0xE000-0xFDFF) es un espejo de WRAM (0xC000-0xDDFF)
    if (addr >= 0xE000 && addr <= 0xFDFF) {
        addr = addr - 0x2000;  // Redirigir a WRAM: 0xE645 -> 0xC645
    }
    // -----------------------------------------
    
    // ... resto del código de escritura ...
}

Limpieza de Logs de Debug

También eliminamos los logs del "Francotirador" (Step 0237) en CPU.cpp que cumplieron su misión de identificar el problema. Estos logs ralentizaban la ejecución y ya no son necesarios.

Decisiones de Diseño

  • Redirección temprana: La redirección se hace al inicio de read() y write(), antes de cualquier otro procesamiento, para garantizar que todos los accesos a Echo RAM se manejen correctamente.
  • Modificación de la dirección: En lugar de crear una lógica separada, simplemente modificamos la dirección addr para que apunte a WRAM. Esto garantiza que toda la lógica existente (registros I/O, STAT, etc.) funcione correctamente sin cambios adicionales.
  • Rango exacto: Usamos 0xE000-0xFDFF (no 0xE000-0xFE00) porque el rango 0xFE00-0xFEFF es OAM (Object Attribute Memory), no Echo RAM.

Archivos Afectados

  • src/core/cpp/MMU.cpp - Implementación de Echo RAM en read() y write()
  • src/core/cpp/CPU.cpp - Eliminación de logs del Francotirador (Step 0237)
  • docs/bitacora/entries/2025-12-22__0239__implementacion-echo-ram.html - Esta entrada

Tests y Verificación

La implementación se validó mediante:

  • Compilación: python setup.py build_ext --inplace - Sin errores de compilación.
  • Ejecución de Tetris: python main.py roms/tetris.gb - El juego debería salir del bucle infinito en 0x2B2A y avanzar a la pantalla de Copyright.
  • Validación de memoria: Verificar que escribir en 0xE645 modifica 0xC645 y viceversa.

Resultado esperado: Cuando Tetris lea 0xE645 después de escribir 0xFD en 0xC645, debería obtener 0xFD, la comparación CP 0xFD pasará (Z=1), y el salto condicional JR NZ no se tomará, permitiendo que el juego continúe.

Fuentes Consultadas

  • Pan Docs: Memory Map - Sección "Echo RAM"
  • Análisis forense del Step 0238: Identificación de la dirección 0xE645 como causa del bucle

Integridad Educativa

Lo que Entiendo Ahora

  • Echo RAM: Es un espejo de WRAM causado por el diseño del bus de direcciones, no una región de memoria separada.
  • Uso en juegos: Los juegos a veces usan Echo RAM para verificar la integridad de la memoria, escribiendo en WRAM y leyendo desde Echo RAM.
  • Implementación: La redirección se hace simplemente restando 0x2000 a la dirección antes de acceder a la memoria.

Lo que Falta Confirmar

  • Comportamiento en hardware real: Verificar si hay alguna diferencia de timing entre acceder a WRAM directamente vs. a través de Echo RAM (probablemente no, pero es importante confirmarlo).
  • Rango exacto: Confirmar que 0xFDFF es el límite superior correcto (no 0xFE00).

Hipótesis y Suposiciones

Asumimos que la redirección de Echo RAM no tiene efectos secundarios en el timing o en otros componentes del hardware. Si el juego sigue fallando después de esta implementación, podría haber otros problemas (inicialización de WRAM, rutinas de copia, etc.).

Próximos Pasos

  • [ ] Ejecutar Tetris y verificar que sale del bucle infinito
  • [ ] Confirmar que el juego avanza a la pantalla de Copyright
  • [ ] Si el juego sigue fallando, investigar otras posibles causas (inicialización de WRAM, rutinas de copia, etc.)
  • [ ] Verificar que otros juegos que usen Echo RAM funcionen correctamente