Битовые операции и маски: управление байтом по битам

Байт — восемь независимых переключателей, живущих в одной ячейке. Как дотянуться до одного из них, не тронув остальные? Для этого служат битовые операции: AND, OR и XOR применяются к байту поэлементно, а число-помощник, которое подставляют к ним в пару, называется маской. В курсе маски встречаются дважды: в шинных вентилях (BusAND/BusOR) и в программе чтения кнопок геймпада на уровне 2.23.

байт маска операция результат что сделано 00000101 11111011 AND 00000001 сброшен бит 1 00000101 00000010 OR 00000111 установлен бит 1 00000101 00000010 XOR 00000111 бит 1 переключён 00000101 00000100 AND 00000100 бит 2 был 1 — проверка пройдена Маска = трафарет: единицы — «пропустить/изменить», нули — «не трогать» (для AND — наоборот)
Четыре типовых приёма: AND с «почти нулём» сбрасывает бит, OR с единицей устанавливает, XOR переключает, AND с одиночной единицей проверяет

Битовые операции — это поэлементность

Обычное сложение связывает разряды между собой через переносы. Битовые операции — нет: каждая пара битов (байт, маска) обрабатывается независимо. В i-м разряде результата получается ровно то, что даёт таблица операции для i-х битов операндов. Именно поэтому шинные версии вентилей в курсе — это просто восемь обычных вентилей рядом (для Verilog — generate-for из уровня 4.37).

Таблицы вам уже знакомы: AND даёт 1 только на двух единицах, OR — если есть хотя бы одна, XOR — когда биты различны. Не привычные «числа», а восемь независимых мини-вычислений.

Маска: трафарет для байта

Маска — это второй операнд, подобранный так, чтобы нужные разряды изменились, а остальные — нет. Четыре базовых приёма для работы с битом номер k:

Установить (сделать 1): X OR (1 << k). Единицы маски «рисуют» единицы поверх байта.
Сбросить (сделать 0): X AND NOT(1 << k) — маска из всех единиц, кроме позиции k. Помните 0xFE из статьи о шине? Это маска «всё, кроме последнего бита».
Переключить: X XOR (1 << k). XOR с единицей всегда меняет бит на противоположный.
Проверить: X AND (1 << k). Результат ненулевой — бит был 1; нулевой — был 0.

Геймпад: чтение кнопок масками

Уровень 2.23 «Геймпад» читает кнопки из порта 254: в ответ приходят восемь бит — по одному на кнопку (1 — Вверх, 2 — Вниз, 4 — Влево, 8 — Вправо). Как узнать, нажата ли «Влево»? Наложить маску 4:

LDA 0        ; обнулить аккумулятор
ADD 254      ; Acc = байт кнопок, например 00000101 (Вверх + Влево = 1 + 4 = 5)
AND 4        ; маска 00000100 — оставить только бит Влево
JZ skip      ; ноль? кнопка не нажата — прыгаем
...          ; сюда попадаем, если Влево нажата
skip: HLT

Три строки — и программа «щупает» один конкретный бит восьмибитного байта. Этот же приём двигает змейку на финальном уровне части 2: опрос кнопок, проверка направлений, изменение координат — всё на AND-масках и переходах по флагам из прошлой статьи.

Проверь себя

Какой операцией установить бит 3, не трогая остальные?

OR с маской 00001000: единица «дорисовывается» в нужном разряде, остальные биты OR не меняет.

Почему AND 0xFF не меняет байт, а AND 0x00 стирает его целиком?

AND с единицей оставляет бит как есть, AND с нулём всегда даёт 0. Маска 0xFF — все единицы, 0x00 — все нули.

Чем XOR полезен при работе с битами?

Он переключает: XOR с 1 меняет бит на противоположный, XOR с 0 оставляет. Повторное применение возвращает всё как было.

Попробовать в симуляторе →