Машинный код и ассемблер: программа как числа

Как команды процессора записываются числами, чем машинный код отличается от языка ассемблера и почему программа для одного процессора не работает на другом. Кодируем и расшифровываем команды учебного процессора М-8.

Содержание

Машинный код — это программа в том виде, в каком её исполняет процессор: последовательность чисел, каждое из которых означает команду. Язык ассемблера — запись тех же команд короткими словами, чтобы программу мог прочитать человек. По смыслу это одна и та же программа, только в двух записях.

Все команды М-8

В прошлом уроке учебному процессору М-8 хватило четырёх команд. Всего их у него семь.

КодЗаписьЧто делает
0001LOAD адрескопирует число из ячейки в аккумулятор A
0010ADD адресприбавляет к A число из ячейки
0011SUB адресвычитает из A число из ячейки
0100STORE адресзаписывает число из A в ячейку
0101JUMP адресзаписывает адрес в счётчик команд
0110JZ адресзаписывает адрес в счётчик команд, если флаг Z равен 1
0000HALTостанавливает процессор

Такой список называют системой команд процессора. Четырьмя битами можно записать 16 разных кодов, так что девять кодов у М-8 остались свободными. Команды JUMP и JZ меняют порядок выполнения программы, и о них пойдёт речь в следующем уроке. Здесь нам хватит остальных пяти.

Как закодировать команду

Команда М-8 — это 8 битов: слева четыре бита кода операции, справа четыре бита адреса. Чтобы её закодировать, нужны два шага.

  1. Найти код операции в таблице.
  2. Записать адрес четырьмя двоичными цифрами и приписать его справа.

Закодируем STORE 15. Код операции STORE — 0100. Адрес 15 в двоичной записи — 1111. Вместе получается 0100 1111.

Восемь битов удобно записывать двумя шестнадцатеричными цифрами, как в уроке Шестнадцатеричная система. У М-8 это выходит особенно наглядно: первая цифра — код операции, вторая — адрес. 0100 — это 4, 1111 — это F, и команда STORE 15 превращается в 4F.

Байт 0001 1100 разделён на две четвёрки битов: левая, 0001, — код операции LOAD, правая, 1100, — адрес 12. Шестнадцатеричные цифры четвёрок — 1 и C
Рисунок 1 — Старшие четыре бита говорят, что сделать, младшие — с какой ячейкой. В шестнадцатеричной записи это 1C

Расшифровка идёт в обратную сторону. Возьмём байт 3E. Первая цифра 3 — это код 0011, команда SUB. Вторая цифра E — это 14. Значит, 3E — команда SUB 14.

Команде HALT адрес не нужен, и на младшие четыре бита процессор не смотрит. Обычно их оставляют нулевыми: 0000 0000, то есть 00.

Программа как столбец байтов

Пусть у героя игры было 7 жизней, на уровне он нашёл ещё 5 и потерял 2. Программа должна вычислить 7 + 5 − 2. Числа положим в ячейки 12, 13 и 14, а для ответа отведём ячейку 15.

АдресЗапись словамиДвоичный кодШестнадцатеричный код
0LOAD 120001 11001C
1ADD 130010 11012D
2SUB 140011 11103E
3STORE 150100 11114F
4HALT0000 000000
12число 70000 011107
13число 50000 010105
14число 20000 001002
15место для ответа0000 000000

Третий столбец и есть машинный код: именно эти биты лежат в памяти. Всю память М-8 можно выписать шестнадцатью байтами подряд. Такую распечатку называют дампом памяти.

TXT
адреса 0–7:    1C 2D 3E 4F 00 00 00 00
адреса 8–15:   00 00 00 00 07 05 02 00

Процессор выполнит пять команд и остановится. В дампе изменится только последний байт: вместо 00 там окажется 0A — число 10.

Команда или число?

Посмотрите на байт 1C в ячейке 0. Как команда он означает LOAD 12. Но те же биты 0001 1100 — это ещё и число 28. Так что же там лежит?

