Ingeniería Inversa de un juego de Game Boy Advance: Entendiendo a la traviesa DMA — Parte 2
Esta publicación es parte de una serie llamada Ingeniería Inversa de un juego de Game Boy Advance. Lee la introducción aquí. Lea la publicación anterior aquí.
Sígueme en Twitter para más diversión de computadora 🐦
Como vimos antes, alargar el puente alterando la memoria en la VRAM solo duró por un frame. ¡Vamos a entender por qué esto pasa y cómo podemos mantener nuestras tiles vivas!
El primer paso es entender mejor la sección de la memoria que estamos trabajando, en este caso VRAM (0600~0601).
Para esto, gasté un poco de tiempo “jugando” con él para ver cómo se comportaba. Por ejemplo, si almacena todo el nivel, o solo el área que el jugador está viendo, y si la VRAM almacena sólo las tiles o algo más. Por el momento, tendremos que usar nuestra creatividad y deducir, por la poca información que tenemos, que representa esta región, que propósito sirve ese byte, etcétera.
Mientras lo analizaba, vine con la conclusión que esta sección sólo almacena lo que ve el jugador ahora mismo. En otras palabras, cada frame del juego lo extrae, de otra sección que almacena el nivel entero, la porción que el jugador puede ver.
Por lo tanto, ¡tenemos que escribir nuestras tiles en la fuente que la VRAM consume!
Pero eso plantea algunas preguntas: ¿por qué funciona así? ¿Cómo se escribe en la VRAM en cada frame? ¿Y dónde está localizada la fuente con el nivel entero?
Así que estudié un poco más sobre la GBA para poder responder a estas preguntas. Estuve perdido por un tiempo, ¡hasta que eventualmente encontré un excelente manual sobre la GBA!
En él, donde explicaban cómo funcionan las tiles, decía que un buen enfoque para actualizar la VRAM, que es donde las tiles se enseñan, es usar DMA (Direct Memory Access). Es un recurso que muchas computadoras tienen, y es muy útil para copiar rápidamente una porción continua de la memoria de una sección a la otra.
Hablando más técnicamente, DMA es un recurso de hardware que permite acceso a la memoria independientemente de la CPU (sin necesidad de cierres, por ejemplo)
En práctica, una sección de la memoria almacena el nivel entero, mientras, usando DMA, los bytes necesarios son copiados a la VRAM. Esto tiene sentido, así que la VRAM solo sirve para presentar las tiles que serán enseñadas en la pantalla en ese momento, y el uso de DMA previene lag durante la transición.
Cuando digo que la DMA es muy traviesa, es porque varias veces durante el proceso de ingeniería inversa, alterará valores en una manera que es difícil de entender, y puede ser difícil saber donde vienen los nuevos valores. Hay tiempos en donde juramos que es una instrucción que escribe un cierto valor cuando en realidad es la DMA, o cuando pensamos que es la DMA cuando en realidad es una instrucción el que lo hizo.
Afortunadamente, no$gba tiene una función muy útil para el debugging de DMA, el TTY Debug Window. Enseña registros de la DMA.
Por lo tanto, cuando avanzamos un frame en el juego, conseguimos los siguientes registros.
DMA3: 03000900 0600E000 80000400
DMA3: 03001100 0600E800 80000400
DMA3: 03004DB0 0600F000 80000200
DMA3: 03004800 07000000 84000048
Varias lecturas y escrituras fueron performadas por la DMA… genial…
Y como sabemos que los bytes refiriéndome al principio del puente en 0600F1AD, la escritura más cercana a esa dirección es esta operación:
DMA3: 03004DB0 0600F000 80000200
Así es, necesitamos mirar en 03004DB0, que es la fuente de los datos de la VRAM. ¡Muy genial!
Este byte está en una sección de la memoria llamada Fast WRAM (0300:256kb), y es muy útil para almacenar datos que será leído frecuentemente — exactamente el caso cuando actualizan las tiles cada frame. También está llamada IWRAM, pero yo continuaré usando Fast WRAM, que es el término usado en no$gba.
Yendo a esta sección de la memoria, lo primero que podemos ver es que se parece mucho a un tilemap, ya que hay un montón de ceros y bytes continuos de los mismos valores. Y nota: yendo un poco más abajo, encontramos nuestro puente, ¡empezando en la dirección 030045FD!
¿Alargar nuestro puente funcionará aquí? Vamos a intentarlo…
¡Y mira, funcionó! ¡Nuestro puente está alargado! ¡La DMA ahora copia los valores que escribimos desde Fast WRAM a VRAM en cada frame, así que el puente se mantiene vivo!
Sin embargo, tenemos un problema inesperado… ¡es un “fantasma”! Es visible, pero las físicas no están aplicadas a eso, lo que significa que el personaje pasa a través de él. Que bizarro.
¿Por qué son las tiles que creamos visibles pero sin físicas, apareciendo como fantasmas? ¿Y como podemos aplicar físicas a ellos?
Siguiente publicación: Entendiendo las físicas del juego (disponible cuando la traducción esté terminada)
Read it in English here.