Introdução à programação do ARM Cortex-M0+ em Assembly

Este post faz parte da série ARM Cortex-M0+. Leia também os outros posts da série:

Um processador simples pode executar operações aritméticas, lógicas, teste e comparação, controle de fluxo e movimentação de dados. Tais operações e os recursos visíveis ao programador são especificados pela ISA.

No artigo de introdução foi apresentada a estrutura básica do processador e os recursos visíveis ao programador. Utilizando a ISA do ARM Cortex-M0+, este artigo apresenta o padrão ARM assembly para criar programas em assembly. Para introdução, serão especificadas as operações que caracterizam a arquitetura: load-store. No próximo artigo da série, os programas serão estruturados considerando as características de um microcontrolador.

Conhecendo a ferramenta de montagem

O desenvolvimento de um programa depende da ferramenta utilizada. Para os códigos mostrados nesse texto será utilizado o padrão ARM assembly. Cabe ressaltar que atualmente os códigos em assembly são escritos seguindo o padrão UAL (do inglês, Unified Assembler Language) [2]. A seguir são apresentadas algumas convenções do montador.

Definindo o código que será executado: Lista de Instruções

Cada linha de código – que possui uma instrução – segue o seguinte formato:

label    mnemônico    operando1,   operando2, .    ; Comentários

  • label: é utilizado para referenciar o endereço que a instrução está. É opcional, e pode ser utilizado como referência em outras instruções (instruções de desvio). Também pode ser utilizado como referência para o endereço de um dado. É importante ressaltar que o label deve iniciar na primeira coluna da linha (sem espaço).
  • mnemônico: é utilizado para indicar a instrução. Dependendo da instrução é necessário indicar os operandos.
  • Para operações de escrita na memória, o primeiro operando indica a origem, isto é, o registrador que contém o dado que será gravado na memória.
  • Para operações de leitura da memória, o primeiro operando indica o destino, isto é, o registrador que que receberá a informação que está na memória.
  • Instruções no formato ARM usam o primeiro operando como destino da operação.
  • Por fim, os comentários do programa são precedidos do símbolo ‘;’.

Alguns exemplos são mostrados a seguir. No padrão UAL o sufixo S deve ser utilizado nas instruções que alteram o conteúdo do registrador APSR [2]. Cabe, nesse ponto, apresentar o conteúdo do registrador APSR, em que N, Z, C e V representam, respectivamente: Valor negativo; Valor zero; Carry ou Borrow; Overflow.

Flags condicionais da ULA.
Figura 1: Flags condicionais da ULA.