И то и другое. В ячейке нет пометки «я команда» или «я число». Всё решает то, каким путём процессор до неё добрался:

  • если адрес ячейки пришёл из счётчика команд, её содержимое попадает в регистр команды и выполняется;
  • если адрес указан в команде LOAD, ADD или SUB, содержимое считается числом и идёт в аккумулятор или в АЛУ.

Отсюда берётся частая ошибка. Если забыть HALT, процессор после последней команды не остановится, а возьмёт следующую ячейку. Окажись в ней число 28 — он послушно выполнит LOAD 12. Выручает то, что код HALT состоит из нулей: пустая ячейка 00 для М-8 означает остановку. Как HALT читаются и все числа от 0 до 15, потому что их старшие четыре бита нулевые.

Ассемблер: слова вместо кодов

Писать программу сразу байтами тяжело: коды приходится держать в голове, а в строке 1C 2D 3E 4F ошибку не разглядеть. Поэтому команды записывают словами: LOAD, ADD, SUB. Такие сокращения называют мнемониками, а язык, в котором каждая строка — одна команда процессора, — языком ассемблера. Второй столбец нашей таблицы — это программа на языке ассемблера для М-8.

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

У каждого процессора свой машинный код

Таблицу команд М-8 придумали мы, и никакой другой процессор о ней не знает. У настоящих процессоров всё устроено так же, только команд у них не семь, а сотни: умножение, сдвиги, сравнения, действия с несколькими регистрами.

Самые известные семейства — x86 и ARM. Процессоры x86 выпускают компании Intel и AMD, они стоят во многих настольных компьютерах и ноутбуках. Процессоры ARM работают почти во всех смартфонах. Системы команд у этих семейств разные: отличаются и сами команды, и их коды.

Поэтому программа в машинном коде привязана к своему процессору. Байты, которые для x86 складываются в осмысленные команды, процессор ARM прочитает по своей таблице и получит бессмыслицу. И дамп 1C 2D 3E 4F — программа только для М-8.

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

  • Путают адрес и число. LOAD 12 кладёт в A не число 12, а то, что лежит в ячейке 12.
  • Записывают адрес десятичными цифрами. LOAD 12 — это 1C, а не 112: адрес занимает ровно одну шестнадцатеричную цифру.
  • Считают, что ассемблер один на всех. У каждого семейства процессоров свой язык ассемблера, потому что свои команды.
  • Забывают HALT. Без него процессор продолжит выполнять то, что лежит дальше, даже если это данные.

Коротко

  • Машинный код — программа в виде чисел, которые процессор выполняет как команды.
  • Команда М-8 занимает один байт: первая шестнадцатеричная цифра — код операции, вторая — адрес.
  • Байт сам по себе не команда и не число: командой его делает то, что он прочитан по адресу из счётчика команд.
  • Язык ассемблера записывает команды мнемониками, а программа-ассемблер переводит их в машинный код.
  • У разных процессоров разные системы команд, поэтому машинный код одного не подходит другому.

Все наши программы шли по ячейкам строго подряд. Как заставить процессор вернуться назад или перескочить через несколько команд — в уроке Переходы, условия и циклы.

Использованная литература

  1. Машинный код — Википедия [Электронный ресурс]. — URL: https://ru.wikipedia.org/wiki/Машинный_код (дата обращения: 09.10.2026).
  2. Язык ассемблера — Википедия [Электронный ресурс]. — URL: https://ru.wikipedia.org/wiki/Язык_ассемблера (дата обращения: 09.10.2026).
  3. x86 — Википедия [Электронный ресурс]. — URL: https://ru.wikipedia.org/wiki/X86 (дата обращения: 09.10.2026).
  4. ARM (архитектура) — Википедия [Электронный ресурс]. — URL: https://ru.wikipedia.org/wiki/ARM_(архитектура) (дата обращения: 09.10.2026).