ALAN JAHNEL / Engenharia de software

Artigos

GBA em Assembly — 6. Lendo os botões

Leia KEYINPUT, interprete bits ativos em zero e faça um quadrado mudar de cor. Aprenda a distinguir apertar de continuar segurando.

Até agora, o programa fazia tudo sozinho. Vamos permitir uma ação do jogador: o quadrado fica verde quando A está solto e vermelho enquanto A está pressionado. Esse exemplo é pequeno o suficiente para enxergar todo o caminho entre um botão físico e os pixels.

Use botoes.s, com gba.ld e rom.py do pacote da série. O programa mantém a espera de VBlank do capítulo anterior.

Roteiro deste capítulo

Antes de começar: o exemplo de pisca do capítulo 5 deve funcionar. Agora você vai trocar a mudança automática de cor por uma ação do jogador.

  1. Salve botoes.s completo ao lado dos arquivos de apoio. Construa com os quatro comandos abaixo e abra botoes.gba.
  2. No mGBA, confira qual tecla representa A do GBA. Dê foco à janela do jogo antes de testar; a tecla do computador e o botão do console podem ter nomes diferentes.
  3. Teste em ordem: nenhum botão, segurar A, soltar A. Espere verde, vermelho, verde. Só depois acompanhe read_keys e o laço principal abaixo.
  4. Na primeira edição, localize tst r1, #1 em main_loop e troque a máscara por 2. Salve, reconstrua e confira que B agora controla a cor. Não altere o teste semelhante dentro de read_keys.

Ponto de conferência: você consegue prever a cor ao segurar e soltar o botão escolhido. Se nada mudar, confira o foco e o mapeamento do emulador antes de alterar Assembly. Inspecionar press_count em um depurador é opcional; o teste visual da cor é suficiente para continuar.

O que existe no lugar de um evento de teclado

No navegador, você pode receber um evento quando uma tecla muda. Neste exemplo do GBA, a CPU consulta o registrador de hardware KEYINPUT, no endereço 0x04000130. Os dez bits inferiores representam os dez botões do console.

BitMáscara decimalBotão
01A
12B
24Select
38Start
416Direita
532Esquerda
664Cima
7128Baixo
8256R
9512L

Há um detalhe: esses bits são ativos em zero. Um bit zero significa botão pressionado; um significa botão solto. Isso é uma convenção do hardware, não um comportamento da linguagem Assembly.

Com nenhum botão pressionado, os dez bits relevantes valem 0x03FF. Com somente A pressionado, valem 0x03FE. Esses exemplos consideram apenas os dez bits dos controles; os demais não participam da nossa interpretação.

Normalizar a leitura uma vez

Veja LDRH e as larguras de leitura e MVN, AND e BIC para interpretar a rotina.

A rotina do exemplo lê o hardware e guarda uma versão mais conveniente para o restante do programa:

<!-- source: botoes.s#input -->
Assembly ARM
read_keys:
    ldr r0, =KEYINPUT
    ldrh r1, [r0]
    mvn r1, r1
    ldr r0, =0x03ff
    and r1, r1, r0
    ldr r0, =keys
    ldr r2, [r0]
    str r1, [r0]
    bic r1, r1, r2
    ldr r0, =pressed
    str r1, [r0]
    tst r1, #1
    ldr r0, =press_count
    ldr r2, [r0]
    addne r2, r2, #1
    str r2, [r0]
    bx lr

mvn inverte os bits: o zero de um botão pressionado vira um. Como o registrador da CPU tem 32 bits, a inversão também afeta bits que não representam botões. Por isso aplicamos a máscara 0x03FF com and.

Depois dessa normalização, o valor em keys tem um bit um para cada botão pressionado. É uma variável na RAM. Nosso programa lê o hardware uma vez por atualização e usa essa fotografia durante aquela volta do laço.

Texto simples
KEYINPUT do hardware     -> inverter e limitar a dez bits
keys na RAM              -> 1 significa pressionado

