Ещё до того, как программа напишет в память первый байт, процессор уже активно работает на уровне отдельных двоичных значений — проверяет EFLAGS, устанавливает маски, обнуляет регистры. Именно для этого и существуют четыре базовые операции: AND, OR, XOR и NOT. Материал пригодится тем, кто изучает ассемблер x86/x64, пишет низкоуровневый код или хочет разобраться, как процессор реально обрабатывает данные.
По данным Intel Software Developer’s Manual (Intel, 2024), логические операции в ассемблере относятся к группе побитовых инструкций ALU и выполняются за один такт процессора — независимо от размера операнда.
Как процессор обрабатывает биты

Когда вы вызываете логическую инструкцию, операнды поступают на ALU — арифметико-логическое устройство внутри ядра. Там каждый разряд первого значения независимо взаимодействует с соответствующим разрядом второго. Результат немедленно записывается обратно в первый операнд, перезаписывая исходное содержимое.
Это принципиально отличается от арифметики. При сложении перенос распространяется от младших позиций к старшим — результат каждой позиции зависит от соседей. При логических операциях каждая пара разрядов обрабатывается полностью изолированно. Поэтому AND, OR, XOR и NOT работают предсказуемо и не дают побочных эффектов в соседних позициях.
Последовательность выполнения любой логической операции (кроме NOT):
- ALU считывает оба аргумента из регистров или памяти
- Обрабатывает каждую пару значений независимо
- Записывает результат в первый аргумент
- Обновляет флаги: SF (знак), ZF (ноль), PF (чётность)
- Принудительно обнуляет CF и OF
💡 SF, ZF, PF обновляются после AND, OR и XOR. NOT — исключение: она не трогает регистр флагов вообще. CF и OF сбрасываются после каждой из первых трёх. Это важно, если следом стоит условный переход JZ, JNZ или JS — они читают именно эти значения.
Понимание этого поведения избавляет от ошибок при отладке. Если JZ срабатывает неожиданно — первое, что проверяют: какая операция изменила ZF последней. Часто виновата AND с нулевой маской или XOR регистра с собой.
Стоит учитывать и архитектурный контекст: в 64-битном режиме (x86-64) эти операции работают с теми же форматами, что и в 32-битном, но дополнительно поддерживают 64-битные регистры — RAX, RBX и другие. Операция над 32-битным регистром (например, AND EAX, 0Fh) автоматически обнуляет старшие 32 разряда результата. С 8- и 16-битными регистрами этого не происходит — старшая часть RAX не затрагивается.
Логические операции в ассемблере: AND, OR, XOR и NOT
Четыре операции решают разные задачи. AND — маскирование и проверка конкретных позиций. OR — принудительная установка нужных разрядов. XOR — переключение и обнуление. NOT — полная побитовая инверсия. Их объединяет общая модель: каждый аргумент может быть регистром, непосредственной константой или ячейкой памяти — результат всегда попадает в первый из них. Два адреса памяти в одной записи не допускаются: один аргумент обязан быть регистром.
Таблица истинности и флаги
| A | B | AND | OR | XOR | NOT A |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 |
NOT принимает только один аргумент — столбец «B» для неё не применим.
После AND, OR и XOR картина одинакова: SF отражает знаковый разряд результата, ZF устанавливается при нулевом итоге, PF — по чётности единичных позиций. CF и OF сбрасываются.
NOT не меняет ни один флаг — это нестандартное поведение для логической группы, которое регулярно становится источником путаницы. Из-за этой асимметрии NOT выделяется: три операции из четырёх ведут себя одинаково — NOT стоит особняком. Помните это каждый раз, когда ставите условный переход после инверсии.
Практический ориентир по выбору нужной операции: проверить или очистить разряд — AND; включить — OR; переключить или обнулить регистр — XOR; получить дополнение до 1 или инвертировать маску перед AND — NOT. Четыре операции, четыре сценария — лучше не смешивать.
AND в ассемблере: маски и проверка битов

