Instruções de Laço Dedicadas em Assembly NASM: loop e jrcxz
Embora laços de repetição possam ser implementados com instruções primitivas (dec, cmp, jnz), a arquitetura x86-64 oferece instruções especializadas que simplificam a criação de laços baseados em contagem, vinculando a lógica diretamente ao registrador RCX.
A Instrução loop: Um Contador Automatizado
A instrução loop foi projetada para simplificar a estrutura de laços for ou repeat-until onde o número de iterações é conhecido previamente. Ela combina três ações em uma única operação atômica:
- Decrementa o registrador
RCXem 1. - Verifica se o novo valor de
RCXé diferente de zero. - Se
RCXnão for zero, salta para o rótulo de destino especificado.
Uma característica importante é que a instrução loop não afeta nenhum flag do registrador EFLAGS.
Exemplo de Uso (Linux):
global _start
section .text
_start:
mov rcx, 5 ; Inicializa RCX como o contador do laço.
mov rdi, 0
mainloop: ; Rótulo de início do laço.
add rdi, 2 ; Corpo do laço: executa a lógica principal.
loop mainloop ; Decrementa rcx. Se rcx != 0, salta para mainloop.
; A execução continua aqui quando rcx se torna 0.
mov rax, 60
syscallNeste exemplo, o laço executará exatamente cinco vezes. A cada iteração, loop gerencia o contador e o salto, tornando o código mais conciso do que a alternativa manual (dec rcx / jnz mainloop).
Nota sobre Desempenho: Em processadores modernos, a instrução
looppode ser mais lenta do que a combinaçãodec/jnz. Seu uso hoje é mais por concisão e legado do que por otimização de velocidade.
Variações Condicionais: loope e loopne
A família loop inclui duas variantes que adicionam uma segunda condição de parada baseada no Zero Flag (ZF). Elas são ideais para algoritmos de busca, como encontrar um caractere em uma string.
loope(Loop if Equal) /loopz(Loop if Zero): DecrementaRCX. O laço continua apenas seRCX != 0EZF = 1.- Caso de uso: Continuar um laço enquanto os elementos comparados forem iguais, por no máximo
RCXiterações.
- Caso de uso: Continuar um laço enquanto os elementos comparados forem iguais, por no máximo
loopne(Loop if Not Equal) /loopnz(Loop if Not Zero): DecrementaRCX. O laço continua apenas seRCX != 0EZF = 0.- Caso de uso: Procurar por um elemento diferente em uma sequência, por no máximo
RCXiterações.
- Caso de uso: Procurar por um elemento diferente em uma sequência, por no máximo
A Instrução JRCXZ: A Guarda do Laço
A instrução loop tem uma limitação crítica: ela testa a condição (RCX != 0) após decrementar. Se um laço loop for iniciado com RCX valendo 0, o primeiro decremento o transformará em 0xFFFFFFFFFFFFFFFF (underflow), fazendo com que o laço execute 2^64 vezes, um bug grave.
Para prevenir isso, a arquitetura fornece a instrução jrcxz (Jump if RCX is Zero).
jrcxz: Verifica o valor deRCX. SeRCXfor exatamente0, salta para o rótulo especificado.
Esta instrução funciona como uma "guarda" na entrada do laço, garantindo que o corpo não seja executado se o contador já for zero.
Exemplo de Uso Seguro (Linux):
global _start
section .text
_start:
mov rcx, 0 ; Exemplo com contador inicial zero.
mov rdi, 1
jrcxz exit_loop ; Antes de entrar, verifica se rcx é 0. O salto ocorre.
mainloop:
add rdi, 2 ; Este corpo do laço nunca será executado.
loop mainloop
exit_loop:
mov rax, 60
syscallA combinação de jrcxz no início e loop no final cria uma estrutura de laço robusta e segura, similar a um laço for padrão em linguagens de alto nível, que sempre verifica a condição antes da primeira iteração.
Resumo
- A instrução
loopsimplifica laços contados ao combinar o decremento deRCXcom um salto condicional. RCXé o registrador implicitamente usado como contador pela família de instruçõesloop.- As variantes
loopeeloopneadicionam o Zero Flag como uma segunda condição de parada, úteis em algoritmos de busca. - A instrução
jrcxzé usada para verificar seRCXé zero antes de iniciar um laço, prevenindo a execução acidental com um contador inválido.