Manipulação de bits em Thumb: AND, ORR, EOR, BIC, BFI, BFC e shift register
Em sistemas embarcados, os registradores de periféricos (GPIO, UART, SPI, timers) são mapeados em memória. Cada bit controla uma funcionalidade: ligar/desligar um LED, habilitar uma interrupção, selecionar um clock. O ARM Thumb oferece instruções dedicadas para ler, setar, limpar e alternar bits sem afetar os demais — essencial para controle de hardware.
Operações lógicas: o arsenal de 3 operandos
No Thumb-2, as operações lógicas de 3 operandos atualizam os flags (N/Z/C/V) quando levam o sufixo S (ex.: ANDS). Sem S, não atualizam — ideal quando o resultado é usado apenas como fonte de dados.
AND / ANDS — máscara (mask)
ANDS Rd, Rn, Operand2 → Rd = Rn AND Operand2 + flags. É a instrução clássica para extrair ou testar um bit específico.
/* ARM (32-bit) */
ANDS r0, r1, #0x0F ; e211000f
/* Thumb-2 (32-bit wide) */
ANDS.W r0, r1, #0x0F ; f011 000f| Modo | Bytes (hex) | Codificação |
|---|---|---|
| ARM | E2 11 00 0F | 32-bit |
| Thumb-2 | F0 11 00 0F | 32-bit wide |
ORR / ORRS — setar bits
ORRS Rd, Rn, Operand2 → Rd = Rn OR Operand2 + flags. Liga bits específicos.
/* ARM */
ORRS r0, r1, #0x0F ; e391000f
/* Thumb-2 */
ORRS.W r0, r1, #0x0F ; f051 000f| Modo | Bytes (hex) |
|---|---|
| ARM | E3 91 00 0F |
| Thumb-2 | F0 51 00 0F |
EOR / EORS — alternar (toggle) bits
EORS Rd, Rn, Operand2 → Rd = Rn XOR Operand2 + flags. XOR com 1 inverte o bit; XOR com 0 mantém. Útil para toggle.
/* ARM */
EORS r0, r1, #0x01 ; e2310001
/* Thumb-2 */
EORS.W r0, r1, #0x01 ; f091 000f| Modo | Bytes (hex) |
|---|---|
| ARM | E2 31 00 01 |
| Thumb-2 | F0 91 00 01 |
BIC / BICS — bit clear (AND com complemento)
BICS Rd, Rn, Operand2 → Rd = Rn AND NOT(Operand2). O jeito mais direto de limpar bits específicos sem afetar os demais.
/* ARM */
BICS r0, r1, #0xF0 ; e3d100f0
/* Thumb-2 */
BICS.W r0, r1, #0xF0 ; f031 00f0| Modo | Bytes (hex) |
|---|---|
| ARM | E3 D1 00 F0 |
| Thumb-2 | F0 31 00 F0 |
Dica:BIC Rn, Rm, #mask limpa os bits de Rn indicados por mask. Equivale a Rn = Rn & ~mask.
MVN / MVNS — move NOT (complemento)
MVNS Rd, Operand2 → Rd = NOT(Operand2). Carrega o valor bit-a-bit invertido. Útil para máscaras rápidas.
/* ARM */
MVN r0, #0 ; e3e00000
/* Thumb-2 */
MVN.W r0, #0 ; f06f 0000| Modo | Bytes (hex) |
|---|---|
| ARM | E3 E0 00 00 |
| Thumb-2 | F0 6F 00 00 |
Detalhe:MVN r0, #0 → 0xFFFFFFFF. O imm12 codifica 0x000 com rotação de 0 bits (sem rotação = valor literal).
TST — testar bits sem alterar registradores
TST Rn, Operand2 → Rn AND Operand2, atualiza flags sem armazenar resultado. Perfeito para verificar se um bit está ligado.
/* ARM */
TST r0, #0x04 ; e3100004
/* Thumb-2 */
TST.W r0, #0x04 ; f010 0f04| Modo | Bytes (hex) |
|---|---|
| ARM | E3 10 00 04 |
| Thumb-2 | F0 10 0F 04 |
Nota:TST é um pseudo-código do processador — o assembler traduz para ANDS sem destination register. O encoding Thumb-2 F010 0F04 é um ANDS implícito com Rd descartado.
Tabela: operações lógicas (bytes verificados)
| Instrução | ARM (hex) | Thumb-2 (hex) | Tamanho | flags? |
|---|---|---|---|---|
ANDS | E211000F | F011000F | 32b / 32b wide | Sim |
ORRS | E391000F | F051000F | 32b / 32b wide | Sim |
EORS | E2310001 | F0910001 | 32b / 32b wide | Sim |
BICS | E3D100F0 | F03100F0 | 32b / 32b wide | Sim |
MVN | E3E00000 | F06F0000 | 32b / 32b wide | Não |
TST | E3100004 | F0100F04 | 32b / 32b wide | Sim |
Shifts e rotações
O ARM usa barrel shifter — o hardware desloca o operando em 1 ciclo. As instruções principais:
| Instrução | Efeito | Exemplo |
|---|---|---|
LSL Rd, Rm, #n | Deslocamento lógico esquerda (×2ⁿ) | LSL r0, r1, #3 → r0 = r1 × 4 |
LSR Rd, Rm, #n | Deslocamento lógico direita (÷2ⁿ) | LSR r0, r1, #2 → r0 = r1 / 4 |
ASR Rd, Rm, #n | Deslocamento aritmético direita (sinal preservado) | ASR r0, r1, #1 → divisão signed |
ROR Rd, Rm, #n | Rotação à direita com carry | ROR r0, r1, #8 |
Bytes verificados
/* ARM */
LSL r0, r1, #3 ; e1a00181
LSR r0, r1, #2 ; e1a00121
ASR r0, r1, #1 ; e1a000c1
ROR r0, r1, #8 ; e1a00461
/* Thumb-2 */
LSLS r0, r1, #3 ; 00c8 (16-bit!)
LSRS r0, r1, #2 ; 0888 (16-bit!)
ASRS r0, r1, #1 ; 1048 (16-bit!)
MOVS r0, r1, ROR #8 ; ea5f 2031 (Thumb-2 não tem ROR standalone → via MOVS)| Instrução | ARM (hex) | Thumb (hex) | Tamanho |
|---|---|---|---|
LSL | E1 A0 01 81 | 00 C8 | 32b / 16b |
LSR | E1 A0 01 21 | 08 88 | 32b / 16b |
ASR | E1 A0 00 C1 | 10 48 | 32b / 16b |
ROR | E1 A0 04 61 | EA 5F 20 31 | 32b / 32b (via MOVS) |
Ponto-chave:LSLS, LSRS, ASRS com imediato são instruções de 16 bits em Thumb — ocupam metade do espaço que ocupariam em ARM (32 bits). Isso é um ganho de densidade de código crucial em microcontroladores com Flash limitado. Já ROR não tem encoding standalone em Thumb-2 — o assembler o traduz para MOVS Rd, Rm, ROR #n.
Instruções de campo de bits: BFI, BFC, UBFX
O Thumb-2 adiciona instruções de 32 bits para manipular campos de bits (bit fields) diretamente — essenciais para periféricos que usam campos de bits nos registradores.
Nota importante:BFI, BFC e UBFX são instruções Thumb-2 only. Não existem equivalentes em ARM mode (testado com -mcpu=arm1176jzf-s — o assembler rejeitou).
BFI — Bit Field Insert
BFI Rd, Rn, #lsb, #width insere o campo de width bits de Rn (a partir do LSB) em Rd, a partir da posição lsb.
bfii r0, r1, #4, #3 ; insere r1[2:0] em r0[6:4]00000000 <thumb_bfi>:
36: f361 1006 bfi r0, r1, #4, #3Bytes: F3 61 10 06
BFC — Bit Field Clear
BFC Rd, #lsb, #width limpa width bits a partir de lsb em Rd, sem alterar os demais.
bfc r0, #4, #12 ; limpa bits 4..1500000000 <thumb_bfc>:
3c: f36f 100f bfc r0, #4, #12Bytes: F3 6F 10 0F
UBFX — Unsigned Bit Field Extract
UBFX Rd, Rn, #lsb, #width extrai width bits de Rn a partir de lsb e zero-extende em Rd.
ubfx r0, r1, #8, #8 ; extrai byte 1 de R000000000 <thumb_ubfx>:
42: f3c1 2007 ubfx r0, r1, #8, #8Bytes: F3 C1 20 07
Tabela: instruções de campo de bits
| Instrução | Bytes (hex) | Tamanho | Disponível em | Uso típico |
|---|---|---|---|---|
BFI | F3611006 | 32 bits | Thumb-2 only | Configurar campos de registrador |
BFC | F36F100F | 32 bits | Thumb-2 only | Limpar registrador de periférico |
UBFX | F3C12007 | 32 bits | Thumb-2 only | Leer campos de periférico |
Comparativo ARM vs Thumb-2: o que muda de fato
| Aspecto | ARM mode | Thumb-2 |
|---|---|---|
| Instruções lógicas (ANDS, ORRS…) | 32 bits | 32 bits wide |
| Shifts com imediato (LSLS, LSRS, ASRS) | 32 bits | 16 bits (metade do espaço) |
ROR | Instrução standalone (E1A00461) | Via MOVS Rd, Rm, ROR #n (EA5F2031) |
BFI/BFC/UBFX | Não existe | Disponível (32 bits) |
| Density de código | Menor | Maior (16-bit shifts) |
Implicação prática: em Cortex-M (somente Thumb-2), o código de manipulação de bits tende a ser mais compacto que em ARM mode, especialmente em loops que usam shifts repetidos.
Exemplo prático: configurando bits de um registrador de periférico
/* Supondo GPIOA base em 0x40020000 */
LDR r0, =0x40020000 ; base do GPIOA
/* Configura PA0 como output: setar bit 0 do MODER */
LDR r1, =0x00000001
ORRS r0, r0, r1 ; r0 = r0 OR 1 → ligar bit 0
STR r0, [r0, #0x00] ; escreve no MODER
/* Liga LED no PA0: setar bit no ODR */
LDR r1, =0x00000001
ORRS r0, r0, r1
STR r0, [r0, #0x14] ; ODR offset 0x14
/* Limpa bit 0 do ODR (desliga LED) */
LDR r1, =0xFFFFFFFE
BICS r0, r0, r1
STR r0, [r0, #0x14]Quando usar cada instrução
AND/ANDS: extrair ou testar um bit (máscara).ORR/ORRS: ligar bits específicos.EOR/EORS: alternar bits (toggle).BIC/BICS: limpar bits específicos (complemento).MVN: complemento rápido para montar máscaras.TST: testar bit sem efeito colateral (não altera registrador).LSLS/LSRS/ASRS: multiplicar/dividir por potências de 2, ou manipular campos de bits.BFI/BFC/UBFX: manipular campos de bits em registradores de periférico (Thumb-2 only).
Previsibilidade e o barrel shifter: o barrel shifter do ARM opera em 1 ciclo para shifts imediatos. No Cortex-M, LSLS/LSRS/ASRS em formato 16-bit ocupam metade do Flash e mantêm o mesmo custo de 1 ciclo. Isso torna o Thumb-2 mais eficiente que ARM mode para código de manipulação de bits.
Glossário: instruções
| Instrução | O que faz |
|---|---|
ANDS Rd, Rn, Operand2 | AND lógico + flags |
ORRS Rd, Rn, Operand2 | OR lógico + flags |
EORS Rd, Rn, Operand2 | XOR lógico + flags |
BICS Rd, Rn, Operand2 | Bit clear (AND com NOT) + flags |
MVN Rd, Operand2 | Move NOT (complemento) |
TST Rn, Operand2 | Testa bits (AND sem resultado) |
LSLS/LSRS/ASRS | Shifts lógico/direita/aritmético (16-bit Thumb) |
ROR Rd, Rm, #n | Rotação (ARM standalone / Thumb via MOVS) |
BFI Rd, Rn, #lsb, #width | Insere campo de bits (Thumb-2 only) |
BFC Rd, #lsb, #width | Limpa campo de bits (Thumb-2 only) |
UBFX Rd, Rn, #lsb, #width | Extrai campo unsigned (Thumb-2 only) |
Glossário: comandos
| Comando | O que faz |
|---|---|
-mcpu=cortex-m3 | Monta para Cortex-M3 (Thumb-2) |
-mcpu=arm1176jzf-s | Monta para ARM mode |
.syntax unified | Habilita Thumb-2 no assembler |
arm-none-eabi-as | Monta assembly |
arm-none-eabi-objdump -d | Mostra bytes e instruções |
O que o arm-jitter faz com isso
Quando o arm-jitter executa uma instrução de manipulação de bits, ele decodifica o opcode e executa a operação no registrador emulado. Para LSLS/LSRS/ASRS, o barrel shifter do emulador opera em 1 ciclo — o mesmo custo do hardware real do Cortex-M. Para BFI/BFC/UBFX, o emulador aplica a máscara e o deslocamento diretamente no valor do registrador. O resultado é idêntico ao comportamento do hardware.
Próximo passo
Até aqui cobrimos operações lógicas, shifts e campos de bits em Thumb-2. O próximo texto entra em interrupções e exceções: SVC, PendSV e SysTick — os três pilares que todo RTOS em Cortex-M precisa entender. Até lá, tente escrever um loop que alterna o bit 0 de um registrador usando EORS e verifique os bytes com arm-none-eabi-objdump -d.