Engenharia reversa no GBA: Entendendo o sapeca DMA — Parte 2
Essa postagem faz parte da série intitulada Engenharia Reversa num jogo de Gameboy Advance. Leia a introdução aqui. Leia a postagem anterior aqui.
Me siga no Twitter para acompanhar mais computarias 🐦
Como vimos anteriormente, o esticamento que fazemos na ponte alterando a memória na VRAM dura apenas um único frame. Vamos entender como isso acontece e como podemos manter os nossos tiles vivos!
A primeira coisa a ser feita é entender melhor a região da memória em que estamos mexendo, no caso, a VRAM (0600~0601).
Para isso, fiquei “jogando” e vendo como ela se comportava. Por exemplo, se ela armazenava toda a fase ou apenas a região que o jogador está visualizando, e se a VRAM armazena somente os tiles ou algo além.
Nesse momento, devemos usar a criatividade e assim deduzir, com as poucas informações que temos, o que aquela região representa, para que aquele byte serve, e assim por diante.
Ao analisar, cheguei a uma boa conclusão de que aquela região basicamente armazena o que o jogador está visualizando no momento. Ou seja, a cada frame do jogo ele puxa, a partir de alguma outra região que armazena a fase inteira, somente a região em que o jogador deve visualizar. Assim, precisamos escrever os nossos tiles na fonte que a VRAM consome!
Mas assim criamos algumas perguntas: por que ele funciona desse modo? Como ele efetua essa escrita na VRAM a cada frame? E onde fica a tal fonte com a fase inteira?
Então fui estudar um pouco mais de GBA para poder responder essas perguntas. Fiquei um bom tempo perdido, até enfim encontrar um excelente manual sobre o GBA!
Nele, ao explicar sobre o funcionamento dos tiles, ele diz que uma boa estratégia para atualizar a VRAM, que é onde exibe os tiles, seria o uso do DMA (Direct Memory Access). Isso é um recurso presente em muitos computadores, da qual é muito útil para copiar rapidamente uma porção contínua da memória de uma região para outra região.
Falando mais tecnicamente, o DMA é um recurso de hardware que permite acessar a memória independentemente da CPU (sem precisar de locks, por exemplo).
Na prática, uma região da memória armazena toda a fase, enquanto, usando o DMA, copia os bytes necessários para a VRAM.
O que faz sentido, pois a VRAM serve unicamente para expor os tiles que serão exibidos na tela naquele momento, e o uso do DMA faz com que não fique com lag essa transição.
Quando digo que o DMA é bem sapeca é pelo fato de que, muitas vezes durante o processo de engenharia reversa, ele vai alterar valores de forma com que seja difícil acompanhar, além de também poder ficar difícil saber de onde veio tal valor. Tem momentos que juramos que é uma instrução que escreveu um determinado valor, mas na verdade é o DMA, ou achamos que foi o DMA, mas é uma instrução que fez.
Felizmente, o no$gba tem uma feature muito útil para debugar o DMA, o TTY Debug Window. Ele exibe os logs do DMA.
Assim, ao rodar um único frame do jogo, obtemos os seguintes logs:
DMA3: 03000900 0600E000 80000400
DMA3: 03001100 0600E800 80000400
DMA3: 03004DB0 0600F000 80000200
DMA3: 03004800 07000000 84000048
Várias leituras e escritas foram feitas no DMA… bacana…
E como sabemos que os bytes referentes à ponte começam em 0600F1AD, o mais próximo dele, sem ultrapassar, seria essa operação:
DMA3: 03004DB0 0600F000 80000200
Ou seja, precisamos ver o que tem a partir do 03004DB0, que é a fonte de dados da VRAM. Muito legal!
Esse byte está na região de memória chamada de Fast WRAM (0300:256kb), e é muito útil para armazenar dados que serão lidos várias vezes — exatamente o caso de uso de atualizar os tiles a cada frame. Ela também é chamada de IWRAM, mas aqui continuarei usando apenas Fast WRAM, que é o termo usado no no$gba.
Indo para essa região da memória, a primeira coisa que conseguimos perceber é que realmente se parece com um tilemap, pois há uma grande presença de zeros e bytes de mesmo valores continuamente. E perceba: olhando um pouco mais para baixo, encontramos a nossa ponte, começando no endereço 03004F5D!
Será que se esticarmos ela aqui funcionaria? E então isso foi feito…
E veja só, deu certo! A ponte foi esticada!! O DMA agora copia para a VRAM a cada frame os valores que escrevemos na Fast WRAM. Desse modo, a ponte permanecendo viva!
Porém, temos um inesperado problema… ela é “fantasma”! Visualmente ela aparece, mas ela não aplica a física, fazendo com que o personagem a cruze totalmente!! Que bizarro.
Por qual razão o tile que criamos são exibidos, porém, não tem física, parecendo fantasmas? E como podemos aplicar física neles?