Ассемблер
CMP в ассемблере: как работает сравнение
CMP в ассемблере нужен для сравнения значений перед условным переходом. Он сам никуда не прыгает и не записывает ответ «больше» или «меньше» в отдельную переменную. Он выставляет флаги процессора, а следующая команда перехода решает, что делать дальше
Типичная пара выглядит так:
cmp eax, 100
jl small_value
Смысл: сравнить eax со 100; если eax меньше 100, перейти на метку small_value
Как читать строку cmp
В Intel/NASM-синтаксисе:
cmp eax, 100
читается как:
сравнить eax со 100
Для понимания флагов можно представлять это как:
eax - 100
Но результат не сохраняется. Если в eax было 90, там и останется 90
Пример if на ассемблере
Допустим, нужно сделать аналог:
if (x == 10) {
y = 1;
} else {
y = 0;
}
На ассемблере:
cmp eax, 10
je equal
mov ebx, 0
jmp done
equal:
mov ebx, 1
done:
Здесь eax хранит x, а ebx получает результат. Если eax == 10, переход идет к equal. Если нет, выполняется ветка с mov ebx, 0
Таблица базовых переходов
После cmp a, b удобно читать переходы так:
| Переход | Смысл |
|---|---|
je или jz |
равно |
jne или jnz |
не равно |
jl |
меньше для signed |
jle |
меньше или равно для signed |
jg |
больше для signed |
jge |
больше или равно для signed |
jb |
меньше для unsigned |
jbe |
меньше или равно для unsigned |
ja |
больше для unsigned |
jae |
больше или равно для unsigned |
Главная ошибка — использовать signed-переход для беззнаковых чисел или наоборот
Signed и unsigned на примере
Возьмем байт:
mov al, 0FFh
cmp al, 1
Если считать al как signed, 0FFh можно воспринимать как -1. Тогда -1 < 1
Если считать al как unsigned, 0FFh — это 255. Тогда 255 > 1
Именно поэтому существуют разные переходы. jl и jg подходят для знаковых сравнений, а jb и ja — для беззнаковых
Проверка нуля
Для проверки нуля часто пишут:
cmp eax, 0
je zero_value
jne not_zero
Это понятный вариант для новичка. Позже вы встретите:
test eax, eax
je zero_value
test тоже выставляет флаги, но работает через побитовое И. Для первого знакомства с условиями cmp проще
Как использовать CMP в цикле
mov ecx, 0
again:
add ecx, 1
cmp ecx, 5
jl again
Цикл повторяется, пока ecx < 5. После сравнения переход jl смотрит на флаги и решает, возвращаться к метке или идти дальше
Что проверить при ошибке
Если условие срабатывает не так:
- посмотрите, какой регистр реально сравнивается;
- убедитесь, что порядок операндов правильный;
- проверьте, signed это сравнение или unsigned;
- убедитесь, что между
cmpи переходом нет команды, которая меняет флаги; - проверьте размер операндов: байт, слово, двойное слово или 64-битное значение.
Что почитать дальше по ассемблеру
Если нужен общий маршрут по теме, откройте рубрику ассемблеру. Для соседних задач пригодятся эти разборы:
- Как работает CMP в ассемблере
- CMP в ассемблере
- CMP, JMP, JE, JNZ: условия и циклы в ассемблере
- JP в ассемблере: как работает переход