Carga e armazenamento no ARM: LDR, STR e seu primeiro laço
No artigo anterior vimos a arquitetura ARM por cima: banco de registradores, modos e o conjunto de instruções. Mas arquitetura só "cola" quando a gente suja a mão. Neste texto vamos exercitar a parte que mais assusta quem chega do C ou do Java: como o ARM acessa memória. Spoiler — o ARM segue o modelo load/store, e isso muda tudo.
Load/store: só LDR e STR tocam a memória
Em arquiteturas CISC (x86, por exemplo) você faz add eax, [ebx] — soma direto lendo da memória. O ARM não. Todas as instruções de processamento de dados (add, sub, and, mov…) operam só entre registradores ou com immediatos. Para ler ou escrever memória existem exatamente duas portas:
LDR— load: memória → registrador.STR— store: registrador → memória.
Isso parece limitação, mas é o que torna o conjunto enxuto e o ciclo de instrução previsível — exatamente o que o arm-jitter emula quando decodifica cada opcode. Se você veio do hello world no arm-box, já usou ldr r1, =msg sem perceber: era um LDR buscando o endereço da string numa literal pool.
Os modos de endereçamento do LDR/STR
O endereço de memória é calculado a partir de um registrador base (r0 no nosso exemplo) mais um offset. Há três sabores, e vale decorar porque eles aparecem em quase todo programa real:
ldr r1, [r0] @ offset 0 (sem deslocamento)
ldr r1, [r0, #8] @ pré-indexado: usa r0+8 (r0 NÃO muda)
ldr r1, [r0, #8]! @ pré-indexado com writeback: r0 = r0+8
ldr r1, [r0], #8 @ pós-indexado: usa r0, DEPOIS r0 = r0+8 O pós-indexado ([r0], #4) é o truque que usaremos para varrer um vetor: a gente lê o elemento e já empurra o ponteiro para o próximo, numa instrução só.
Nosso objetivo: somar um vetor no arm-box
Vamos escrever um programa que soma os elementos de um vetor em .data e encerra com a soma como exit code — assim dá para conferir o resultado com echo $?. O arm-box roda binários ARMv5–ARMv7, então montamos com -march=armv5te (a mesma ISA do hello world).
.syntax unified
.text
.global _start
.data
nums:
.word 3, 7, 1, 9, 4
len = (. - nums) / 4
result:
.word 0
.text
_start:
ldr r0, =nums @ r0 = ponteiro para o vetor
mov r1, #0 @ r1 = soma acumulada
mov r2, #0 @ r2 = índice i
ldr r3, =len @ r3 = total de elementos
loop:
cmp r2, r3 @ i == len?
bge done @ sim -> termina
ldr r4, [r0], #4 @ r4 = nums[i]; r0 += 4 (próximo)
add r1, r1, r4 @ soma += nums[i]
add r2, r2, #1 @ i++
b loop @ volta
done:
ldr r5, =result
str r1, [r5] @ armazena a soma na memória (STR!)
mov r0, r1 @ código de saída = soma
mov r7, #1 @ syscall 1 = exit
svc #0Passo a passo do que está acontecendo:
len = (. - nums) / 4é calculado em tempo de link: a diferença de endereço dividida por 4 dá a quantidade de palavras de 32 bits.- O laço usa
cmp+bge(branch if greater or equal) como umwhile (i < len). ldr r4, [r0], #4é o coração do varredor: lê a palavra e já avança o ponteiro. É por isso que não precisamos der2para calcular endereços — o ponteiro anda sozinho.str r1, [r5]é o nosso únicoSTR: prova que a soma realmente voltou para a memória, não ficou só no registrador.
Montando, ligando e rodando
Geramos um ELF estático idêntico ao do hello world:
arm-none-eabi-as -march=armv5te sum.s -o sum.o
arm-none-eabi-ld -static -o sum.elf sum.o Rodamos com o --check para garantir que o JIT e o interpretador IR do arm-jitter concordam, e pedimos o código de saída:
java -jar target/armbox-1.0-SNAPSHOT.jar --check sum.elf
echo $? # 24 (3+7+1+9+4) Se aparecer 24, parabéns: você acabou de ler e escrever memória de verdade num núcleo ARM emulado em Java. O --check não reclamou, então JIT e interpretador calcularam a mesma soma.
Por que isso importa para o curso
Carga e armazenamento são a fronteira entre a CPU e o mundo exterior. Todo acesso a arrays, structs, pilha de chamadas e até argumentos de função passa por LDR/STR. Dominar os modos de endereçamento (especialmente o writeback) é o que separa quem "copia assembly de stackoverflow" de quem entende o que o processador faz. No próximo texto vamos abrir o objdump desse ELF e decodificar instruções ARM à mão, bit a bit — a mesma leitura que o decodificador do arm-jitter faz. Até lá!