Ассемблер

Как работает CMP в ассемблере

CMP в ассемблере работает как вычитание без сохранения результата. Инструкция сравнивает два операнда, мысленно выполняет левый операнд - правый операнд, выставляет флаги процессора и оставляет сами значения без изменений. После cmp обычно сразу идет условный переход: je, jne, jl, jg, jb, ja и другие

Простой пример:

mov eax, 10
cmp eax, 10
je equal

Здесь cmp eax, 10 проверяет, что получилось бы при eax - 10. Так как результат был бы нулевым, выставляется флаг ZF, и переход je equal срабатывает

Что именно делает CMP

Если написать:

cmp eax, ebx

процессор сравнит eax и ebx так, будто выполняет:

eax - ebx

Но результат не запишется обратно в eax. Меняются только флаги. Поэтому после команды:

mov eax, 7
mov ebx, 3
cmp eax, ebx

в eax все еще будет 7, а в ebx все еще будет 3

Какие флаги важны

Для первого понимания достаточно четырех флагов:

  • ZF — zero flag, выставляется, если значения равны;
  • SF — sign flag, показывает знак результата сравнения;
  • CF — carry flag, важен для беззнаковых сравнений;
  • OF — overflow flag, важен для знаковых сравнений.

Условные переходы смотрят на эти флаги. Например, je смотрит на ZF, а jl оценивает сочетание SF и OF

Равенство и неравенство

cmp eax, 5
je is_equal
jne is_not_equal

Если eax равен 5, сработает je. Если не равен, сработает jne

Для проверки равенства не нужно думать о знаковых и беззнаковых числах. Равенство битов одинаково в обоих случаях

Больше и меньше: signed и unsigned

Вот здесь начинается важное различие

Для знаковых чисел:

cmp eax, ebx
jl less_signed
jg greater_signed

Для беззнаковых чисел:

cmp eax, ebx
jb below_unsigned
ja above_unsigned

jl означает «меньше как signed». jb означает «ниже как unsigned». Одни и те же биты могут означать разные числа. Например, байт 0FFh как signed может означать -1, а как unsigned — 255

Почему нельзя вставлять команды между CMP и переходом

Так правильно:

cmp eax, 10
je equal

Так опасно:

cmp eax, 10
add ebx, 1
je equal

Инструкция add тоже меняет флаги. Поэтому je будет смотреть уже не на результат сравнения eax с 10, а на флаги после add

Если между cmp и переходом стоит команда, которая не меняет флаги, код может быть корректным, но новичку лучше держать переход сразу после сравнения. Так легче читать программу и искать ошибку

CMP и обычное вычитание

sub и cmp похожи тем, что оба используют логику вычитания и выставляют флаги. Отличие в том, что sub меняет левый операнд:

sub eax, 3

а cmp только проверяет:

cmp eax, 3

Если нужно сохранить число, используйте cmp. Если нужно действительно вычесть, используйте sub

Частые ошибки

Думают, что CMP меняет регистр

Не меняет. После cmp eax, 5 значение eax остается прежним

Используют не тот переход

Для signed-сравнений используйте jl, jle, jg, jge. Для unsigned — jb, jbe, ja, jae

Ломают флаги перед переходом

Ставьте условный переход сразу после cmp, пока учитесь

Сравнивают разные размеры

Нельзя просто так сравнить 32-битный регистр с 64-битным операндом. Размеры должны соответствовать синтаксису и платформе

Что почитать дальше по ассемблеру

Если нужен общий маршрут по теме, откройте рубрику ассемблеру. Для соседних задач пригодятся эти разборы:

Подписаться
Уведомить о
guest
0 комментариев
Старые
Новые Популярные

Настройки cookie

0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x