AND в ассемблере — основной инструмент для работы с маской. Нулевые позиции маски гасят соответствующие разряды операнда, единичные — оставляют без изменений. Так можно выделить любую группу разрядов, проверить конкретную позицию или очистить ненужные поля в байте.
Синтаксис команды AND
AND поддерживает несколько форматов записи:
AND reg, reg ⠀⠀⠀⠀; AND AL, BL
AND reg, imm ⠀⠀⠀⠀; AND AL, 0Fh
AND reg, mem ⠀⠀⠀⠀; AND AX, [var]
AND mem, reg ⠀⠀⠀⠀; AND [var], AL
⠀
AND mem, imm ⠀⠀⠀⠀; AND byte [var], 0Fh
Размерность обоих аргументов должна совпадать: AND AX, BL — ошибка, первый аргумент 16-битный, второй 8-битный. Два адреса памяти в одной строке запрещены архитектурой x86.
Выделяем и проверяем нужный бит
Чтобы проверить разряд с номером N, нужна маска с единицей в позиции N и нулями в остальных. После AND результат ненулевой тогда и только тогда, когда исходный разряд был равен единице — и ZF остаётся нулевым.
; Проверяем разряд 3 регистра AL
⠀
MOV AL, 0Ah ⠀⠀⠀⠀⠀⠀; AL = 0000 1010
AND AL, 08h ⠀⠀⠀⠀⠀⠀; маска 0000 1000 — изолируем позицию 3
JZ bit_not_set ⠀⠀⠀; ZF=1 → разряд 3 был равен 0
⠀
; если пропустили переход — разряд 3 установлен
Другой распространённый паттерн — проверка чётности через нулевую позицию:
MOV AX, 15 ⠀⠀⠀⠀⠀; нечётное число
AND AX, 1 ⠀⠀⠀⠀⠀⠀; изолируем 0
JZ is_even ⠀ ⠀ ⠀; ZF=1 → число чётное
AND используется и для очистки группы позиций — например, обнулить нижний нибл байта (полубайт):
AND AL, 0F0h ⠀⠀⠀; сохраняем только старшие 4 позиции AL
📌 AND перезаписывает первый аргумент. Если исходное значение регистра ещё понадобится — скопируйте его заранее:
MOV BX, AX.
OR и XOR в ассемблере: включение и переключение разрядов
XOR и OR в ассемблере решают противоположные задачи. OR принудительно включает нужные позиции, XOR переключает или обнуляет целиком. Синтаксис обеих операций аналогичен AND по форматам аргументов, ограничениям на размерность и обращению к памяти.
OR — устанавливаем биты
Единичный разряд в маске OR принудительно переводит соответствующую позицию в единицу — независимо от предыдущего значения. Нулевая позиция маски не меняет ничего.
; Устанавливаем старший разряд AL
MOV AL, 0Ah ⠀⠀⠀; AL = 0000 1010
OR AL, 80h ⠀⠀ ⠀; AL = 1000 1010⠀⠀
; Объединяем два нибла в байт
MOV AL, 03h ⠀⠀⠀; AL = 0000 0011
OR AL, 50h ⠀⠀⠀ ; AL = 0101 0011
OR не гасит разряды, только включает. Чтобы сбросить конкретную позицию, нужна AND с инвертированной маской. Для сброса разряда 3: AND AL, 0F7h (двоичное 11110111).
XOR — обнуляем регистр за одну команду
Операция исключающего ИЛИ переключает каждую позицию: если маска единица — позиция инвертируется, если ноль — остаётся нетронутой. Известный приём — обнуление регистра:
XOR EAX, EAX ⠀⠀; EAX = 0
Ассемблер кодирует XOR EAX, EAX двумя байтами машинного кода против пяти у MOV EAX, 0. Поэтому эта запись — стандарт в прологах функций и системном коде.
Три типовые задачи для XOR:
- Обнуление регистра:
XOR EAX, EAX— компактно и быстро, устанавливает ZF=1 - Переключение конкретных позиций:
XOR AL, 0Fh— инвертируем нижний нибл, не трогая старший - Обмен двух значений без временной переменной:
XOR AX, BX ⠀⠀⠀; AX = AX ^ BX
XOR BX, AX ⠀⠀⠀; BX = BX ^ (AX ^ BX) = исходный AX
XOR AX, BX ⠀⠀⠀; AX = (AX ^ BX) ^ исходный AX = исходный BX
❗ XOR reg, reg обнуляет регистр, но изменяет состояние флагов: ZF становится единицей, SF и PF пересчитываются. Если сразу после стоит JZ — он сработает всегда.
NOT в ассемблере: полная инверсия
Инструкция NOT ассемблера инвертирует все разряды: нулевые становятся единичными, единичные — нулевыми. Это побитовое дополнение до 1, а не арифметическое отрицание — для последнего в x86 предусмотрена отдельная инструкция NEG.
NOT reg ⠀⠀⠀⠀⠀; NOT AL → инвертирует все 8 позиций
NOT mem ⠀⠀⠀⠀⠀; NOT byte [var]
У инструкции только один аргумент — второй архитектурой не предусмотрен.
После выполнения NOT ни один флаг не меняется. Это главное практическое отличие от NEG и от трёх других логических операций:
- NOT не трогает SF, ZF, PF, CF, OF
- NEG выполняет дополнение до 2 и обновляет весь регистр флагов
- NOT + INC эквивалентен NEG:
NOT AX→INC AXдаёт тот же результат, чтоNEG AX
Типичное применение NOT — получить инвертированную маску перед AND для сброса конкретной позиции:
MOV BL, 08h ⠀⠀; BL = 0000 1000 — маска разряда 3
NOT BL ⠀⠀⠀⠀⠀⠀⠀; BL = 1111 0111 — инвертированная маска
AND AL, BL ⠀⠀⠀; сбрасываем разряд 3 в AL, остальные не трогаем
В алгоритмах контрольных сумм дополнение до 1 используется напрямую. XOR байта данных с 0FFh полностью аналогичен NOT, но через другую инструкцию.
💡 NOT — единственная логическая инструкция, которая не меняет флаги. Условный переход после неё реагирует на состояние от предыдущей операции. Это ловушка, в которую попадают регулярно.
Типичные ошибки с побитовыми командами

