Registradores e CPSR: banking, modos e SPSR
Os 16 registradores visíveis
Em qualquer instante, o programa enxerga R0–R15, todos de 32 bits:
| Registrador | Apelido | Papel |
|---|---|---|
| R0–R3 | — | argumentos / retorno de função (AAPCS) |
| R4–R11 | — | variáveis locais; callee-saved |
| R12 | ip | intra-procedimento |
| R13 | sp | ponteiro de pilha |
| R14 | lr | link register — endereço de retorno gravado por BL/BLX |
| R15 | pc | program counter |
Não há registrador de flags entre esses 16 — as flags moram no CPSR, um registrador à parte, acessível só por MRS/MSR e implicitamente pelas instruções com sufixo S e pelos desvios condicionais.
O PC não aponta para "a instrução atual"
Herança do pipeline clássico de 3 estágios: ler R15 devolve o endereço da instrução em execução + 8 em estado ARM (+ 4 em Thumb). Por isso adr r1, msg no primeiro artigo virou add r1, pc, #20 e não #28 — o montador já descontou o offset do pipeline. Escrever em R15 é um desvio.
O CPSR, bit a bit
O Current Program Status Register carrega o estado de execução. Os campos que importam nesta trilha:
| Bits | Campo | Significado |
|---|---|---|
| 31 | N | resultado negativo (bit 31 do resultado) |
| 30 | Z | resultado zero |
| 29 | C | carry / borrow / último bit deslocado |
| 28 | V | overflow aritmético com sinal |
| 27 | Q | saturação "pegajosa" (DSP/saturating); só limpa com MSR |
| 26–25, 15–10 | IT[7:0] | estado do bloco IT (Thumb-2) — dividido em dois pedaços |
| 19–16 | GE[3:0] | greater-or-equal por lane (SIMD paralelo do ARMv6) |
| 9 | E | endianness de dados (0 = little) |
| 8 | A | mascara aborts assíncronos |
| 7 | I | mascara IRQ |
| 6 | F | mascara FIQ |
| 5 | T | estado Thumb (1) ou ARM (0) |
| 4–0 | M[4:0] | modo de processador (ver tabela adiante) |
O bit T e o bit 0 do endereço em BX/BLX são a mesma decisão vista de dois ângulos: pular para um endereço ímpar entra em Thumb e liga o T; par entra em ARM.
Modos de processador e register banking
O ARM tem modos de execução. Cada exceção entra em um modo específico, e alguns registradores são bancados: R13/R14 (e, no FIQ, também R8–R12) têm cópias físicas distintas por modo. Trocar de modo troca qual R13 você enxerga — sem salvar nada explicitamente.
| Modo | M[4:0] | Entra quando | Bancados além de R0–R12/R15 |
|---|---|---|---|
| User | 10000 | execução normal | — (é o banco base) |
| System | 11111 | igual User, mas privilegiado | compartilha o banco de User |
| FIQ | 10001 | interrupção rápida | R8–R12, R13, R14, SPSR |
| IRQ | 10010 | interrupção normal | R13, R14, SPSR |
| Supervisor | 10011 | reset e SVC | R13, R14, SPSR |
| Abort | 10111 | falha de memória (data/prefetch abort) | R13, R14, SPSR |
| Undefined | 11011 | instrução indefinida / UDF | R13, R14, SPSR |
| Monitor | 10110 | SMC (TrustZone, ARMv6K+) | R13, R14, SPSR |
| Hyp | 11010 | Hypervisor (ARMv7VE) | R13, ELR_hyp, SPSR (LR é compartilhado com User) |
R0–R7 e R15 (PC) nunca são bancados. É por isso que o handler de exceção pode ler os argumentos que estavam em R0–R3 sem mais cerimônia, mas precisa cuidar de R13/R14.
O SPSR
Ao entrar num modo de exceção, o hardware copia o CPSR de origem para o SPSR (Saved PSR) daquele modo, ajusta o CPSR (novo modo, mascara IRQ, etc.) e grava o endereço de retorno em R14. Para voltar, o handler restaura o CPSR a partir do SPSR e o PC a partir de R14 — tipicamente com SUBS pc, lr, #offset ou RFE. Os modos User e System não têm SPSR (não são alvos de exceção).
Como o arm-jitter modela isso
O núcleo (dev.vitorsilverio.armjitter.core) tem uma classe para cada peça:
CpuMode — o enum dos modos
Os cinco bits de modo, com o encoding real embutido:
public enum CpuMode {
USER(0b10000),
FIQ(0b10001),
IRQ(0b10010),
SUPERVISOR(0b10011),
ABORT(0b10111),
UNDEFINED(0b11011),
SYSTEM(0b11111),
HYP(0b11010),
MONITOR(0b10110);
public int bits() { /* ... */ }
public static CpuMode fromBits(int bits) { /* lookup O(1) */ }
}CpsrRegister — o status register
As máscaras são constantes públicas, e o acesso é tipado:
public static final int NEGATIVE_FLAG = 1 << 31;
public static final int ZERO_FLAG = 1 << 30;
public static final int CARRY_FLAG = 1 << 29;
public static final int OVERFLOW_FLAG = 1 << 28;
public static final int THUMB_FLAG = 1 << 5;
public static final int IRQ_DISABLE_FLAG = 1 << 7;
int get();
void set(int value);
boolean isThumbMode();
CpuMode mode();
void setMode(CpuMode mode);
void setNzcv(boolean n, boolean z, boolean c, boolean v);
boolean evalCond(Condition condition); // usado pela predicação
boolean carry(); // e negative()/zero()/overflow()
int itState(); // reconstrói IT[7:0] dos dois pedaçosO evalCond(Condition) é o coração da execução condicional: antes de executar qualquer instrução ARM, o interpretador consulta a condição de 4 bits contra as flags atuais.
ArmCore — o banco de registradores
int register(int index);
void setRegister(int index, int value);
int programCounter();
void setProgramCounter(int value);
CpsrRegister cpsr();
CpuMode mode();
int bankedRegister(CpuMode mode, int register); // lê o R13/R14 de OUTRO modo
void setBankedRegister(CpuMode mode, int register, int value);
int spsr(CpuMode mode);
void setSpsr(CpuMode mode, int value);
void switchMode(CpuMode mode); // troca o banco visívelregister(13) sempre devolve o SP do modo atual; bankedRegister(CpuMode.IRQ, 13) devolve o SP_irq mesmo estando em User. É exatamente essa indireção que o silício faz em hardware.
Mão na massa: lendo o CPSR
Um programa que fotografa o CPSR em três momentos — cpsr.s (.elf pré-montado):
.syntax unified
.arch armv7-a
.arm
.text
.global _start
_start:
mrs r0, cpsr @ r0 = CPSR inicial
movs r1, #0 @ resultado 0 -> Z <- 1
mrs r2, cpsr @ r2 = CPSR com Z setado
subs r3, r1, #1 @ 0 - 1 -> N <- 1, Z <- 0
mrs r4, cpsr @ r4 = CPSR com N setado
mov r0, #0 @ exit code 0
mov r7, #1 @ NR_exit
svc #0Montado, ligado e desmontado com o toolchain arm-none-eabi:
arm-none-eabi-as -march=armv7-a -o cpsr.o cpsr.s
arm-none-eabi-ld -Ttext=0x10000 -o cpsr.elf cpsr.o
arm-none-eabi-objdump -d cpsr.elfAgora sob o arm-box com o stub GDB (--gdb=PORT, ver o artigo de depuração):
# terminal 1 — sobe o emulador e trava esperando o gdb
java -jar target/armbox.jar --arch=armv7a --gdb=3333 cpsr.elf
# terminal 2 — conecta e anda instrução a instrução
arm-none-eabi-gdb -q cpsr.elf -ex "target remote :3333"(gdb) x/1i $pc
=> 0x10000 <_start>: mrs r0, CPSR
(gdb) info registers cpsr
cpsr 0xdf 223
(gdb) stepi @ mrs r0, cpsr
(gdb) p/x $r0
$1 = 0xdf
(gdb) stepi @ movs r1, #0
(gdb) info registers cpsr
cpsr 0x400000df 1073742047 @ bit 30 (Z) ligado
(gdb) stepi @ mrs r2, cpsr -> r2 = 0x400000df
(gdb) stepi @ subs r3, r1, #1
(gdb) p/x $r3
$2 = 0xffffffff
(gdb) info registers cpsr
cpsr 0x800000df 2147483871 @ bit 31 (N) ligado, Z apagado
(gdb) p/t $cpsr
$3 = 10000000000000000000000011011111Dois detalhes que esse trace revela sobre o emulador, não só sobre o ARM:
- O CPSR inicial é
0xdf. O byte baixo11011111éI=1,F=1,M[4:0]=11111: oarm-boxmonta o guest em modo System com IRQ e FIQ mascarados. Faz sentido para um runner user-mode — não há controlador de interrupção nem tabela de vetores. É oArmCoresendo posto nesse estado antes do primeirostep(). - As flags são exatamente as esperadas.
movs r1, #0liga só Z (0x40000000).subs r3, r1, #1faz0 - 1 = 0xFFFFFFFF: liga N (0x80000000), apaga Z, e — detalhe do ARM — na subtraçãoCé o NOT borrow, então0 - 1deixa C = 0. É essa álgebra que oevalConddoarm-jitterconsulta quando umBGE/BLTaparece logo depois.
Glossário
| Termo | O que é |
|---|---|
| CPSR | Current Program Status Register — flags + modo + máscaras de interrupção |
| SPSR | Saved PSR — cópia do CPSR feita ao entrar num modo de exceção |
| Banking | ter cópias físicas de um registrador, uma por modo |
| Modo | estado privilegiado da CPU; cada exceção entra em um |
| Flag pegajosa | bit que, uma vez ligado, só limpa por escrita explícita (Q) |
MRS / MSR | mover CPSR/SPSR ↔ registrador de propósito geral |
Comandos
| Comando | O que faz |
|---|---|
mrs r0, cpsr | copia o CPSR para R0 |
msr cpsr_f, r0 | escreve só o campo de flags (_f) do CPSR |
p $cpsr / p/t $cpsr | (GDB) imprime o CPSR em hex / binário |
p $spp $lrp $pc | (GDB) imprime R13 / R14 / R15 |
info registers | (GDB) todos os registradores + cpsr decodificado |
Próximo passo
Com os registradores e o CPSR no lugar, o próximo artigo disseca a anatomia de uma instrução ARM — o formato {cond}{S}, o Operand2 e o barrel shifter.
Trilha: Curso de Arquitetura ARM · Módulo 1, lição 1. Ver também: Fundamentos da arquitetura ARM, Flags e desvios condicionais.