Логические операции в ассемблере: команды AND, OR, XOR и NOT

Содержание

  1. 1.Как процессор обрабатывает биты
  2. 2.Логические операции в ассемблере: AND, OR, XOR и NOT
  3. 3.AND в ассемблере: маски и проверка битов
    1. 3.1.Синтаксис команды AND
    2. 3.2.Выделяем и проверяем нужный бит
  4. 4.OR и XOR в ассемблере: включение и переключение разрядов
    1. 4.1.OR — устанавливаем биты
    2. 4.2.XOR — обнуляем регистр за одну команду
  5. 5.NOT в ассемблере: полная инверсия
  6. 6.Типичные ошибки с побитовыми командами
  7. 7.Заключение
Хотите стать фрилансером и начать зарабатывать удаленно?
Регистрируйтесь на Ворк24!
Хотите заказать настройку и доработку сайта?
Эксперты Ворк24 помогут!

Ещё до того, как программа напишет в память первый байт, процессор уже активно работает на уровне отдельных двоичных значений — проверяет EFLAGS, устанавливает маски, обнуляет регистры. Именно для этого и существуют четыре базовые операции: AND, OR, XOR и NOT. Материал пригодится тем, кто изучает ассемблер x86/x64, пишет низкоуровневый код или хочет разобраться, как процессор реально обрабатывает данные.

По данным Intel Software Developer’s Manual (Intel, 2024), логические операции в ассемблере относятся к группе побитовых инструкций ALU и выполняются за один такт процессора — независимо от размера операнда.


Как процессор обрабатывает биты

1.png

Когда вы вызываете логическую инструкцию, операнды поступают на ALU — арифметико-логическое устройство внутри ядра. Там каждый разряд первого значения независимо взаимодействует с соответствующим разрядом второго. Результат немедленно записывается обратно в первый операнд, перезаписывая исходное содержимое.

Это принципиально отличается от арифметики. При сложении перенос распространяется от младших позиций к старшим — результат каждой позиции зависит от соседей. При логических операциях каждая пара разрядов обрабатывается полностью изолированно. Поэтому AND, OR, XOR и NOT работают предсказуемо и не дают побочных эффектов в соседних позициях.

Последовательность выполнения любой логической операции (кроме NOT):

  1. ALU считывает оба аргумента из регистров или памяти
  2. Обрабатывает каждую пару значений независимо
  3. Записывает результат в первый аргумент
  4. Обновляет флаги: SF (знак), ZF (ноль), PF (чётность)
  5. Принудительно обнуляет 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 в ассемблере: маски и проверка битов

2.png

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:

  1. Обнуление регистра: XOR EAX, EAX — компактно и быстро, устанавливает ZF=1
  2. Переключение конкретных позиций: XOR AL, 0Fh — инвертируем нижний нибл, не трогая старший
  3. Обмен двух значений без временной переменной:

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 AXINC 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 — единственная логическая инструкция, которая не меняет флаги. Условный переход после неё реагирует на состояние от предыдущей операции. Это ловушка, в которую попадают регулярно.


Типичные ошибки с побитовыми командами

3.png

Большинство проблем с логическими операциями связаны не с синтаксисом, а с неправильными ожиданиями о SF/ZF и поведении аргументов. Разработчик знает, что инструкция делает с данными, но забывает о побочном эффекте — или его намеренном отсутствии — на регистры состояния.

  1. Несовпадение размерности аргументов. AND AX, BL — ошибка ассемблера. Первый аргумент 16-битный, второй 8-битный. Совмещать нельзя: byte–byte, word–word, dword–dword.
  2. Ожидание, что NOT изменит флаги. NOT их не обновляет. Если после NOT стоит JZ — переход реагирует на предыдущую операцию, а не на результат инверсии.
  3. Попытка сбросить разряд через OR. OR только включает позиции. Для сброса нужна AND с инвертированной маской: NOT маски, затем AND. Или константа напрямую — AND AL, 0F7h для разряда 3.
  4. Потеря исходного значения. Результат всегда записывается в первый аргумент, уничтожая стаявшую запись. Если значение нужно дальше: MOV BX, AX перед вызовом.
  5. XOR reg, reg и неожиданный ZF. После XOR EAX, EAX флаг ZF равен единице. Если через несколько строк идёт JZ, он может сработать из-за этого, а не из-за нужной проверки.
  6. AND вместо XOR для обнуления. AND EAX, 0 работает, но генерирует длинный машинный код. Стандарт — XOR EAX, EAX.
  7. Два адреса памяти. 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 влияют на регистр состояния — значит перестать угадывать поведение кода и начать его контролировать.

Вам нужна биржа фриланса для новичков или требуются разработчики сайтов?

Комментарии

Нет комментариев
Не можешь разобраться в этой теме?
Обратись за помощью к фрилансерам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 1 дня
Безопасная сделка
Прямой эфир