← cd ../

~/labs/workshops/gba-firered/01-arquitetura-do-gba

A arquitetura do GBAO hardware, a CPU ARM7TDMI e o mapa de memoria do console.

Antes de abrir qualquer ferramenta, você precisa de um modelo mental do console. Engenharia reversa fica muito mais fácil quando você sabe onde o código mora, onde os dados moram e como a CPU enxerga tudo isso.

O hardware em uma frase

O Game Boy Advance é um computador pequeno com um processador ARM7TDMI de 32 bits rodando a cerca de 16,78 MHz, com pouca memória, uma tela de 240 por 160 pixels e um cartucho que contém a ROM do jogo.

CPU      ARM7TDMI, 32 bits, ~16.78 MHz
Tela     240 x 160 pixels
EWRAM    256 KB de RAM "externa" (mais lenta)
IWRAM    32 KB de RAM "interna" (mais rápida)
ROM      o cartucho, até 32 MB, somente leitura

O processador ARM7TDMI

Toda a lógica do jogo é executada por essa CPU. Ela tem características que definem tudo o que vamos fazer depois:

- É RISC. As instruções são simples e de tamanho fixo.
- É little endian. O byte menos significativo vem primeiro na memória.
- Tem 16 registradores visíveis de uso geral e de controle.
- Executa dois conjuntos de instruções: ARM (32 bits) e Thumb (16 bits).

O capítulo 02 trata só de ARM e Thumb, porque é ali que mora a parte que assusta no começo. Por enquanto, guarde uma ideia: o jogo é uma sequência gigante dessas instruções, e a CPU lê uma de cada vez.

O mapa de memória

A CPU acessa tudo por endereços. Cada tipo de memória fica em uma faixa fixa de endereços. Esse é o mapa que você vai usar o tempo todo:

0x00000000  BIOS         código interno do console
0x02000000  EWRAM        RAM de trabalho, 256 KB
0x03000000  IWRAM        RAM rápida, 32 KB
0x04000000  registradores de hardware (vídeo, som, input, DMA, timers)
0x05000000  Palette RAM  cores
0x06000000  VRAM         dados de vídeo (tiles, mapas, bitmaps)
0x07000000  OAM          atributos dos sprites
0x08000000  ROM          o cartucho do jogo, mapeado aqui

Duas faixas importam mais para este workshop:

0x08000000  é onde a ROM do jogo aparece. Código e dados fixos.
0x02000000  é onde fica o estado atual do jogo. HP, posição, batalha.

A regra geral é simples de lembrar:

ROM (0x08...)   = o que o jogo é. Não muda enquanto joga.
EWRAM (0x02...) = o que está acontecendo agora. Muda a cada frame.

Quando a gente quer entender a lógica, olha a ROM. Quando a gente quer achar um valor vivo como o HP, procura na EWRAM. Esses dois mundos se encontram no momento em que uma instrução da ROM escreve um valor na EWRAM. Achar esse momento é o coração da engenharia reversa de jogos.

A ROM mapeada em 0x08000000

Este é o ponto que mais confunde no começo, então vale parar nele.

O arquivo PokemonFireRed.gba no seu disco começa no offset 0. Mas quando o console liga, ele coloca esse arquivo inteiro a partir do endereço 0x08000000. Então o byte 0 do arquivo vira o endereço 0x08000000, o byte 0x100 do arquivo vira 0x08000100, e assim por diante.

A conta para ir de um para o outro é direta:

endereço na memória = offset no arquivo + 0x08000000
offset no arquivo    = endereço na memória - 0x08000000

Exemplo:

endereço 0x08012345  ->  offset 0x00012345 no arquivo

Você vai usar isso em todo patch. O Ghidra mostra endereços como 0x08xxxxxx, mas para editar o arquivo no disco você precisa do offset, que é o endereço menos 0x08000000.

Olhando os primeiros bytes da ROM

Dá para confirmar tudo isso sem ferramenta nenhuma, só com xxd. Os primeiros quatro bytes da ROM são o primeiro opcode que a CPU executa:

xxd -g 1 -l 4 rom/PokemonFireRed.gba

Saída:

00000000: 7f 00 00 ea

Esses quatro bytes, lidos como uma instrução ARM em little endian, viram um salto. No capítulo 02 a gente decodifica isso byte a byte. Por enquanto, confie: isso é a instrução b 0x08000204, um branch que pula o cabeçalho e entra no código de verdade.

A ROM também guarda um cabeçalho com o nome do jogo. Ele fica no offset 0xA0:

xxd -g 1 -s 0xa0 -l 32 rom/PokemonFireRed.gba

Saída, lendo a coluna ASCII à direita:

POKEMON FIRE
BPRE

O código BPRE identifica Pokémon FireRed USA. Se a sua ROM mostra isso, ela é a versão certa para o workshop.

Vídeo e sprites, por cima

Você não precisa dominar o vídeo do GBA para fazer os patches deste workshop, mas vale saber o nome das coisas para não se perder quando aparecerem:

Backgrounds  camadas de fundo, montadas com tiles guardados na VRAM.
Sprites      os objetos que se movem (o jogador, os Pokémon na batalha).
OAM          a lista que diz onde cada sprite está na tela.
Palette RAM  as cores usadas pelos tiles e sprites.

A tela é redesenhada de cima para baixo, várias vezes por segundo. No fim de cada desenho completo existe um pequeno intervalo chamado VBlank. O jogo usa esse momento para atualizar gráficos com segurança. Por isso o loop principal do jogo termina sempre esperando o VBlank, como você vai ver no capítulo 04.

Interrupções, por cima

Uma interrupção é um aviso do hardware para a CPU de que algo aconteceu, por exemplo “terminei de desenhar a tela” ou “o jogador apertou um botão de link”. A CPU para o que está fazendo, atende o aviso em uma rotina específica e depois volta.

No GBA, a rotina que atende esses avisos no FireRed fica perto do início da ROM e no mapa do workshop ela se chama intr_main. Você não precisa reverter ela inteira. O que importa entender é que ela existe para sincronizar vídeo, som, input e timers.

Por que FireRed é um bom alvo

Vale fechar o capítulo com o motivo da escolha:

ARM/Thumb   bem suportado pelo Ghidra, então o disassembly sai limpo.
ROM direta  menos complexidade de boot do que um N64, por exemplo.
Dados claros strings, tabelas de Pokémon, itens e mapas legíveis.
Feedback rápido andar na grama, abrir loja, lutar. Dá para testar o patch na hora.
Debugger bom o mGBA tem servidor de debug que conversa com o GDB.

O que levar deste capítulo

- A CPU é um ARM7TDMI little endian de 32 bits.
- Cada tipo de memória mora em uma faixa fixa de endereços.
- A ROM aparece em 0x08000000. O estado do jogo mora na EWRAM, em 0x02000000.
- offset no arquivo = endereço - 0x08000000.
- O jogo é uma sequência de instruções ARM e Thumb. É o próximo capítulo.