Carregar um registrador com um valor constante (#constante) de 8 bits.

  • MOVS R0,#10 ; R0 = 10
  • MOVS R0,#0x0A ; R0 = 10

Movimentação de dados entre registradores

  • MOV R0,R1

Adição entre um registrador e uma constante de 3 bits

  • ADDS R0,R0,#1 ; R0 = R0 + 1
  • ADDS R1,R0,#1 ; R1 = R0 + 1

Adição entre um registrador e uma constante de 8 bits

  • ADDS R0,#10 ; R0 = R0 + 10

Adição entre registradores

  • ADD R0,R1 ; R0 = R0 + R1

Comparação entre registradores

  • CMP R0,R1 ;Z flag =1 se R0==R1

Desvio condicional

  • BEQ label ; salta para o endereço indicado se Z flag = 1

Desvio incondicional

  • B label ; salta para o endereço indicado

Instruindo o montador: Diretivas

Além das instruções, algumas diretivas podem ser utilizadas no programa. Para definir um valor constante no programa (#define) e referenciá-lo pelo nome, deve ser utilizada a diretiva EQU [2]. Um exemplo de aplicação é mostrado a seguir:

  • MAGIC_NUMBER EQU 10
  • MOVS R0, #MAGIC_NUMBER ; R0 = 10

Também é possível definir variáveis e valores constantes na memória. Para tal, as diretivas DCn são utilizadas [2]:

  • DCB: Reserva espaço para valores de um byte (-2^7 à 2^8-1). Também é utilizado para sequência de caracteres (string);
  • DCW: Reserva espaço para valores de dois bytes (-2^15 à 2^16-1);
  • DCD: Reserva espaço para uma palavra de quatro bytes (-2^31 à 2^32-1);
  • DCQ: Reserva espaço para uma palavra de oito bytes (-2^63 à 2^64-1);
  • DCFS: Ponto flutuante de precisão simples (4 bytes);
  • DCFD: Ponto flutuante de precisão dupla (8 bytes);
  • DCI: Utilizado para declarar uma instrução utilizando linguagem de máquina.

Exemplo: Loop com 10 iterações.

Modos de Endereçamento

Como apresentado, a arquitetura do processador é caracterizada por sua operação load-store [1]. Isso significa que somente instruções load e store acessam a memória [3]. Assim sendo, tais instruções possibilitam várias formas de determinar o operando de origem e destino das operações. Além disso, é importante conhecer os tipos de dados que essas instruções operam [1]:

  • Byte: 8 bits;
  • Halfword: 16 bits;
  • Word: 32 bits;

É importante ressaltar que todo endereço acessado deve estar alinhado [1]. Isto é, o acesso de meia-palavra deve ser alinhado por 16 bits e os acessos de palavra precisam ser alinhados por 32 bits [3]. Qualquer violação gerará uma exceção do tipo Hardware Fault [1].

Para os modos de endereçamento apresentados a seguir:

  • Leitura: Rd significa registrador de destino; Rm/Rn, registrador de origem; #imm, constante de N bits.
  • Escrita: Rd significa registrador de origem; #imm, constante de N bits.

Imediato

A instrução MOV permite carregar uma constante de 8 bits em um registrador. Esse método é chamado de imediado porque a constante é definida na própria instrução.

Descrição

Sintaxe

Valor imediato de 8 bits

MOVS Rd, #<imm>

Por Registradores

Movimentações internas entre registradores são feitos pela instrução MOV.

Descrição

Sintaxe

Entre registradores da parte baixa (low registers)

MOVS Rd, Rm

Qualquer registrador

MOV Rd, Rm

Altera valor de PC (PC = Rm)MOV PC, Rm

Endereçamento por deslocamento

O endereço acessado na memória é dado por uma combinação de registradores/valores constantes.

Registrador base com deslocamento constante

Um valor imediato de 5 bits é somado (offset) com o valor de um registrador (Rn).

Descrição

Sintaxe

Word, offset imediato

Rd = MEMÓRIA[Rn + (offset << 2)]

LDR Rd, [Rn, #<imm>]

Halfword, offset imediato

Rd = MEMÓRIA[Rn + (offset << 1)]

LDRH Rd, [Rn, #<imm>]

Byte, offset imediato

Rd = MEMÓRIA[Rn + offset]

LDRB Rd, [Rn, #<imm>]

Word, offset imediato

MEMÓRIA[Rn + offset] = Rd

STR Rd, [Rn, #<imm>]

Halfword, offset imediato

MEMÓRIA[Rn + offset] = Rd

STRH Rd, [Rn, #<imm>]

Byte, offset imediato

MEMÓRIA[Rn + offset] = Rd

STRB Rd, [Rn, #<imm>]

Registrador base com deslocamento por registrador

Todos os registradores envolvidos são da região Low Registers.

Descrição

Sintaxe

Word, deslocamento por registrador

Rd = MEMÓRIA[Rn + Rm]

LDR Rd, [Rn, Rm]

Halfword, deslocamento por registrador

Rd = MEMÓRIA[Rn + Rm]

LDRH Rd, [Rn, Rm]

Byte, deslocamento por registrador

Rd = MEMÓRIA[Rn + Rm]

LDRB Rd, [Rn, Rm]

Word, deslocamento por registrador

MEMÓRIA[Rn + Rm] = Rd

STR Rd, [Rn, Rm]

Halfword, deslocamento por registrador

MEMÓRIA[Rn + Rm] = Rd

STRH Rd, [Rn, Rm]

Byte, deslocamento por registrador

MEMÓRIA[Rn + Rm] = Rd

STRB Rd, [Rn, Rm]

Registrador base com deslocamento (sinalizado) por registrador

O valor de leitura é ajustado considerando o sinal.

Descrição

Sintaxe

halfword sinalizado, offset por registrador

Rd = MEMÓRIA[sinalizado(Rn + Rm)]

LDRSH Rd, [Rn, Rm]

byte sinalizado, offset por registrador

Rd = sinalizado(MEMÓRIA[Rn + Rm])

LDRSB Rd, [Rn, Rm]

Relativo

Nessas operações, o valor imediato é uma constante de 8 bits. Cabe ressaltar que essas instruções têm desvio máximo que pode ser alcançado [1].

Descrição

Sintaxe

relativo ao PC

Rd = MEMÓRIA[(PC+4) + (offset << 2)]

O deslocamento do PC considera o pipeline de 2 estágios

LDR Rd, <label>

relativo ao SP

Rd = MEMÓRIA[SP + (offset << 2)]

LDR Rd, [SP, #<imm>]

relativo ao SP

MEMÓRIA[SP + (offset << 2)] = Rd

STR Rd, [SP, #<imm>]

Múltiplos registradores

O argumento loreglist representa a lista de registradores (Low Registers) que serão manipulados. Já Rn, indica o endereço base da operação que ocorre da seguinte maneira: o registrador com menor número acessa o menor endereço; o registrador com maior número acessa o maior endereço. A ordem dos registradores não faz diferença.

Descrição

Sintaxe

Múltiplo, o endereço final da operação é gravado em Rn

<loreglist> = MEMÓRIA[Rn]

LDM Rn!, {<loreglist>}

Múltiplo, o endereço indicado por Rn não é alterado

<loreglist> = MEMÓRIA[Rn]

LDM Rn, {<loreglist>}

Múltiplo

MEMÓRIA[Rn] = <loreglist>

STM Rn!, {<loreglist>}

Exemplos de aplicação

A seguir são demonstrados dois casos em que um vetor de inteiros é preenchido com os valores armazenados em outro vetor. No primeiro exemplo, a cópia é realizada item por item. No segundo, a cópia é realizada de 4 em 4.

Observações:

  • THUMB indica que as instruções são Thumb e o código é UAL.
  • AREA indica ao assembler que a sequência de comandos, código ou dados, pertence à região especificada.

Sabia mais

O conjunto de instruções é bastante flexível e também apresenta variações entre arquiteturas. Para conhecer as instruções e suas restrições consulte o conjunto de instruções no manual do processador e também no manual do compilador.

ARM Cortex M0+

Configuração e inicialização de microcontroladores: Um estudo utilizando ARM Cortex-M0+

TickAttack: um gerenciador de tarefas simples para um ARM Cortex-M0

Referências

[1] Cortex-M0+Technical Reference Manual.

[2] Arm® Compiler armasm User Guide.

[3] STALLINGS, W. Arquitetura e organização de computadores. Pearson Prentice-Hall, 10ª ed., São Paulo. 2017.

Outros artigos da série

<< Introdução ao processador ARM Cortex-M0+
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Hardware » Sistemas Digitais » Introdução à programação do ARM Cortex-M0+ em Assembly
Comentários:
Notificações
Notificar
guest
5 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Eder Andrade
Membro
07/08/2018 08:33

Muito bom Fernando!

LAURIANO ELMIRO DUARTE
laurianoelmiroduarte
07/08/2018 00:08

show show, saberia onde eu posso comprar um ARM Cortex-M0+ ? ou tipo uma placa pra programar que tenha um deles ?
abraços

Eder Andrade
Membro
Reply to  laurianoelmiroduarte
07/08/2018 08:33

Lauriano, você pode comprar kits STM32Fx Discovery que tenha o Cortex M0+ ou M0 na Mouser, Digikey ou em sites com o Aliexpress.

Alessandro
Alessandro
06/08/2018 14:55

Ótimo artigo, espero pelos próximos

Talvez você goste:

Séries



Outros da Série

Menu

WEBINAR
 
Redes Mesh para Monitoramento
e Controle de Sensores

Data: 15/07 às 14:00h Apoio: Artimar| Microchip| Tecsus
 
INSCREVA-SE AGORA »



 
close-link