Ассемблер
Как в ассемблер записать отрицательное число
В ассемблере отрицательное число обычно можно записать прямо через минус: mov eax, -5, db -1, dd -100. Assembler сам закодирует значение в дополнительном коде, если размер операнда позволяет это сделать. Главное понимать, сколько байт выделено под число и как потом вы будете его интерпретировать: как signed или как unsigned
Простой пример NASM:
mov eax, -5
В eax окажется битовое представление числа -5 для 32-битного регистра. Это не текст -5, а набор битов
Что такое дополнительный код
Современные x86 и x86-64 хранят отрицательные целые числа в two’s complement, по-русски часто говорят «дополнительный код»
Для 8 бит:
1 = 00000001
-1 = 11111111
-2 = 11111110
Для 32 бит -1 выглядит как:
FFFFFFFFh
Но эти же биты как unsigned означают 4294967295. Поэтому сам набор битов не говорит, «положительное» число перед вами или «отрицательное». Смысл зависит от операции и перехода
Запись в данные
В NASM можно писать так:
section .data
a db -1
b dw -300
c dd -100000
d dq -5
Размеры:
db— 1 байт;dw— 2 байта;dd— 4 байта;dq— 8 байт.
Число должно помещаться в выбранный размер. Например, для signed 8-битного значения диапазон от -128 до 127
Запись в регистр
mov al, -1
mov ax, -1
mov eax, -1
mov rax, -1
В каждом случае размер разный. al — 8 бит, ax — 16 бит, eax — 32 бита, rax — 64 бита. Визуально значение одно, но битовое представление занимает разное количество байт
Как получить отрицательное число через NEG
Инструкция neg меняет знак:
mov eax, 5
neg eax
После этого в eax будет -5. Внутри neg eax фактически делает 0 - eax
Это удобно, когда знак зависит от вычислений, а не от константы в исходном коде
Знаковое расширение
Если маленькое отрицательное число нужно перенести в больший регистр, важен знак. Для этого есть инструкции вроде movsx:
mov al, -5
movsx eax, al
movsx расширяет значение с сохранением знака. Если использовать обычное нулевое расширение, отрицательное число может превратиться в большое положительное
Пример:
mov al, -1
movzx eax, al
После movzx в eax будет 255, потому что байт FFh расширили нулями
Как сравнивать отрицательные числа
Для отрицательных чисел используйте signed-переходы:
cmp eax, -10
jl less_than_minus_10
jg greater_than_minus_10
Переходы jl, jle, jg, jge подходят для signed-логики. Переходы jb, jbe, ja, jae используются для unsigned-логики
Частые ошибки
Записывают число не в тот размер
db -200 не подходит для signed 8-битного числа. Оно не помещается в диапазон
Смотрят на значение как unsigned
Биты FFh могут означать -1 или 255. Нужно знать, как программа трактует эти биты
Используют movzx вместо movsx
Для отрицательных байтов и слов при расширении нужен movsx, если вы хотите сохранить знак
Сравнивают signed-число unsigned-переходом
Для отрицательных чисел почти всегда смотрите в сторону jl, jle, jg, jge
Что почитать дальше по ассемблеру
Если нужен общий маршрут по теме, откройте рубрику ассемблеру. Для соседних задач пригодятся эти разборы:
- CMP, JMP, JE, JNZ: условия и циклы в ассемблере
- I8080, AVR и STM32 на ассемблере: когда идти в нишевые ветки
- MASM, NASM, FASM, SASM: что выбрать новичку
- Ассемблер x86 и x64: как адресуются переменные в стеке