Флаги состояния: Как процессор понимает мир
Арифметико-логическое устройство (АЛУ) умеет отлично считать, но для создания игр этого мало. Игре постоянно нужно проверять условия: закончились ли жизни? Ударилась ли змейка о стену? Стал ли счётчик равен нулю? А геймпад, который ты научился читать в статье про memory-mapped I/O, бесполезен без проверок: мало прочитать кнопку — надо решить, куда двигать персонажа.
Чтобы процессор мог принимать такие решения, АЛУ снабжено специальными сигнальными проводами — флагами состояния. Это как сигнальные ракеты, которые АЛУ запускает после каждой математической операции.
В компьютере Ершова флагов два: Zero и Negative. Никаких скрытых регистров — это обычные провода, которые ты соединишь сам.
Флаг 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 | Результат = 0 | JZ | счётчик жизней дошёл до нуля |
| 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 и объединить его с сигналами дешифратора так, чтобы счётчик команд загружал адрес перехода. После этого твой процессор научится принимать решения.