Comparação de Dados em Assembly NASM com a Instrução CMP
Em programação Assembly, a execução de lógicas condicionais depende da comparação de operandos. A instrução cmp (compare) é o mecanismo padrão para essa finalidade. Ela compara dois operandos, ajusta os flags de status do processador com base no resultado e preserva os valores originais dos operandos.
A sintaxe da instrução é:
cmp operand1, operand2
Os operandos podem ser registradores, valores na memória ou constantes (operandos imediatos), sujeitos às mesmas restrições de endereçamento de outras instruções de dois operandos (por exemplo, ambos não podem ser endereços de memória).
Mecanismo de Operação
A instrução cmp opera executando uma subtração implícita de operand1 - operand2. Os flags de status (ZF, CF, SF, OF) são atualizados como se uma instrução sub tivesse sido executada. No entanto, o resultado dessa subtração é descartado, e o valor em operand1 permanece inalterado.
O estado dos flags após cmp operand1, operand2 reflete a relação entre os dois operandos, permitindo que instruções de salto condicional subsequentes alterem o fluxo de controle do programa.
Instruções de Salto Condicional Pós-Comparação
A escolha da instrução de salto condicional correta após uma cmp é determinada pela interpretação dos dados: se são tratados como inteiros sem sinal (unsigned) ou com sinal (signed).
Saltos para Comparações Sem Sinal (Unsigned)
Utilizados para dados como contadores, tamanhos de buffers ou endereços de memória. A lógica de comparação é baseada nos flags CF e ZF.
je(Jump if Equal): Salta seoperand1 == operand2. (VerificaZF=1).jne(Jump if Not Equal): Salta seoperand1 != operand2. (VerificaZF=0).ja(Jump if Above): Salta seoperand1 > operand2. (VerificaCF=0eZF=0).jb(Jump if Below): Salta seoperand1 < operand2. (VerificaCF=1). É um mnemônico parajc.jae(Jump if Above or Equal): Salta seoperand1 >= operand2. (VerificaCF=0). É um mnemônico parajnc.jbe(Jump if Below or Equal): Salta seoperand1 <= operand2. (VerificaCF=1ouZF=1).
Saltos para Comparações Com Sinal (Signed)
Utilizados para dados que podem representar valores negativos, como temperaturas ou coordenadas. A lógica de comparação é baseada nos flags SF, OF e ZF.
je(Jump if Equal): Salta seoperand1 == operand2.jne(Jump if Not Equal): Salta seoperand1 != operand2.jg(Jump if Greater): Salta seoperand1 > operand2. (VerificaSF=OFeZF=0).jl(Jump if Less): Salta seoperand1 < operand2. (VerificaSF!=OF).jge(Jump if Greater or Equal): Salta seoperand1 >= operand2. (VerificaSF=OF).jle(Jump if Less or Equal): Salta seoperand1 <= operand2. (VerificaSF!=OFouZF=1).
Exemplos de Implementação
Exemplo 1: Verificação de Igualdade A verificação de igualdade é idêntica para ambos os tipos de dados, pois depende exclusivamente do Zero Flag.
global _start
section .text
_start:
mov rcx, 33
cmp rcx, 33 ; Compara rcx com 33. ZF é setado para 1.
je equal ; Salto ocorre, pois ZF=1.
mov rdi, 2 ; Código não alcançado
jmp exit
equal:
mov rdi, 4 ; Código executado
exit:
mov rax, 60
syscallExemplo 2: Distinção entre Comparações Unsigned e Signed
O padrão de bits 0xFFFFFFFFFFFFFFFF pode ser interpretado como o maior inteiro sem sinal de 64 bits ou como -1 (em complemento de dois). A escolha da instrução de salto define o comportamento do programa.
Cenário A: Interpretação Sem Sinal (Unsigned)
Aqui, o padrão de bits em rax é tratado como um número positivo muito grande.
global _start
section .text
_start:
mov rax, -1 ; rax = 0xFFFFFFFFFFFFFFFF
mov rbx, 5
cmp rax, rbx
; Usa-se 'ja' para a comparação sem sinal.
ja rax_is_above ; O salto ocorre, pois rax (unsigned) > rbx.
mov rdi, 1 ; Código não alcançado
jmp exit
rax_is_above:
mov rdi, 100 ; Código executado.
exit:
mov rax, 60
syscallCenário B: Interpretação Com Sinal (Signed)
Aqui, o mesmo padrão de bits em rax é tratado como -1.
global _start
section .text
_start:
mov rax, -1 ; rax representa -1
mov rbx, 5
cmp rax, rbx
; Usa-se 'jl' para a comparação com sinal.
jl rax_is_less ; O salto ocorre, pois -1 < 5.
mov rdi, 1 ; Código não alcançado
jmp exit
rax_is_less:
mov rdi, 200 ; Código executado.
exit:
mov rax, 60
syscallEsses exemplos demonstram que a instrução cmp é neutra. A lógica condicional é determinada pela instrução de salto subsequente, que deve ser escolhida pelo programador de acordo com o tipo de dados sendo manipulado.
Resumo
- A instrução
cmprealiza uma subtração implícita para ajustar os flags de status (ZF,CF,SF,OF) sem modificar os operandos. - A escolha da instrução de salto condicional após uma
cmpé fundamental e depende do contexto dos dados (com ou sem sinal). - Utilizam-se
ja/jb(Above/Below) e suas variantes para comparações sem sinal. - Utilizam-se
jg/jl(Greater/Less) e suas variantes para comparações com sinal. jeejnesão válidas para ambos os tipos de dados.