Большинство проблем с логическими операциями связаны не с синтаксисом, а с неправильными ожиданиями о SF/ZF и поведении аргументов. Разработчик знает, что инструкция делает с данными, но забывает о побочном эффекте — или его намеренном отсутствии — на регистры состояния.
- Несовпадение размерности аргументов.
AND AX, BL— ошибка ассемблера. Первый аргумент 16-битный, второй 8-битный. Совмещать нельзя: byte–byte, word–word, dword–dword. - Ожидание, что NOT изменит флаги. NOT их не обновляет. Если после NOT стоит JZ — переход реагирует на предыдущую операцию, а не на результат инверсии.
- Попытка сбросить разряд через OR. OR только включает позиции. Для сброса нужна AND с инвертированной маской:
NOTмаски, затемAND. Или константа напрямую —AND AL, 0F7hдля разряда 3. - Потеря исходного значения. Результат всегда записывается в первый аргумент, уничтожая стаявшую запись. Если значение нужно дальше:
MOV BX, AXперед вызовом. - XOR reg, reg и неожиданный ZF. После
XOR EAX, EAXфлаг ZF равен единице. Если через несколько строк идётJZ, он может сработать из-за этого, а не из-за нужной проверки. - AND вместо XOR для обнуления.
AND EAX, 0работает, но генерирует длинный машинный код. Стандарт —XOR EAX, EAX. - Два адреса памяти.
AND [var1], [var2]— недопустимо в x86. Один аргумент обязан быть регистром.
✅ Проверяйте состояние флагов в отладчике сразу после каждой логической операции. CF и OF всегда должны быть нулевыми после AND, OR, XOR. Если ZF неожиданно единичный — ищите причину в предыдущей строке. Большинство ошибок с условными переходами фиксируются в отладчике.
Заключение
AND, OR, XOR и NOT покрывают весь спектр задач с двоичным представлением данных. AND изолирует нужные позиции через маску и сигнализирует через ZF. OR принудительно включает конкретные разряды, не затрагивая остальные. XOR переключает и обнуляет — в том числе регистры за одну компактную инструкцию. NOT инвертирует всё поле за один такт, не обновляя флаги.
Между четырьмя операциями прослеживается принципиальное различие: NOT — единственная, которая оставляет CF, OF, SF, ZF, PF нетронутыми. AND, OR и XOR всегда обнуляют CF и OF, а SF, ZF, PF пересчитывают по результату. Если условный переход после NOT ведёт себя странно —причина в этом.
Без побитовых операций не обходится ни проверка состояния периферийного устройства, ни разбор байта конфигурации, ни оптимизация пролога функции. Понять, как AND, OR, XOR и NOT влияют на регистр состояния — значит перестать угадывать поведение кода и начать его контролировать.
Вам нужна биржа фриланса для новичков или требуются разработчики сайтов?


Комментарии