Сравнение. Инструкция CMP

Последнее обновление: 20.09.2023

Инструкция CMP (от слова compare - сравнить) позволяет сравнить значения и установить флаги. Благодаря чему мы можем использовать результат сравнения для выполнения условного перехода. Она принимает два операнда:

cmp left_operand, right_operand

В качестве операндов могут участвовать регистры, переменные, константы и непосредственные операнды. Следует учитывать, оба операнда не могут быть одновременно переменными, и что константы и непосредственные операнды могут быть размером не более 32 бита, даже если первый операнд представляет 64-битный регистр. При этом оба операнда должны представлять целые числа, числа с плавающей точкой НЕ сравниваются.

Как и инструкция SUB она также вычитает второй операнд из первого операнда и устанавливает флаги кода условия на основе результата вычитания. Отличие состоит в том, что инструкция cmp не сохраняет результат вычитания в первый операнд, поскольку цель инструкции cmp состоит лишь в том, чтобы установить флаги кода условия на основе результата вычитания. То есть фактически посредством инструкции cmp мы сравнивает первый операнд со вторым. Например, возьмем следующую инструкцию

cmp ax, bx

Как здесь будут установлены флаги:

  • Флаг нуля ZF устанавливается, если AX = BX

  • Флаг знака SF устанавливается, если результат отрицательный. При этом установка флага НЕ означает, что AX обязательно меньше BX. Если AX = 7FFFh (32767) и BX = 0FFFFh (-1), то вычитание AX - BX дает отрицательное значение 8000h (поэтому будет установлен флаг знака). Другой пример: есть беззнаковые операнды AX = 0FFFFh и BX = 1. Здесь AX больше, чем BX, но их разница составляет 0FFFEh, что по-прежнему отрицательно. Поэтому для сравнения двух значений со знаком надо использовать вместе два флага - флаг знака и флаг переполнения:

    • Если ((SF = 0) и (OF = 1)) или ((SF = 1) и (OF = 0)), тогда AX меньше, чем BX

    • Если ((SF = 0) и (OF = 0)) или ((SF = 1) и (OF = 1)), тогда AX больше или равно BX

    Таким образом, если флаги SF и OF не равны, то AX меньше, чем BX. Если эти флаги равны, тогда AX больше или равно BX

  • Флаг переполнения OF устанавливается, если при вычитании произойдет переполнение знакового бита.

  • Флаг переноса CF устанавливается, если при вычитании AX - BX потребуется заимствование

Рассмотрим несколько ситуаций:

  1. Сравним 0FFFFh (–1) и 0FFFEh (–2). Инструкция вычисляет выражение (–1) – (–2), что равно (+1). Результат положительный и переполнения не произошло, поэтому оба флага SF и OF равны 0. Соответственно левый операнд (-1) больше или равен правому (-2)

  2. Сравним 8000h (–32 768) и 0001h (1). Инструкция вычисляет выражение –32768 – 1, что равно -32769. Поскольку 16-разрядное целое число со знаком не может представлять это значение, значение переносится на 7FFFh (+32 767) и устанавливает флаг переполнения OF. Результат положительный, поэтому флаг знака SF сбрасывается. SF и OF не равны, левый операнд (–32768) меньше чем правый (1).

  3. Сравним 7FFFh (32767) и 0FFFFh (–1). Инструкция вычисляет выражение 32767 – (–1), что равно -32768 или 8000h. Поскольку произошло переполнение знака, то устанавливает флаг переполнения OF. И поскольку результат отрицательный, устанавливается флаг знака SF. Оба флага SF и OF равны 1. Соответственно левый операнд (32767) больше или равен правому (-1)

  4. Сравним 0FFFEh (–2) и 0FFFFh (–1). Инструкция вычисляет выражение (–2) – (–1), что равно (-1). Переполнения не произошло, поэтому флаг OF равен 0. И так как результат отрицательный, SF равен 1. Соответственно левый операнд (-2) меньше, чем правый (-1)

Пример применения:

.code
main proc
    mov eax, 0
    mov ebx, 1
    cmp eax, ebx ; сраниваем значения регистра EAX и регистра EBX
    jc carry_set
    mov eax, 0
    ret
carry_set:
    mov eax, 1
    ret
main endp
end

Здесь инструкция cmp сравнивает значение из регистра EAX со значением регистра EBX (фактически вычитает из EAX значение EBX). Поскольку по сути выполняется вычитание 0 -1, то происходит вычитание с заимствованием, что приведет к установке флага переноса. Соответственно последующая инструкция jc выполнит переход к метке carry_set.

Инструкции перехода

Для упрощения работы с переходами в ассемблере есть ряд инструкций, которые позволяют более точно определить результат сравнения и выполнить переход на определенную метку:

  • je: проверяет условие ZF == 1 и выполняет переход, если оба операнда равны. Фактически эквивалентна инструкции jz

  • jne: проверяет условие ZF == 0 и выполняет переход, если оба операнда НЕ равны. Фактически эквивалентна инструкции jnz

  • ja / jnbe: проверяет одновременно два условия СF == 0 и ZF == 0 (оба условия должны быть истинными). Выполняет переход, если первый операнд больше второго. Оба операнда беззнаковые.

  • jae / jnb: проверяет условие СF == 0 и выполняет переход, если первый операнд больше или равен второму. Оба операнда беззнаковые. Аналогичен инструкции jnc

  • jb / jnae: проверяет условие СF == 1 и выполняет переход, если первый операнд меньше второго. Оба операнда беззнаковые. Аналогичен инструкции jc.

  • jbe / jna: проверяет одновременно два условия СF == 1 и ZF == 1 (достаточно, чтобы выполнялось хотя бы одно из этих условий). Выполняет переход, если первый операнд меньше или равен второму. Оба операнда беззнаковые.

  • jg / jnle: проверяет одновременно два условия SF == OF и ZF == 0 (оба условия должны быть истинными). Выполняет переход, если первый операнд больше второго. Оба операнда со знаком.

  • jge / jnl: проверяет условие SF == OF и выполняет переход, если первый операнд больше или равен второму. Оба операнда со знаком.

  • jl / jnge: проверяет условие SF != OF (флаги SF и OF не должны быть равны) и выполняет переход, если первый операнд меньше второго. Оба операнда со знаком.

  • jle / jng: проверяет одновременно два условия SF != OF и ZF == 1 (достаточно, чтобы выполнялось хотя бы одно из этих условий). Выполняет переход, если первый операнд меньше или равен второму. Оба операнда со знаком.

В этом списке стоит отметить два момента. Во-первых, инструкции сравнения для беззнаковых чисел и чисел со знаком различаются. Во-вторых, ряд инструкций имеют синонимичные конструкции, которые выполняют одно и то же действие и на бинарном уровне имеют один и тот же код, например, вместо инструкции jb можно использовать jc или jnae.

Пример применения инструкций:

.data
    n1 byte 22
    n2 byte 33
.code
main proc
    mov al, n1
    cmp al, n2      ; сраниваем значения регистра AL и переменной n2
    jne not_equal   ; если значения не равны, переходим к метке not_equal
    mov eax, 0
    ret
not_equal:
    mov eax, 1
    ret
main endp
end

В данном случае помещаем в регистр AL значение переменной n1 и затем сравниваем его в переменной n2

mov al, n1
cmp al, n2

Оба значения представляют тип byte, то есть являются 8-разрядными беззнаковыми числами. С помощью инструкции jne проверяем флаг нуля, и если он НЕ установлен (то есть числа НЕ равны), переходим к метке not_equal:

jne not_equal
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850