Ассемблер: самый честный язык программирования

Продвинутые схемы
Песочница с готовым процессором: слева схема из вентилей, справа ассемблер, отладчик и RAM

Современные языки программирования, вроде Python или JavaScript, делают всё, чтобы скрыть от тебя реальное железо. Ты просто пишешь print("Привет"), а язык сам разбирается, где найти память, как передать данные на видеокарту и как управлять процессором. Это удобно, но это магия.

Ассемблер — это полная противоположность. Это самый честный язык в мире. Здесь нет скрытых механизмов, переменных сложного типа или сборщиков мусора. Каждая строчка кода на ассемблере — это ровно одна базовая команда, которую физически понимает дешифратор процессора (opcode).

Написал LDA 10 — и процессор послушно загрузит число 10 в аккумулятор. Но сразу договоримся о честности: LDA N загружает непосредственное значение N — само число. А вот ADD N читает данные по адресу N с шины — даже если там порт ввода-вывода. Одно слово, две разные операции, и об этом надо помнить всегда.

Откуда берётся байт команды

Каждая инструкция — это число. Формула простая: байт = (opcode << 4) | операнд. Верхние 4 бита — код операции, нижние 4 бита — операнд от 0 до 15.

Соберём ADD 5: opcode сложения равен 1, значит, байт = (1 << 4) | 5 = 0x15 = 0b00010101. Вот так строчка кода превращается в один байт памяти ROM.

Набор команд компьютера Ершова

Полный набор инструкций выглядит так:

МнемоникаOpcodeЧто делает
NOP0Ничего — пауза
ADD1Acc = Acc + RAM[адрес/порт]
SUB2Acc = Acc − RAM[адрес]
AND3Acc = Acc AND RAM[адрес]
OR4Acc = Acc OR RAM[адрес]
LDA5Acc = число (непосредственное значение)
STA6RAM[адрес] = Acc
JMP7Безусловный переход
JZ8Переход, если флаг Zero = 1
LDX9IX = число
LDAX10Acc = RAM[IX]
STAX11RAM[IX] = Acc
JN12Переход, если флаг Negative = 1
INX13IX = IX + 1
HLT15Стоп

Условные переходы JZ и JN опираются на флаги АЛУ — про них мы подробно говорили в статье про флаги состояния. А команды LDX, LDAX, STAX и INX работают с индексным регистром — об этом расскажет статья про указатели.

В байтовом формате операнд ограничен значениями 0–15. В игре есть и словесный формат: инструкция занимает два байта — байт opcode и байт операнда 0–255. Именно он используется в продвинутых уровнях, начиная с 2.19.

Python против ассемблера

Сравним. На Python ты пишешь a = 10, и интерпретатор выделяет память, создаёт объект, настраивает счётчик ссылок. На ассемблере LDA 10 означает ровно одно: положить число 10 в аккумулятор. Никакой магии.

Три команды — LDA 10, ADD 20, STA 30 — и ты вручную выполнил то, ради чего Python написал десятки строк на C. Ты больше не просишь компьютер что-то сделать — ты управляешь его электрическими импульсами напрямую.

Пощупать честный язык можно сразу, без прохождения уровней: открой песочницу с готовым процессором, введи те же три команды в отладчике и понажимай «Шаг».

Читаем геймпад

Пора применить честный язык к делу: прочитаем геймпад с уровня 2.23.

LDA 0      ; Acc = 0 — обнуляем аккумулятор
ADD 254    ; Acc = 0 + кнопки (порт геймпада 0xFE)
STA 0      ; сохраняем кнопки в RAM[0]
HLT        ; стоп

Обрати внимание: ADD 254 — это не «прибавить число 254», а «прочитать данные по адресу 254 и прибавить». Разницу между непосредственным значением и адресом важно чувствовать — от неё зависит вся дальнейшая логика.

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

1. Путать LDA N (число) и ADD N (адрес/порт).

2. Забывать про диапазон операнда 0–15 в байтовом формате: старшие биты молча отбрасываются, и LDA 42 превратится в LDA 10.

3. Считать HLT необязательным: без него счётчик команд продолжит читать мусор из ROM.

Резюме

1. Одна строчка ассемблера — одна команда процессора.

2. LDA N — загрузить число N; ADD N — прочитать данные по адресу N.

3. Байт команды: (opcode << 4) | операнд; ADD 5 собирается в 0x15.

4. Полный набор — от NOP до HLT; условные переходы JZ и JN работают на флагах АЛУ.

5. Геймпад читается тремя командами: LDA 0, ADD 254, STA 0.

На уровне 2.23 тебе предстоит написать программу, которая читает геймпад и сохраняет кнопки в память. Это первая настоящая программа ввода-вывода — дальше будет только интереснее.

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