Diretivas do Pré-processador do Assembly NASM: %define, %assign e %rep
A primeira etapa do processo de montagem de um código Assembly é o pré-processamento. Nesta fase, uma ferramenta chamada pré-processador analisa o código fonte em busca de comandos especiais, conhecidos como diretivas. O pré-processador então transforma o código com base nessas diretivas, antes mesmo que o montador (assembler) comece a traduzir as instruções para código de máquina.
Vamos explorar algumas das diretivas mais comuns do pré-processador do NASM.
A Diretiva %define
A diretiva %define permite a criação de constantes. O pré-processador realiza uma substituição textual, trocando todas as ocorrências da constante pelo valor definido.
global _start
%define MAGIC_NUM 22
section .data
num: db MAGIC_NUM ; Após o pré-processamento, num = 22
section .text
_start:
mov rdi, MAGIC_NUM ; Move o valor 22 para RDI
mov rax, 60
syscallNeste exemplo, a constante MAGIC_NUM é definida com o valor 22. Antes da montagem, qualquer menção a MAGIC_NUM no código é substituída pelo número 22.
Identificadores como MAGIC_NUM são chamados de símbolos de pré-processador. Eles são úteis para dar nomes descritivos a valores e ações, tornando o programa mais legível.
A diretiva %define também pode ser usada para definir expressões mais complexas, como trechos de código.
global _start
%define set_status mov rdi,
%define STATUS_CODE rax
section .text
_start:
mov STATUS_CODE, 45 ; Expande para: mov rax, 45
set_status STATUS_CODE ; Expande para: mov rdi, rax
mov rax, 60
syscallAqui, set_status é substituído pelo texto mov rdi, e STATUS_CODE é substituído por rax.
Uma constante também pode depender de outra:
%define i 1
%define d i * 3Neste caso, i é 1, e d é definido como a expressão 1 * 3.
Reatribuição de Símbolos com %assign
O NASM permite que um símbolo de pré-processador seja redefinido com %define:
%define num 1
%define num 3No entanto, a diretiva %define não permite redefinir um símbolo usando seu próprio valor em uma expressão aritmética, pois isso criaria uma recursão infinita durante a substituição textual.
%define num 1
%define num num + 2 ; ! ERRO: Definição recursivaPara esses casos, a diretiva %assign é a solução. Ela permite alterar o valor de um símbolo, reavaliando a expressão.
%define num 1
%assign num num + 2 ; Correto: num agora é 3Repetição de Código com %rep
A diretiva %rep permite automatizar a repetição de um bloco de código ou dados um número específico de vezes.
%rep numero_de_repeticoes
; ações a serem repetidas
%endrepO bloco entre %rep e %endrep será repetido a quantidade de vezes especificada.
global _start
%define num 1
section .data
; Laço de 10 repetições
%rep 10
%assign num num + 1
%endrep
status_code: dq num ; Após o laço, status_code = 11
section .text
_start:
mov rdi, [status_code]
mov rax, 60
syscallNo exemplo acima, o símbolo num é inicializado com 1. O laço %rep executa 10 vezes, e em cada iteração, a diretiva %assign incrementa o valor de num. Ao final do pré-processamento, num terá o valor 11.
Laços são úteis para gerar tabelas de dados ou sequências de instruções. Por exemplo, podemos gerar uma tabela de fatoriais em tempo de compilação.
global _start
%define N 5
%define F 1
factorials:
%assign i 1
%rep N
%assign F F*i
db F
%assign i i+1
%endrep
section .text
_start:
mov rdi, [factorials+4] ; Carrega o quinto fatorial (24)
mov rax, 60
syscallNeste laço, o símbolo F é atualizado para representar o fatorial de i. Após o pré-processador executar, o código Assembly final conterá a seguinte tabela de dados:
factorials:
db 1
db 2
db 6
db 24
db 120Resumo
- O pré-processador é uma ferramenta que transforma o código fonte antes da montagem.
- A diretiva
%definecria constantes por meio de substituição textual. - A diretiva
%assigné usada para reatribuir um valor a um símbolo, permitindo operações aritméticas. - A diretiva
%reppermite repetir um bloco de código um número fixo de vezes durante o pré-processamento, sendo útil para gerar dados ou instruções repetitivas.