MCU & FPGA Linguagem Assembly ARM vs Thumb: Arquitetura de Instruções nos Cortex-M e Exemplo Prático em Assembly

Assembly ARM vs Thumb: Arquitetura de Instruções nos Cortex-M e Exemplo Prático em Assembly


4 — Exemplo prático: “Hello World” em assembly para Cortex-M3 com UART hipotética

Nesta seção, vamos unir os conceitos discutidos até aqui em um exemplo concreto e didático. O objetivo é mostrar como um firmware mínimo em assembly Thumb-2 pode inicializar o sistema, escrever caracteres em uma UART hipotética e encerrar a execução. O foco não é em um microcontrolador específico de fabricante, mas no modelo mental e na estrutura típica de um programa bare-metal para Cortex-M3.

Premissas do exemplo

Assumiremos uma UART mapeada em memória com dois registradores simples:

  • UART_DRData Register (escrita envia um byte)
  • UART_SRStatus Register
    • bit 0 = TX_READY (1 quando pode transmitir)

Endereços hipotéticos:

  • UART_DR = 0x4000_0000
  • UART_SR = 0x4000_0004

O código é escrito em Thumb-2, usa apenas instruções disponíveis na ARMv7-M, e segue o fluxo típico:

  1. Reset
  2. Carregamento do endereço da string
  3. Loop de transmissão byte a byte
  4. Espera por TX_READY

Vetor de interrupções e ponto de entrada

    .syntax unified
    .cpu cortex-m3
    .thumb

    .section .isr_vector, "a"
    .word   _stack_top     /* Ponteiro inicial da pilha */
    .word   Reset_Handler  /* Endereço do reset */

Aqui vemos um ponto importante da arquitetura Cortex-M: o vetor de interrupções já contém o endereço da pilha inicial. Não há instrução explícita para configurar SP; o hardware faz isso automaticamente no reset.


Rotina de reset

    .section .text
    .thumb_func
Reset_Handler:
    ldr r0, =hello_str     /* r0 aponta para a string */

A instrução ldr r0, =hello_str é um pseudo-opcode. O montador decide se isso vira uma instrução de 16 ou 32 bits, ou se gera um literal pool. Esse é um exemplo prático de como o Thumb-2 abstrai detalhes de codificação, mantendo o código legível.


Loop principal de transmissão

send_loop:
    ldrb r1, [r0]          /* Carrega um byte da string */
    cmp  r1, #0            /* Verifica fim da string */
    beq  end               /* Se zero, termina */

wait_tx:
    ldr  r2, =UART_SR
    ldr  r3, [r2]
    tst  r3, #1            /* Testa TX_READY */
    beq  wait_tx           /* Aguarda UART pronta */

    ldr  r2, =UART_DR
    strb r1, [r2]          /* Envia o byte */

    adds r0, r0, #1        /* Próximo caractere */
    b    send_loop

Esse trecho concentra vários conceitos-chave:

  • Acesso a periféricos por memória mapeada, típico de microcontroladores.
  • Uso de ldrb/strb, reforçando que estamos transmitindo bytes, não palavras.
  • Estrutura de busy-wait, comum em exemplos iniciais e drivers simples.
  • Instruções curtas e previsíveis, todas adequadas ao pipeline simples do Cortex-M3.

Observe que não há nenhuma instrução “especial” de I/O. Para o processador, UART é apenas memória, o que simplifica enormemente o modelo de programação.


Finalização

end:
    b end                  /* Loop infinito */

Em sistemas embarcados bare-metal, é comum finalizar com um loop infinito. Não existe “retorno ao sistema operacional”. Esse detalhe reforça a diferença conceitual entre firmware e software de propósito geral.


String em memória

    .section .rodata
hello_str:
    .asciz "Hello World\r\n"

A diretiva .asciz adiciona automaticamente o byte nulo (\0) ao final da string, facilitando o controle do loop. Esse padrão é amplamente utilizado em exemplos de baixo nível por sua simplicidade e clareza.


O que esse exemplo ensina, de fato

Mais do que imprimir “Hello World”, esse código demonstra:

  • Como o Thumb-2 se encaixa naturalmente em firmware Cortex-M.
  • Por que o modelo de execução é determinístico e previsível.
  • Como a ausência de modos ARM clássicos simplifica o hardware e o raciocínio.
  • Como assembly, mesmo simples, revela claramente a arquitetura subjacente.

Esse tipo de exemplo é extremamente valioso para quem deseja entender o que o compilador C faz por baixo dos panos e para depurar problemas reais de inicialização, comunicação serial e falhas de boot.

Related Post