ALAN JAHNEL / Engenharia de software

Artigos

GBA em Assembly — 1. Como o hardware funciona

Comece do zero: entenda CPU, ROM, RAM e o mapa de memória do Game Boy Advance antes de escrever as primeiras instruções.

Você aperta uma tecla, um personagem anda e a tela muda. Em um jogo feito para navegador, uma biblioteca pode esconder quase todo o caminho entre essas três coisas. No Game Boy Advance, vamos enxergar esse caminho: a CPU lê os botões, calcula uma posição, grava números na memória de vídeo e o circuito de vídeo transforma esses números em imagem.

Esta série parte de noções de variáveis, condições e laços. Você não precisa conhecer eletrônica, Assembly ou arquitetura de computadores. Vamos aprender cada conceito com um exemplo pequeno e chegar a um minijogo de levar um quadrado até um alvo. Essa base prepara a leitura do artigo principal de Tower Defense.

Durante a leitura, use a referência de instruções e sintaxe. Ela reúne os comandos, as condições e as diretivas em um artigo de consulta; os capítulos também apontam diretamente para as seções usadas em cada exemplo.

Como acompanhar este curso

Você só precisa reconhecer uma variável, uma condição if e um laço. Vamos apresentar o vocabulário de hardware e Assembly conforme ele aparecer. Não é necessário conhecer eletrônica ou ter desenvolvido para consoles.

  1. Leia os capítulos 1 e 2 com um caderno. Os blocos curtos de código são exercícios para acompanhar valores; ainda não é necessário instalar ou executar nada.
  2. No capítulo 3, instale as ferramentas, prepare três arquivos e construa sua primeira ROM. Aqui, ROM é o arquivo do jogo aberto em um emulador, programa que imita o console no seu computador.
  3. Nos capítulos 4 a 8, abra o arquivo .s completo do capítulo, construa e confira o resultado descrito antes de editar. Cada arquivo é um programa independente. As rotinas citadas explicam partes desse arquivo; não são arquivos para substituí-lo.
  4. Faça uma pequena mudança, salve, reconstrua e compare o resultado com a previsão. Guarde uma cópia do último código que funcionou para poder desfazer a alteração.
  5. Use o capítulo 9 para conferir sua compreensão antes de começar o Tower Defense. A referência de instruções serve para consultar um comando quando ele aparecer; você não precisa decorá-la antes.

O caminho obrigatório termina no ponto de conferência de cada capítulo. As experiências extras são opcionais. Se uma conferência falhar, resolva antes de acrescentar o próximo conceito: os exemplos seguintes se apoiam na explicação que você acabou de usar.

Qual Game Boy estamos programando?

O alvo é o Game Boy Advance, ou GBA, o mesmo do tutorial de Tower Defense deste site. O Game Boy clássico e o Game Boy Color usam outra CPU e outro conjunto de instruções. Um programa ARM deste curso não funciona neles. A compatibilidade do GBA com jogos antigos não torna os dois ambientes de programação iguais.

No GBA, nossa CPU é a ARM7TDMI. Vamos usar seu conjunto de instruções ARM de 32 bits. Ela também oferece Thumb, com instruções menores, mas misturar os dois agora acrescentaria uma dificuldade desnecessária. “Assembly” é o nome de uma família de linguagens próximas das instruções das CPUs; não existe um Assembly universal.

O percurso de um programa

Uma CPU executa instruções representadas por números. Uma instrução pode somar dois valores; outra pode ler um endereço de memória; outra pode mudar o ponto da execução. O texto add r1, r1, #1 é uma forma legível de pedir uma dessas operações. Um programa chamado assembler traduz esse texto para a representação que a CPU executa.

O nosso arquivo .gba será uma imagem de ROM: uma sequência de bytes organizada como o conteúdo de um cartucho. No console, a inicialização passa pelo programa interno de boot antes de entrar no cartucho. No emulador, esse processo pode ser reproduzido ou parcialmente substituído. Por isso, abrir uma ROM no emulador é uma verificação útil, mas não comprova funcionamento em um console físico.

Depois da entrada no nosso código, não há um navegador chamando uma função de atualização. Somos responsáveis por preparar a memória que usamos e por escrever o laço que mantém o jogo funcionando.

CPU, ROM e RAM têm trabalhos diferentes

Pense em uma receita, uma bancada e nas pequenas anotações que você mantém enquanto cozinha. A ROM se parece com a receita: contém instruções e dados que o nosso programa vai consultar. A RAM se parece com a bancada: guarda o estado que muda durante a partida. Os registradores da CPU se parecem com as anotações que você está usando naquele instante.

A analogia tem limites. Os registradores são espaços de armazenamento dentro da CPU; a RAM fica em outras regiões do sistema. Para calcular algo com uma variável da RAM, normalmente precisamos carregá-la em um registrador, operar e gravar o resultado de volta.

PeçaPapel neste jogoExemplo
CPUExecutar instruçõesSomar um à posição do inimigo
ROM do cartuchoConservar código e constantesRotina que calcula dano
RAM de trabalhoGuardar o estado da partidaVida da base e saldo de moedas
Memória de vídeo, VRAMGuardar dados gráficosCores dos pixels no modo escolhido
Registradores de entrada e saídaExpor controles do hardwareLer botões ou selecionar um modo de vídeo
Circuito de vídeoProduzir a imagem a partir da configuração e dos dadosExibir o tabuleiro

O hardware não sabe o que é uma torre, uma moeda ou uma vitória. Esses significados pertencem ao nosso programa.

O caminho entre o código e a tela

