Ассемблер: самый честный язык программирования
Современные языки программирования, вроде 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 | Что делает |
|---|---|---|
| NOP | 0 | Ничего — пауза |
| ADD | 1 | Acc = Acc + RAM[адрес/порт] |
| SUB | 2 | Acc = Acc − RAM[адрес] |
| AND | 3 | Acc = Acc AND RAM[адрес] |
| OR | 4 | Acc = Acc OR RAM[адрес] |
| LDA | 5 | Acc = число (непосредственное значение) |
| STA | 6 | RAM[адрес] = Acc |
| JMP | 7 | Безусловный переход |
| JZ | 8 | Переход, если флаг Zero = 1 |
| LDX | 9 | IX = число |
| LDAX | 10 | Acc = RAM[IX] |
| STAX | 11 | RAM[IX] = Acc |
| JN | 12 | Переход, если флаг Negative = 1 |
| INX | 13 | IX = IX + 1 |
| HLT | 15 | Стоп |
Условные переходы 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 тебе предстоит написать программу, которая читает геймпад и сохраняет кнопки в память. Это первая настоящая программа ввода-вывода — дальше будет только интереснее.