Ассемблер — язык на стыке машинного кода и человекочитаемого текста. Каждая строка здесь соответствует одной операции процессора: никаких компиляторных абстракций, никакой автоматической оптимизации за вас.
Материал пригодится тем, кто изучает низкоуровневое программирование, разбирает дизассемблированный код или пишет критичные по скорости участки.
По данным Intel® 64 and IA-32 Architectures Software Developer’s Manual (2024), архитектура x86 включает более 300 базовых инструкций — от копирования значения до управления виртуальной памятью. Для большинства задач хватает двух-трёх десятков. В статье разберём команды ассемблера по группам: синтаксис строки, таблица мнемоник, механика пересылки данных, арифметики, логики, переходов. В конце — ошибки, из-за которых код становится непредсказуемым.
Как устроена строка команды ASM

Программа на ассемблере — это последовательность строк. В каждой — ровно одна инструкция или одна директива транслятору. Общий формат:
[метка:] мнемоника [операнд1 [, операнд2]] [; комментарий]
Метка — необязательный именованный адрес; другие строки ссылаются на неё через переход или вызов. Мнемоника — буквенное сокращение операции: MOV (move), ADD (add), JMP (jump). Операнды указывают, с чем работает инструкция.
Операнды бывают трёх видов: регистр (EAX, BL), ячейка памяти (адрес в квадратных скобках: [ebx]) и непосредственное значение — константа от одного байта до двойного слова. Большинство инструкций принимают не больше двух операндов, и оба должны быть одного размера.
Синтаксис Intel и AT&T
Единого стандарта синтаксиса в ассемблере нет. Два самых распространённых — Intel и AT&T.
В Intel-синтаксисе первым идёт получатель, вторым — источник: MOV eax, ebx означает «скопировать EBX в EAX». Именно его используют MASM, NASM и FASM — трансляторы под Windows и Linux. AT&T-синтаксис применяет обратный порядок и суффиксы размера: movl %ebx, %eax. Его по умолчанию генерирует GCC.
Знать оба варианта полезно при чтении дизассемблированного кода из разных инструментов — сразу понятно, с каким синтаксисом работает конкретная утилита.
Метка (адрес в программе) → мнемоника (что сделать) → операнды (с чем работать) → комментарий (зачем).
Команды языка ассемблера: основной список
Все инструкции удобно делить на функциональные группы. Так проще запомнить назначение мнемоники и быстрее найти нужную, когда работаешь со справочником или читаешь чужой код.
| Группа | Мнемоники | Что делает | Пример |
|---|---|---|---|
| Пересылка данных | MOV, XCHG, LEA | Копирует значение или вычисляет адрес | MOV eax, 5 |
| Стек | PUSH, POP | Помещает или извлекает значение | PUSH ebx |
| Арифметика | ADD, SUB, MUL, DIV, INC, DEC | Математические вычисления | ADD eax, ebx |
| Логика | AND, OR, XOR, NOT | Побитовые операции | XOR eax, eax |
| Сдвиги | SHL, SHR, SAR | Сдвиг битов влево или вправо | SHL eax, 2 |
| Управление потоком | JMP, JE, JNE, JZ, CALL, RET, LOOP | Переходы и вызовы подпрограмм | JMP label |
| Сравнение | CMP, TEST | Устанавливает флаги без записи результата | CMP eax, 0 |
| Системные | INT, NOP, HLT | Прерывание, пустая операция, остановка | INT 0x80 |
Большинство программ под x86 строится на первых пяти-шести группах. Системные инструкции требуются реже — при написании драйверов и прямом взаимодействии с операционной системой.
💡 Для написания полноценной программы под Linux или MS-DOS достаточно порядка 20–30 мнемоник из этого списка. Остальные — расширения: SIMD, команды плавающей точки, инструкции виртуализации.
Пересылка данных — MOV, PUSH, POP

MOV — самая часто встречающаяся команда в ассемблерном коде. Она копирует значение источника в получатель, не изменяя источник. Источником может быть регистр, константа или ячейка памяти; получателем — регистр или ячейка памяти, но не два адреса памяти одновременно.
PUSH уменьшает указатель стека ESP на 4 байта (в 32-битном режиме) и записывает значение по новому адресу. POP делает обратное: считывает значение с вершины стека и увеличивает ESP. Пара PUSH/POP — стандартный способ сохранить регистры перед вызовом подпрограммы и восстановить их после.
XCHG обменивает значения двух операндов без промежуточной переменной. При работе с памятью инструкция атомарна — это свойство применяют при реализации примитивов синхронизации в многопоточном коде.
LEA против MOV: когда что брать
MOV с адресом в скобках читает содержимое ячейки памяти. LEA (Load Effective Address) вычисляет адрес и кладёт его в регистр — без фактического обращения к памяти.
MOV eax, \[ebx+4\] ; eax \= значение по адресу ebx+4 LEA eax, \[ebx+4\] ; eax \= сам адрес ebx+4
LEA полезен при вычислении смещений внутри массива или структуры. Процессор выполняет её за один такт без обращения к шине памяти, поэтому инструкцию нередко применяют для быстрого умножения на небольшую константу: LEA eax, [eax+eax*2] даёт EAX × 3 без вызова MUL.
Арифметика и логика в инструкциях ASM

