Tomando Decisões em Assembly: Flags de Status e Saltos Condicionais
Enquanto a instrução jmp realiza um salto incondicional, a verdadeira inteligência de um programa reside na sua capacidade de tomar decisões. Em Assembly, isso é feito através de saltos condicionais, que só ocorrem se certas condições forem atendidas. Essas condições são monitoradas por um registrador especial chamado EFLAGS, que contém vários bits de status, conhecidos como flags.
Após a execução de uma operação aritmética ou lógica, a CPU atualiza esses flags para refletir o resultado da operação. Pense neles como anotações automáticas que o processador faz sobre o último cálculo. Quatro dos flags mais importantes são:
CF(Carry Flag - Flag de Transporte): Ativado quando uma operação com números sem sinal "estoura" o limite do registrador.Exemplo: Em 8 bits, o maior valor sem sinal é 255. A operação
255 + 1resulta em256, que não cabe. O bit que "sobra" ativa o Carry Flag. É o "vai um" da aritmética.OF(Overflow Flag - Flag de Overflow): Ativado quando uma operação com números com sinal "estoura", resultando em uma inversão de sinal logicamente incorreta.Exemplo: Em 8 bits, o maior valor positivo com sinal é 127 (
0111 1111em binário). Se fizermos127 + 1, o resultado binário é1000 0000, que na representação com sinal é interpretado como -128. Somar dois números positivos e obter um resultado negativo é um erro lógico. A CPU detecta essa condição e ativa o Overflow Flag para nos avisar.
SF(Sign Flag - Flag de Sinal): É uma cópia do bit mais significativo do resultado. Como este bit define o sinal de um número, o SF indica se o resultado foi negativo (SF=1) ou não-negativo (SF=0).ZF(Zero Flag - Flag de Zero): Ativado se o resultado de uma operação for exatamente zero. É fundamental para verificar igualdade.
É crucial lembrar: instruções aritméticas e lógicas (add, sub, inc, dec) modificam os flags. Instruções de movimentação de dados (mov, lea) não os afetam.
Agindo com Base nos Flags: Instruções de Salto Condicional
Para agir com base no estado desses flags, o Assembly oferece um conjunto de instruções de salto condicional:
jc/jnc: Salta se houve / não houve Carry (para números sem sinal).jo/jno: Salta se houve / não houve Overflow (para números com sinal).js/jns: Salta se o resultado foi / não foi negativo (Sign).jz/jnz: Salta se o resultado foi / não foi Zero.
Exemplo 1: Verificando o Carry Flag jc
Este exemplo demonstra uma estrutura if/else baseada no Carry Flag.
global _start
section .text
_start:
mov al, 255
add al, 3 ; 255 + 3 = 258, o que causa um "carry" em 8 bits
jc carry_set ; IF (carry == 1) THEN GOTO carry_set
; Bloco ELSE: executado se o carry não ocorrer
mov rdi, 2
jmp exit
carry_set: ; Bloco IF: executado se o carry ocorreu
mov rdi, 4
exit:
mov rax, 60
syscallA instrução add al, 3 tenta armazenar 258 em um registrador de 8 bits (cujo máximo é 255). O valor que "transborda" ativa o Carry Flag. A instrução jc (Jump if Carry) detecta isso e desvia o fluxo para o rótulo carry_set, resultando em rdi valendo 4.
Exemplo 2: Verificando Igualdade com o Zero Flag jz
Uma das maneiras de verificar se dois números são iguais é subtraí-los e ver se o resultado é zero.
global _start
section .text
_start:
mov rcx, 5
mov rdx, 5
sub rdx, rcx ; rdx = 5 - 5. Resultado é 0, ativando o Zero Flag (ZF).
jz zero_set ; IF (resultado == 0) THEN GOTO zero_set
mov rdi, 2
jmp exit
zero_set:
mov rdi, 4
exit:
mov rax, 60
syscallComo 5 - 5 = 0, a instrução sub ativa o Zero Flag. jz (Jump if Zero) detecta o flag e realiza o salto.
ℹ️ A Instrução
cmpUsar
subpara comparar funciona, mas altera o valor do operando de destino. Uma maneira mais limpa de comparar dois valores é com a instruçãocmp(compare).cmp rdx, rcxexecuta a operaçãordx - rcxinternamente, ajusta os flags exatamente comosubfaria, mas descarta o resultado, deixandordxintacto. Na prática,cmpé quase sempre a instrução preferida para comparações.
Exemplo 3: Criando Laços de Repetição com jnz
Saltos condicionais são a base para a criação de qualquer tipo de laço de repetição.
global _start
section .text
_start:
mov rcx, 5 ; rcx será o nosso contador
mov rdi, 0
loop:
add rdi, 2
dec rcx ; Subtrai 1 de rcx e atualiza os flags
jnz loop ; WHILE (rcx != 0), salta de volta para 'loop'
mov rax, 60
syscallA instrução dec rcx diminui o contador. Ela ativará o Zero Flag apenas quando rcx se tornar zero. A instrução jnz loop (Jump if Not Zero) continuará saltando de volta para o rótulo loop: enquanto o Zero Flag estiver desativado. Quando rcx chega a zero, o flag é ativado, a condição de jnz falha, e o programa sai do laço.
Gerenciamento Manual de Flags
O Assembly também oferece instruções para manipular os flags diretamente, o que é útil em cenários avançados.
clc(Clear Carry Flag): Força CF para 0.stc(Set Carry Flag): Força CF para 1.lahf(Load AH from Flags): Copia os flags de status para o registradorah.sahf(Store AH into Flags): Copiaahde volta para o registrador de flags.
As instruções lahf e sahf são importantes para preservar o estado do processador, por exemplo, antes de chamar uma função externa que pode modificar os flags e interferir na lógica do seu código.
Resumo
- Flags de Status (
CF,OF,SF,ZF) são atualizados por instruções aritméticas para registrar propriedades do resultado (se houve transporte, se foi zero, etc.). - Saltos Condicionais (
jc,jz,jnz, etc.) leem esses flags para tomar decisões e desviar o fluxo de execução, permitindo a criação de lógicas complexas. - A combinação de uma operação que afeta os flags (como
suboucmp) seguida por um salto condicional é o padrão fundamental para implementar estruturas comoif/else,whileeforem Assembly. - A instrução
cmpé a forma preferida para comparar dois valores, pois ela ajusta os flags sem alterar os operandos.