Diagrama do GBA: a CPU lê código da ROM e troca estado com a RAM; consulta botões por KEYINPUT, escreve cores na VRAM e configura o vídeo por DISPCNT; o circuito de vídeo produz a imagem na tela

Leia as setas como o caminho dos dados nos nossos exemplos. A seta dupla da RAM indica leitura e escrita: a CPU consulta a posição do jogador, calcula uma nova posição e a guarda de volta. A ROM fornece instruções e constantes; não é onde salvamos o estado que muda durante a partida.

Os botões chegam ao programa por KEYINPUT. Para mostrar o resultado de uma ação, a CPU escreve as cores na VRAM. Ela também escolhe como o vídeo deve interpretar esses dados por meio de DISPCNT. O circuito de vídeo usa a configuração e os dados gráficos para produzir a imagem da tela.

O desenho é um mapa conceitual desse uso do hardware, não um esquema dos fios e barramentos do console. Outros acessos são possíveis, e a forma de organizar os dados gráficos muda conforme o modo de vídeo. A seguir vamos colocar endereços nas peças que o programa acessa.

Endereços são a ponte entre Assembly e hardware

Para a CPU, várias dessas peças podem ser acessadas por endereços. Um endereço identifica um lugar no mapa de memória. Não significa necessariamente uma célula de RAM: certas faixas correspondem a controles do hardware.

Endereço inicialRegiãoComo vamos usá-la
0x02000000EWRAM, 256 KiB de RAM de trabalhoVariáveis da partida
0x03000000IWRAM, 32 KiB de RAM internaPilha de chamadas
0x04000000Registradores de entrada e saída, ou I/OVídeo e botões
0x05000000Memória de paletasFica para uma evolução dos gráficos
0x06000000VRAM, 96 KiBImagem do nosso exemplo
0x07000000OAM, atributos de objetos gráficosFica para a introdução a sprites
0x08000000Primeira região de acesso à ROM do cartuchoCódigo e constantes

0x indica um número escrito em hexadecimal. Vamos aprender essa notação no próximo capítulo. Por enquanto, trate os números da tabela como endereços de uma rua: mudar de endereço pode significar falar com outra peça.

Este é um recorte do mapa. Existem outras regiões, espelhamentos e restrições de acesso. Não vamos supor que qualquer endereço possa ser lido ou escrito livremente.

Dois sentidos para a palavra registrador

Para consultar a notação do exemplo, veja registradores da CPU e LDR, STR e a diferença entre endereço e conteúdo na referência.

Essa distinção evita muita confusão. r0 é um registrador da CPU: pode guardar temporariamente um número ou um endereço. DISPCNT é um registrador de controle do vídeo, acessado pelo endereço 0x04000000: seus bits configuram o circuito de vídeo.

Neste trecho, ainda apenas para leitura, estamos preparando uma configuração:

Assembly ARM
ldr r0, =0x04000000
ldr r1, =0x0403
strh r1, [r0]

A primeira linha coloca o endereço de DISPCNT em r0. A segunda coloca o valor desejado em r1. A terceira escreve 16 bits desse valor no endereço guardado em r0. O circuito de vídeo interpreta esses bits como uma configuração. Não chamamos uma função de desenho: realizamos uma escrita em um endereço com significado para o hardware.

Em outro endereço, a mesma instrução strh poderia escrever uma cor ou alterar uma variável. A instrução descreve a operação; o endereço determina com qual parte do sistema ela interage.

O que vamos construir, em ordem

CapítuloPergunta que vamos responderExperiência ou resultado
1. Este artigoComo o programa alcança o hardware?Identificar CPU, memória e I/O
2. Bits, registradores e memóriaO que uma instrução realmente modifica?Acompanhar valores passo a passo
3. Ambiente e primeira ROMComo o texto vira um cartucho executável?Tela vermelha com um pixel branco
4. Pixels e cenárioComo coordenadas viram endereços na VRAM?Retângulo verde sobre fundo preto
5. Tempo e VBlankComo acompanhar o ritmo da tela?Uma atualização por quadro
6. Ler os botõesComo o hardware representa um botão?Quadrado que muda de cor ao apertar A
7. Mover um quadradoComo unir entrada, posição e imagem?Quadrado controlado pelo direcional
8. Primeiro minijogoComo pequenas regras formam um jogo?Alcançar um alvo, vencer e reiniciar
9. Depuração e próximos passosComo descobrir por que algo falhou?Inspecionar uma ROM e planejar melhorias

Os dois primeiros capítulos são de fundamentos. Do terceiro em diante, vamos executar os exemplos. O pacote de apoio contém seis programas independentes: primeira tela, retângulo, pisca, botões, movimento e minijogo. Cada arquivo .s é completo e usa apenas o linker script e a ferramenta de cabeçalho incluídos. Você pode escrever os trechos estudados e comparar com o arquivo pronto, sem depender de funções escondidas.

O código do Tower Defense vem da versão v0.3.0 do projeto Gateblade e recebeu apoio de IA. Os seis exemplos desta série foram preparados separadamente para ensinar um conceito por vez. Os testes se concentram em emulação; não foi realizado teste em GBA físico.

Antes de continuar

Tente responder: quem decide que o inimigo perdeu vida? Quem guarda esse valor entre duas atualizações? Quem transforma as cores gravadas em uma imagem?

As respostas são, respectivamente, nosso código executado pela CPU, a RAM e o circuito de vídeo. Se você consegue separar esses três trabalhos, já tem o modelo necessário para começar.

Para consultar o mapa e as características da máquina, use Tonc: hardware do GBA. A introdução do Tonc a Assembly ARM será uma referência complementar ao longo da série.

Próximo: bits, registradores e memória, sem pular as primeiras instruções.

Voltar ao início