Флаги состояния: Как процессор понимает мир

Арифметико-логическое устройство (АЛУ) умеет отлично считать, но для создания игр этого мало. Игре постоянно нужно проверять условия: закончились ли жизни? Ударилась ли змейка о стену? Стал ли счётчик равен нулю? А геймпад, который ты научился читать в статье про memory-mapped I/O, бесполезен без проверок: мало прочитать кнопку — надо решить, куда двигать персонажа.

Чтобы процессор мог принимать такие решения, АЛУ снабжено специальными сигнальными проводами — флагами состояния. Это как сигнальные ракеты, которые АЛУ запускает после каждой математической операции.

В компьютере Ершова флагов два: Zero и Negative. Никаких скрытых регистров — это обычные провода, которые ты соединишь сам.

Регистр состояния 7 6 5 4 3 2 1 0 N Z N = 1, если результат отрицательный Z = 1, если результат равен нулю
Регистр состояния: по одному биту на каждый факт о последнем результате — N (минус) и Z (ноль) главные для переходов

Флаг Zero: результат равен нулю

Zero загорается (становится равным 1), если результат последнего вычисления оказался ровно нулём. Аппаратно его создаёт детектор нуля BusZero: он проверяет все 8 битов результата, и если все они равны 0 — выдаёт 1.

Это идеальный способ проверить, равны ли два числа: просто вычитаем одно из другого — и если загорелся флаг Zero, значит, они одинаковые.

Флаг Negative: результат ушёл в минус

Negative загорается, когда старший бит (бит 7) результата равен 1. В дополнительном коде это означает отрицательное число. Допустим, мы проверяем координату игрока: если вычесть из неё координату левой границы экрана и результат станет отрицательным — флаг Negative взведён, и процессор мгновенно понимает, что игрок вылетел за пределы поля.

Условные переходы JZ и JN

Флаги бесполезны, если на них не смотреть. Поэтому в наборе команд есть условные переходы: JZ (Jump if Zero — прыгни, если ноль) и JN (Jump if Negative — прыгни, если отрицательно). Полный набор команд мы разберём в статье про ассемблер, а здесь — только эти два.

ФлагКогда равен 1ИнструкцияПример из игры
ZeroРезультат = 0JZсчётчик жизней дошёл до нуля
NegativeБит 7 = 1 (минус)JNкоордината стала отрицательной

Проверить JZ и JN в деле можно в песочнице с готовым процессором: отладчик показывает флаги Z и N на каждом такте.

Аппаратная схема условного перехода

Как условный переход устроен на железе? Возьмём JN — цепочка из трёх элементов. Splitter извлекает бит 7 из выхода АЛУ (флаг Negative). Элемент AND комбинирует сигнал Decoder.JumpCond с этим битом. Элемент OR складывает результат с сигналом JumpUncond (безусловный переход). Итог — сигнал PC.Load.

Если PC.Load = 1, счётчик команд загружает адрес перехода из операнда вместо того, чтобы просто прибавить шаг. Если условие не выполнилось — PC.Load = 0, и программа идёт дальше по порядку.

Вычитание на числах: 5 − 10 = 251

Разберём тест из уровня 2.25. 5 − 10 = −5. В дополнительном коде −5 — это 251, а 251 = 0b11111011. Старший бит (бит 7) равен 1 — значит, флаг Negative взведён.

Программа-тест:

LDA 10     ; Acc = 10
STA 10     ; RAM[10] = 10
LDA 5      ; Acc = 5
SUB 10     ; Acc = 5 - RAM[10] = 5 - 10 = 251
JN neg     ; Negative = 1 (бит 7) — прыжок на neg
HLT        ; если JN не сработал, Acc останется 251
neg:
LDA 99     ; Acc = 99
HLT        ; стоп

По шагам: 1) в RAM[10] записана десятка; 2) из аккумулятора (5) вычитается RAM[10] — получается 251; 3) бит 7 числа 251 равен 1, флаг Negative взведён; 4) JN переводит счётчик команд на метку neg; 5) аккумулятор становится 99. Если бы JN не сработал — остался бы 251, и чекпойнт уровня это заметит.

Счётчик жизней: JZ и Game Over

Вернёмся к примеру с жизнями. Счётчик жизней — 3. Каждая смерть вычитает единицу: 3 → 2 → 1 → 0. Как только результат стал нулём, флаг Zero = 1, и команда JZ уводит программу на экран «Game Over». Пока ноль не наступил, программа прыгает обратно на начало уровня.

Без флагов процессор не умел бы сравнивать числа и принимать решения — все программы были бы линейными и бесполезными.

Частые ошибки

1. Считать, что флаги — это отдельная память: на самом деле это обычные провода от АЛУ.

2. Забывать, что JZ и JN смотрят на последний результат АЛУ: между вычислением и переходом не должно быть других операций.

3. Проверять Negative «на бумаге» как «меньше нуля»: аппаратно это просто бит 7.

Резюме

1. Флаг Zero = 1, когда результат равен 0; его создаёт детектор BusZero.

2. Флаг Negative = 1, когда бит 7 результата равен 1 — отрицательное число в дополнительном коде.

3. JZ переходит, если Zero = 1; JN — если Negative = 1.

4. Аппаратно условный переход — это AND (JumpCond и флаг) с последующим OR (JumpUncond) в сигнале PC.Load.

5. 5 − 10 = −5 = 251 = 0b11111011: бит 7 = 1, Negative взведён.

На уровне 2.25 тебе предстоит собрать схему условного перехода JN: извлечь бит 7 из выхода АЛУ через Splitter и объединить его с сигналами дешифратора так, чтобы счётчик команд загружал адрес перехода. После этого твой процессор научится принимать решения.

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