← Voltar aos artigos

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
Bytes verificados (devkitARM)
ModoBytes (hex)Codificação
ARME2 11 00 0F32-bit
Thumb-2F0 11 00 0F32-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
Bytes verificados (devkitARM)
ModoBytes (hex)
ARME3 91 00 0F
Thumb-2F0 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
Bytes verificados (devkitARM)
ModoBytes (hex)
ARME2 31 00 01
Thumb-2F0 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
Bytes verificados (devkitARM)
ModoBytes (hex)
ARME3 D1 00 F0
Thumb-2F0 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
Bytes verificados (devkitARM)
ModoBytes (hex)
ARME3 E0 00 00
Thumb-2F0 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
Bytes verificados (devkitARM)
ModoBytes (hex)
ARME3 10 00 04
Thumb-2F0 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)

Operações lógicas — ARM vs Thumb-2
InstruçãoARM (hex)Thumb-2 (hex)Tamanhoflags?
ANDSE211000FF011000F32b / 32b wideSim
ORRSE391000FF051000F32b / 32b wideSim
EORSE2310001F091000132b / 32b wideSim
BICSE3D100F0F03100F032b / 32b wideSim
MVNE3E00000F06F000032b / 32b wideNão
TSTE3100004F0100F0432b / 32b wideSim

Shifts e rotações

O ARM usa barrel shifter — o hardware desloca o operando em 1 ciclo. As instruções principais:

Instruções de shift
InstruçãoEfeitoExemplo
LSL Rd, Rm, #nDeslocamento lógico esquerda (×2ⁿ)LSL r0, r1, #3 → r0 = r1 × 4
LSR Rd, Rm, #nDeslocamento lógico direita (÷2ⁿ)LSR r0, r1, #2 → r0 = r1 / 4
ASR Rd, Rm, #nDeslocamento aritmético direita (sinal preservado)ASR r0, r1, #1 → divisão signed
ROR Rd, Rm, #nRotação à direita com carryROR 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)
Shifts — bytes verificados (devkitARM)
InstruçãoARM (hex)Thumb (hex)Tamanho
LSLE1 A0 01 8100 C832b / 16b
LSRE1 A0 01 2108 8832b / 16b
ASRE1 A0 00 C110 4832b / 16b
RORE1 A0 04 61EA 5F 20 3132b / 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, #3

Bytes: 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..15
00000000 <thumb_bfc>:
  3c:	f36f 100f 	bfc	r0, #4, #12

Bytes: 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 R0
00000000 <thumb_ubfx>:
  42:	f3c1 2007 	ubfx	r0, r1, #8, #8

Bytes: F3 C1 20 07

Tabela: instruções de campo de bits

BFI, BFC, UBFX — bytes verificados
InstruçãoBytes (hex)TamanhoDisponível emUso típico
BFIF361100632 bitsThumb-2 onlyConfigurar campos de registrador
BFCF36F100F32 bitsThumb-2 onlyLimpar registrador de periférico
UBFXF3C1200732 bitsThumb-2 onlyLeer campos de periférico

Comparativo ARM vs Thumb-2: o que muda de fato

Diferenças entre ARM mode e Thumb-2
AspectoARM modeThumb-2
Instruções lógicas (ANDS, ORRS…)32 bits32 bits wide
Shifts com imediato (LSLS, LSRS, ASRS)32 bits16 bits (metade do espaço)
RORInstrução standalone (E1A00461)Via MOVS Rd, Rm, ROR #n (EA5F2031)
BFI/BFC/UBFXNão existeDisponível (32 bits)
Density de códigoMenorMaior (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ções de manipulação de bits
InstruçãoO que faz
ANDS Rd, Rn, Operand2AND lógico + flags
ORRS Rd, Rn, Operand2OR lógico + flags
EORS Rd, Rn, Operand2XOR lógico + flags
BICS Rd, Rn, Operand2Bit clear (AND com NOT) + flags
MVN Rd, Operand2Move NOT (complemento)
TST Rn, Operand2Testa bits (AND sem resultado)
LSLS/LSRS/ASRSShifts lógico/direita/aritmético (16-bit Thumb)
ROR Rd, Rm, #nRotação (ARM standalone / Thumb via MOVS)
BFI Rd, Rn, #lsb, #widthInsere campo de bits (Thumb-2 only)
BFC Rd, #lsb, #widthLimpa campo de bits (Thumb-2 only)
UBFX Rd, Rn, #lsb, #widthExtrai campo unsigned (Thumb-2 only)

Glossário: comandos

Comandos de montagem e depuração
ComandoO que faz
-mcpu=cortex-m3Monta para Cortex-M3 (Thumb-2)
-mcpu=arm1176jzf-sMonta para ARM mode
.syntax unifiedHabilita Thumb-2 no assembler
arm-none-eabi-asMonta assembly
arm-none-eabi-objdump -dMostra 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.