Esse é um bom exemplo de uma camada pequena de software que torna o hardware mais fácil de usar sem esconder seu funcionamento.

Segurar e apertar são informações diferentes

A fórmula das novas pressões corresponde ao BIC, ou AND com a máscara invertida.

Se o jogador segurar A por doze quadros, keys terá o bit de A ligado nos doze. Isso é exatamente o que precisamos para manter o quadrado vermelho. Mas seria inadequado para uma ação que deve acontecer apenas uma vez, como comprar uma torre.

Para identificar a transição, conservamos a leitura anterior. No código, r2 recebe o valor antigo de keys antes de gravarmos o novo. Então:

Texto simples
pressed = atual AND (NOT anterior)

bic r1, r1, r2 faz essa operação: mantém em r1 os bits que estão ligados agora e não estavam ligados antes. Guardamos o resultado em pressed.

QuadroA agoraA antesA em pressed
Antes de apertar000
Primeiro quadro pressionado101
Continuando a segurar110
Soltou010
Apertou novamente101

O exemplo também aumenta press_count apenas quando A aparece em pressed. Esse contador não é desenhado na tela: ele serve para inspeção no depurador e para os testes. Segurar A deve manter a cor vermelha, mas aumentar o contador uma única vez.

Usar os bits para escolher a cor

Consulte TST e o sufixo condicional NE para acompanhar ldrne.

O laço que liga a entrada à imagem é:

<!-- source: botoes.s#button-demo -->
Assembly ARM
main_loop:
    bl wait_vblank
    bl tick
    bl read_keys
    ldr r0, =keys
    ldr r1, [r0]
    tst r1, #1
    ldr r4, =0x03e0
    ldrne r4, =0x001f
    mov r0, #104
    mov r1, #64
    mov r2, #32
    mov r3, #32
    bl fill_rect
    b main_loop

Após ler os botões, testamos a máscara 1 em keys. Escolhemos verde por padrão e vermelho se o resultado não for zero. Chamamos a mesma fill_rect dos capítulos anteriores.

Observe a divisão: o registrador de entrada informa um estado elétrico dos controles; read_keys transforma a leitura em bits úteis; o laço decide a cor; a escrita na VRAM altera o que o circuito de vídeo exibe. O hardware não possui uma regra “A deixa o quadrado vermelho”. Nós escrevemos essa associação.

Executar e verificar as duas situações

Shell
arm-none-eabi-as -mcpu=arm7tdmi -g -o botoes.o botoes.s
arm-none-eabi-ld -T gba.ld -o botoes.elf botoes.o
arm-none-eabi-objcopy -O binary botoes.elf botoes.gba
python3 rom.py botoes.gba

Abra botoes.gba. Nas configurações de controles do mGBA, confira qual tecla está associada ao botão A do GBA. Ela não precisa ser a letra A do teclado. Clique na janela do emulador para garantir que ela recebe a entrada.

Sem apertar nada, o quadrado deve estar verde. Segure A: ele deve ficar vermelho e permanecer assim. Solte: deve voltar a verde. Pressionar outro botão isoladamente não deve mudar a cor.

Como exercício, mude somente a máscara usada para escolher a cor, de 1 para 2. Agora B deve controlar a cor. O contador de novas pressões continuará contando A, pois sua máscara está dentro de read_keys. Mudar a leitura visual não muda automaticamente todas as regras do programa.

Outra experiência é usar pressed no lugar de keys na escolha de cor. O vermelho deve durar apenas uma atualização por aperto, mesmo que A continue segurado. Pode ser rápido demais para perceber claramente; o contador ou um teste quadro a quadro ajudam a observar essa diferença.

Para consultar o formato e as transições dos controles, veja Tonc: keypad.

Anterior: tempo e VBlank. Próximo: usar o direcional para mover um quadrado. Índice da série.

Voltar ao início