ADD складывает два операнда и записывает результат в первый. SUB вычитает второй из первого. Оба устанавливают флаги: CF (перенос), OF (переполнение), ZF (результат ноль). INC и DEC увеличивают или уменьшают операнд на единицу без изменения CF — это важно в циклах, где флаг переноса должен сохраниться от предыдущей операции.
MUL умножает беззнаковые числа: MUL ebx перемножает EAX и EBX, старшую часть результата кладёт в EDX, младшую — в EAX. DIV делит пару EDX:EAX на указанный делитель; частное идёт в EAX, остаток — в EDX. Перед вызовом DIV нужно явно обнулять EDX, иначе в делимом окажется мусор из предыдущих операций.
❗ При делении (DIV/IDIV) нулевой делитель вызывает исключение #DE (Divide Error) — процессор аварийно прерывает выполнение. Проверяйте делитель через TEST или CMP до вызова DIV.
Логические операции AND, OR, XOR, NOT работают побитово. Три приёма, которые встречаются чаще всего:
- XOR eax, eax — обнулить регистр быстрее и компактнее, чем MOV eax, 0: меньше байт в машинном коде.
- AND eax, 0xFF — оставить только младший байт, обнулив остальные биты регистра.
- SHL eax, 3 — умножить на 8 сдвигом без команды MUL.
Ассемблер как инструмент низкоуровневой разработки позволяет комбинировать эти приёмы там, где каждый такт имеет значение: в обработчиках прерываний, кодеках, криптографических библиотеках.
Управление потоком и переходы
JMP передаёт управление на метку или адрес из регистра. Процессор записывает новое значение в EIP (указатель текущей инструкции) — без сохранения адреса возврата, это безусловный прыжок.
CMP вычитает второй операнд из первого, но результат нигде не записывает. Единственный эффект — установка флагов: ZF, SF, CF, OF. После CMP почти всегда идёт условный переход, который читает нужный флаг и решает, прыгать или нет.
Условные переходы охватывают все комбинации флагов. JE/JZ срабатывают при ZF=1 — операнды равны или результат ноль. JNE/JNZ — при ZF=0. JL реагирует на знаковое «меньше», JB — на беззнаковое. CALL кладёт адрес следующей инструкции в стек и прыгает на подпрограмму; RET извлекает этот адрес и возвращает управление. LOOP декрементирует ECX и повторяет переход, пока счётчик не дошёл до нуля.
💡 JZ и JE — это одна и та же инструкция с опкодом 0x74. Разные мнемоники существуют только для читабельности: JZ пишут после TEST, JE — после CMP.
Типичные ошибки в коде на ASM
Два адреса памяти в одной инструкции. Запись MOV [addr1], [addr2] синтаксически неверна: в одной команде не может быть двух операндов-адресов памяти. Решение — промежуточный регистр: сначала MOV reg, [addr2], затем MOV [addr1], reg.
Несовпадение размеров операндов. MOV ax, [ebx] смешивает 16-битный регистр AX с 32-битным адресом. Ассемблер или выдаст ошибку, или прочитает два байта вместо четырёх — данные окажутся обрезанными. Разрядность регистра должна совпадать с размером данных в памяти.
Забытый RET после CALL. Без RET в конце подпрограммы указатель стека остаётся несбалансированным. Следующий возврат заберёт из стека случайное значение, и процессор прыгнет в произвольный адрес. Это причина необъяснимых падений на реальном железе.
Неинициализированный регистр. Чтение регистра до присвоения ему значения даёт неопределённый результат — особенно опасно в циклах, где начальное значение счётчика влияет на все итерации. Всегда явно устанавливайте стартовое значение через MOV или XOR.
Непредвиденное изменение флагов. XOR eax, eax обнуляет регистр, но одновременно устанавливает ZF=1. Если сразу после стоит условный переход, он может сработать неожиданно. Проверяйте логику флагов после любой операции перед условным ветвлением.
→ Разрядность операндов совпадает
→ Не более одного адреса памяти в инструкции
→ RET в конце каждой подпрограммы
→ Все регистры инициализированы до использования
→ Логика флагов проверена перед условными переходами
Заключение
Ассемблер — язык программирования команды — строится на компактном наборе групп инструкций: пересылка данных, арифметика, логика, управление потоком, работа со стеком. Всё остальное — расширения под специфические задачи: обработка мультимедиа, вычисления с плавающей точкой, инструкции виртуализации.
Практический путь выглядит так: сначала разобраться с форматом строки и типами операндов, затем освоить 20–30 базовых мнемоник, а потом читать дизассемблированный код реальных программ. На этом этапе таблица с группами превращается из шпаргалки в навигатор по незнакомому коду.
Синтаксис различается между трансляторами: NASM и MASM используют Intel-порядок, GCC — AT&T. При переходе между инструментами важнее понимать принципы работы, а не заучивать мнемоники отдельно для каждого инструмента.
Понимание того, как работают основные инструкции ASM, даёт точку опоры при отладке, реверс-инжиниринге и оптимизации горячего кода. Архитектура x86 обратно совместима с 1972 года —MOV работает сегодня ровно так же, как в первых программах на этом языке.
Вам нужна биржа фриланса для новичков или требуются разработчики сайтов?


Комментарии