Как выучить ассемблер с нуля: 15 команд и учебный эмулятор

Почему ассемблер кажется сложным

Ассемблер окружён репутацией предмета для избранных. Учебники подают его через x86: многотомные мануалы, регистры с именами вроде EAX, десятки режимов адресации, файлы, линкеры и директивы вроде section .data. Это реальный инструментарий, на нём пишут драйверы и ядра. К самой идее ассемблера всё это отношения не имеет.

Идея помещается в один абзац. Процессор читает из памяти число-команду, исполняет её и переходит к следующей. Режимы адресации, конвейеры и кеши — наращивания поверх этой модели, появившиеся за десятилетия ради совместимости со старыми процессорами. Уберите наследие, оставьте модель, и изучение становится посильным: именно это делает учебный эмулятор.

Тему «что ассемблер открывает в привычных высокоуровневых языках» мы разбирали в статье «Ассемблер: самый честный язык программирования». Здесь — практический маршрут: с чего начать, в каком порядке двигаться и какие инструменты для этого нужны.

Что даёт учебный эмулятор

Чтобы обучение шло без тупиков, от учебного эмулятора нужны четыре вещи:

Эмулятор «Компьютера Ершова»

Эмулятор работает в браузере и ничего не требует устанавливать: ни компиляторов, ни виртуальных машин, ни учётных записей. Процессор 8-битный, с гарвардской архитектурой: команды и данные хранятся в отдельных памятях, ROM для инструкций и RAM для данных, по 256 ячеек.

Процессор не выдают готовым: на уровнях 16–18 вы собираете его сами из уже построенных узлов, от сумматора до дешифратора инструкций. После сборки ассемблер перестаёт быть абстракцией: команда LDA физически проходит через дешифратор и шины, которые вы соединили своими руками.

Команды кодируются двумя форматами. В байтовом вся инструкция занимает один байт: старшие четыре бита хранят код операции, младшие — операнд от 0 до 15. В словесном (он открывается с уровня 19) инструкция занимает два байта, и операнд расширяется до 0–255.

Симулятор логики, из которого вырастает вся игра, доступен отдельно: /tools/logic-simulator/. Начать играть: /learn/.

Первая программа: три действия и стоп

Вот программа, которая помещается в четыре строки:

LDA 7      ; ACC = 7 — загрузить число 7 в аккумулятор
ADD 0      ; ACC = ACC + RAM[0]
STA 0      ; RAM[0] = ACC — сохранить сумму
HLT        ; остановить процессор

Программа складывает число 7 с содержимым нулевой ячейки памяти и записывает сумму обратно. Введите её в отладчике: откройте песочницу с готовым процессором, этот листинг уже будет в редакторе, и нажмите «Шаг». Тот же процессор в игре собирается на уровне 25 «Проверка на минус».

После первого шага строка регистров изменится: PC = 02 (в словесном формате каждая команда занимает два байта), ACC = 7, флаги Z и N пока нулевые. Строка над кодом расшифрует сделанное: «LDA 7: ACC 0 → 7». RAM заполнена нулями: до STA программа ещё не дошла.

Отладчик как тренажёр

Отладчик ассемблера «Компьютера Ершова»: программа из четырёх команд, строка справки «LDA 7: ACC 0 → 7», регистры PC и ACC, раскрытый RAM-вьюер
Отладчик ассемблера: шаг, регистры, RAM и живая справка по текущей инструкции

Отладчик превращает исполнение программы в наблюдаемый процесс. Кнопка «Шаг» исполняет одну инструкцию, индикатор фазы показывает, где процессор находится: на «Выборке» он читает команду из ROM, на «Исполнении» выполняет её. Строка регистров собирает всё состояние в одном месте: PC, аккумулятор ACC, индексный регистр IX, флаги Z и N, счётчик тактов.

Слева от каждой строки кода стоит её адрес в ROM в шестнадцатеричном виде: 00, 02, 04. Клик по адресу ставит брейкпоинт: автопрогон остановится прямо перед этой инструкцией. Для длинного цикла это способ добраться до интересного места, не кликая «Шаг» сто раз.

Раскрытый блок RAM показывает все 256 ячеек в hex. Клик по ячейке открывает правку, двойной клик переключает формат между hex и dec, изменённые ячейки подсвечиваются.

