Инструкции условного копирования

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

Нередко сравнение двух значений произодится, чтобы в зависимости от результатов сравнения загрузить в регистр некоторое значение или, наоборот, не загружать это значение, если проверка или сравнение не пройдены. Для упрощения подобных действий процессоры x86-64 поддерживают набор инструкций условного копирования cmov. Инструкции перемещения с проверкой флагов:

  • cmovc / cmovb / cmovnae: копирует значение, если флаг переноса CF = 1

  • cmovnc / cmovnb / cmovae: копирует значение, если флаг переноса CF = 0

  • cmovz / cmove: копирует значение, если флаг нуля ZF = 1

  • cmovnz / cmovne: копирует значение, если флаг нуля ZF = 0

  • cmovs: копирует значение, если флаг знака SF = 1

  • cmovns: копирует значение, если флаг знака SF = 0

  • cmovo: копирует значение, если флаг переполнения OF = 1

  • cmovno: копирует значение, если флаг переполнения OF = 0

Отдельная группа инструкций предназначена для сравнения с копированием. Здесь есть инструкции для сравнения беззнаковых чисел:

  • cmova: копирует значение, если первый операнд больше второго (CF=0, ZF=0)

  • cmovnbe: копирует значение, если первый операнд не меньше и не равен второму (CF=0, ZF=0)

  • cmovae / cmovnc / cmovnb: копирует значение, если первый операнд больше или равен второму (CF=0)

  • cmovnb / cmovnc / cmovae: копирует значение, если первый операнд не меньше второго (CF=0)

  • cmovb / cmovc / cmovnae: копирует значение, если первый операнд меньше второго (CF=1)

  • cmovnae / cmovc / cmovb: копирует значение, если первый операнд не больше и не равен второму (CF=1)

  • cmovbe: копирует значение, если первый операнд меньше или равен второму (CF=1 или ZF=1)

  • cmovna: копирует значение, если первый операнд не больше второго (CF=1 или ZF=1)

И инструкции для сравнения чисел со знаком:

  • cmovg: копирует значение, если первый операнд больше второго (SF=OF или ZF=0)

  • cmovnle: копирует значение, если первый операнд не меньше и не равен второму (SF=OF или ZF=0)

  • cmovge: копирует значение, если первый операнд больше или равен второму (SF=OF)

  • cmovnl: копирует значение, если первый операнд не меньше второго (SF=OF)

  • cmovl: копирует значение, если первый операнд меньше второго (SF != OF)

  • cmovnge: копирует значение, если первый операнд не больше и не равен второму (SF != OF)

  • cmovle: копирует значение, если первый операнд меньше или равен второму (SF != OF или ZF=1)

  • cmovng: копирует значение, если первый операнд не больше второго (SF != OF или ZF=1)

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

  • cmove: копирует значение, если первый операнд равен второму (ZF=1). Аналогичен инструкции cmovz

  • cmovne: копирует значение, если первый операнд не равен второму (ZF=0). Аналогичен инструкции cmovnz

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

global _start

section .text
_start:
    mov al, 255
    mov bl, 3
    add al, bl      ; складываем AL и BL
    jc carry        ; если флаг переноса установлен, переходим к метке carry
    mov rdi, 2      ; если флаг переноса НЕ установлен
    jmp exit
carry:
    mov rdi, 4      ; если флаг переноса установлен
exit:
    mov rax, 60
    syscall 

Здесь складываем 2 числа (регистры AL и BL). И если при сложении устанавливается флаг переноса, то осуществляется переход к метке carry, где в регистр RDI помещается 4. Если флаг переноса не установлен, то в регистр RDI помещается 2, и выполняется завершение программы. С помощью инструкций условного перемещения эту программу можно упростить так:

global _start

section .text
_start:
    mov al, 255
    mov bl, 3
    add al, bl          ; складываем AL и BL
    
    mov rcx, 2          ; вариант, если флаг переноса сброшен (CF = 0)
    mov rdx, 4          ; вариант, если флаг переноса установлен (CF = 1)

    cmovnc rdi, rcx     ; Если CF = 0
    cmovc rdi, rdx      ; Если CF = 1
    mov rax, 60
    syscall 

В регистры RCX и RDX помещаем варианты на случай, если флаг CF установлен и не установлен соответственно. И с помощью инструкций cmovnc/cmovc выполняем копирование:

cmovnc rdi, rcx     ; Если CF = 0
cmovc rdi, rdx      ; Если CF = 1

Аналогичный пример для Windows:

global _start

section .text
_start:
    mov al, 255
    mov bl, 3
    add al, bl          ; складываем AL и BL
    
    mov rcx, 2          ; вариант, если флаг переноса сброшен (CF = 0)
    mov rdx, 4          ; вариант, если флаг переноса установлен (CF = 1)

    cmovnc rax, rcx     ; Если CF = 0
    cmovc rax, rdx      ; Если CF = 1   
    ret    

Другой пример. Поместим в регистр RDI определенное значение в зависимости от результата сравнения (пример для Linux):

global _start

section .text
_start:
    mov rcx, 2
    mov rdx, 2
    cmp rcx, rdx         ; сравниваем RCX и RDX, устанавливается флаг нуля ZF

    mov rcx, 8           ; вариант, если флаг нуля сброшен (ZF = 0)
    mov rdx , 16         ; вариант, если флаг нуля установлен (ZF = 1)

    cmovne rdi, rcx      ; Если ZF = 0
    cmove rdi, rdx       ; Если ZF = 1
    mov rax, 60
    syscall 

В данном случае если числа в регистрах RCX и RDX равны, в регистр RDI помещается число 16, иначе помещается число 8. Аналогичный пример для Windows:

global _start

section .text
_start:
    mov rcx, 2
    mov rdx, 2
    cmp rcx, rdx         ; сравниваем RCX и RDX, устанавливается флаг нуля ZF

    mov rcx, 8           ; вариант, если флаг нуля сброшен (ZF = 0)
    mov rdx , 16         ; вариант, если флаг нуля установлен (ZF = 1)

    cmovne rax, rcx      ; Если ZF = 0
    cmove rax, rdx       ; Если ZF = 1  
    ret    

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