Живая справка занимает строку между регистрами и кодом. До запуска она объясняет, что сделает текущая инструкция; после шага показывает результат со значениями «до → после». Значок «?» рядом открывает глоссарий по команде. Искать мнемонику в документации не понадобится: ответ лежит в одной строке над кодом.

Маршрут обучения

Команды открываются в игре постепенно, и порядок стоит соблюдать: каждая опирается на предыдущую.

  1. Арифметика и память. LDA, ADD, STA: загрузить, сложить, сохранить. Первые программы части 2 строятся на этой троице.
  2. Ветвления и циклы. JMP и условные переходы JZ, JN — основа любой логики; разбор с примерами в статье «Ветвления и циклы (JZ, JN, JMP)».
  3. Флаги Z и N. Условные переходы опираются на флаги АЛУ; как процессор узнаёт про ноль и минус, разобрано в статье «Флаги состояния: Как процессор понимает мир».
  4. Указатели. Индексный регистр IX и команды LDX, LDAX, STAX, INX открывают косвенную адресацию, массивы и строки; подробности в статье «Указатели и индексные регистры (IX)».
  5. Память и порты. Один и тот же адрес может указывать и на ячейку RAM, и на устройство ввода-вывода; как это устроено, рассказывает статья «Memory-Mapped I/O: Гениальный обман процессора».
  6. Геймпад и «Змейка». На уровне 23 программа впервые читает кнопки, на уровне 29 из команд собирается полноценная игра — финальный экзамен части 2.

Коды операций всех пятнадцати команд и правила синтаксиса собраны в справочнике «Руководство по ассемблеру»; держите его под рукой как настольную таблицу. Ориентировочная длительность каждого уровня указана в программе курса; по ней удобно прикинуть, сколько времени попросит весь маршрут.

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

1. Путать число и адрес. LDA 7 загружает само число 7. ADD 0 прибавляет содержимое ячейки 0, а в ней может лежать что угодно. Первые странные результаты почти всегда отсюда.

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

3. Забывать HLT. Без команды остановки процессор дочитает память за концом программы: нулевые байты исполняются как NOP, затем счётчик переполнится и программа начнёт выполняться заново.

Частые вопросы

Сколько команд в учебном ассемблере?

Пятнадцать: арифметика (ADD, SUB, AND, OR), работа с памятью (LDA, STA), переходы (JMP, JZ, JN), индексный регистр (LDX, LDAX, STAX, INX), NOP и HLT. Полная таблица с кодами операций есть в справочнике.

Нужно ли что-то устанавливать?

Нет. Эмулятор и отладчик работают в браузере, прогресс хранится локально. Скачивать компиляторы и настраивать окружение не нужно.

Чем учебный ассемблер отличается от настоящего?

Учебный процессор исполняет команды по тому же циклу выборки и исполнения, что и промышленные x86 или ARM; понятия регистров, флагов, переходов и косвенной адресации совпадают. Разница в объёме: x86 несёт тысячи инструкций и режимов адресации, накопленных за десятилетия совместимости; учебный процессор укладывается в пятнадцать команд, каждая из которых видна в собранной схеме. Базовые представления переносятся напрямую: меняется синтаксис и инструментарий, модель остаётся.

Сколько времени занимает изучение?

Зависит от стартовой базы и темпа. Ориентировочная длительность каждого уровня указана в программе курса: первые программы на ассемблере идут после сборки процессора (уровни 16–18), финальная «Змейка» закрывает уровень 29.

Проверь себя

Чем операнд LDA отличается от операнда ADD?

LDA берёт непосредственное значение: LDA 7 кладёт в аккумулятор число семь. ADD читает данные по адресу: ADD 7 прибавляет содержимое ячейки 7 (или порта с тем же номером).

Что произойдёт без HLT?

Процессор продолжит читать память за концом программы. Нулевые байты исполняются как NOP, после конца памяти счётчик переполнится и начнёт программу заново.

Зачем нужны флаги Z и N?

АЛУ выставляет их после каждой операции: Z сигнализирует нулевой результат, N отрицательный. Переходы JZ и JN смотрят на флаги и решают, продолжить исполнение по порядку или прыгнуть по адресу из операнда. На них держатся все ветвления и циклы.

Что можно сделать уже сегодня: открыть песочницу с процессором и ввести первую программу из четырёх строк. Сборку процессора своими руками игра предлагает на уровнях 16–18. Каждая её команда видна и в коде, и в схеме, а результат останется в RAM[0] — на него можно посмотреть глазами в отладчике.

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