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

Регистры — это триггерные схемы, встроенные прямо в кристалл CPU. Обращение к ним занимает один такт. Оперативная память — внешняя микросхема: чтобы прочитать значение из RAM, процессору нужно от 50 до 200 тактов только на ожидание ответа.
При частоте 3 ГГц один такт длится ~0,3 нс. Задержка DRAM — 50–70 нс. Разница на два порядка — принципиальное архитектурное решение.
Между регистрами и оперативной памятью стоит кэш — три уровня (L1, L2, L3). L1 расположен прямо в вычислительном ядре и отвечает на запрос за 4–5 тактов. L3 — общий для всех ядер, задержка уже 30–40 тактов. Кэш смягчает разрыв в скорости, но не устраняет его: только регистры работают без единой задержки.
Наглядная аналогия: регистры — это стол разработчика. Всё, с чем работаете прямо сейчас, лежит под рукой. Оперативная память — стеллаж в соседней комнате: за каждым значением нужно идти. Стол маленький, стеллаж большой — поэтому регистров мало.
Размер кристалла и тепловыделение ограничивают их количество: разместить тысячи триггерных схем рядом с вычислительным ядром физически невозможно без роста энергопотребления. Поэтому x86-64 даёт только 16 регистров общего назначения.
Ещё одно отличие от оперативной памяти: регистр не имеет адреса. К нему обращаются по имени прямо в теле инструкции — mov eax, 1 — без разыменования, без загрузки указателя. Поэтому работа с регистрами настолько быстрая.
Регистры в ассемблере: виды и назначение

Регистры классифицируют по назначению, а не по размеру. Размер зависит от разрядности архитектуры: те же названия в 16-битном, 32-битном и 64-битном режиме указывают на регистры разного размера.
Регистры общего назначения
Именно с ними работает большинство инструкций — арифметика, пересылка, сравнение, работа с циклами. Каждый из них имеет традиционную роль, хотя ассемблер не запрещает использовать их для других целей.
| 16-бит | 32-бит | 64-бит | Традиционное назначение |
|---|---|---|---|
| AX | EAX | RAX | Аккумулятор, возврат значений из функций |
| BX | EBX | RBX | База, адресация данных |
| CX | ECX | RCX | Счётчик циклов и строковых операций |
| DX | EDX | RDX | Множитель, делитель, I/O-порты |
| SI | ESI | RSI | Источник строковых операций |
| DI | EDI | RDI | Приёмник строковых операций, первый аргумент (System V) |
| SP | ESP | RSP | Указатель вершины стека |
| BP | EBP | RBP | Указатель фрейма стека |
SP и BP не предназначены для хранения произвольных данных. Запись в них разрушает структуру стека, и отладчик начинает показывать мусор вместо локальных переменных.
Сегментные и специальные регистры
Сегментные регистры (CS, DS, SS, ES, FS, GS) появились в 16-битной эпохе для расширения адресного пространства. В 64-битном защищённом режиме они по-прежнему существуют, но используются иначе: FS и GS задействуют для хранения указателей на поточно-локальные данные.
В отличие от регистров общего назначения, специальные регистры (например,
RIP— указатель инструкций) содержат данные, необходимые для работы самого процессора, включая смещения базовых таблиц и уровни доступа.
EFLAGS / RFLAGS — регистр флагов. Каждый бит — отдельный признак: результат сравнения, факт переполнения, направление строковых операций. Команда CMP записывает результат именно сюда, а не в регистр общего назначения.
EIP / RIP — указатель инструкции. Хранит адрес следующей команды, которую выполнит процессор. Напрямую изменить его нельзя: он обновляется через команды перехода (JMP, CALL, RET).
Как устроен регистр AX и его родственники

Возьмём регистр AX в ассемблере как точку входа — на его примере проще объяснить логику именования во всей группе. AX — 16-битная «версия», EAX — 32-битная (Extended), RAX — 64-битная. Это не три разных регистра: это один физический регистр с тремя способами обратиться к нему.
AX, в свою очередь, делится на два байта: AH (старший байт, биты 8–15) и AL (младший байт, биты 0–7). Деление позволяет работать с символами и портами ввода-вывода, не затрагивая всё значение целиком.
; Записать ASCII-код буквы 'A' в младший байт, ; не изменяя старший mov al, 65 ; 65 \= 'A' в ASCII ; AX теперь: AH без изменений | AL \= 0x41
Аналогичная структура у BX/EBX/RBX, CX/ECX/RCX, DX/EDX/RDX. Традиционная роль CX — счётчик: именно его декрементирует команда LOOP. DX участвует в умножении и делении — хранит старшую часть 32-битного произведения при работе с 16-битными операндами.
При 32-битном делении пара EDX:EAX хранит 64-битное делимое: старшие 32 бита — в EDX, младшие — в EAX. После выполнения div частное оказывается в EAX, а остаток — в EDX. Если забыть обнулить EDX перед делением, результат будет неверным.
Запись в EAX автоматически обнуляет старшие 32 бита RAX — это прямое требование спецификации Intel. Запись в AX или AL старшие биты не затрагивает. Это поведение удивляет новичков и приводит к трудноуловимым ошибкам при переходе с 32- на 64-битный код.
Понимание этой иерархии влияет на качество кода: неправильный выбор размера операнда — одна из самых распространённых причин, по которым ассемблер отказывается собирать файл.
Основные команды для работы с регистрами
Любая инструкция в x86 задействует хотя бы один регистр — либо как источник, либо как приёмник результата. Ниже — команды, с которых начинается практика.
MOV dst, src— скопировать значение. Самая частая инструкция:mov eax, ebxкладёт содержимое EBX в EAX, не изменяя EBX.ADD dst, src / SUB dst, src— сложение и вычитание. Результат записывается в первый операнд:add eax, 5прибавляет 5 к EAX.INC reg / DEC reg— инкремент и декремент на 1. Типично для счётчиков на основе CX/ECX: быстрее и короче, чемadd ecx, 1.XOR reg, reg— обнуление регистра.xor eax, eaxработает быстрее, чемmov eax, 0, и на один байт короче в машинном коде.PUSH reg / POP reg— сохранить регистр на стек и восстановить. Обязательный приём при вызове процедур: сохраняем нужные регистры перед вызовом, восстанавливаем после.CMP op1, op2— сравнить без изменения операндов. Результат уходит в EFLAGS; следом идут команды условного перехода (JE,JNE,JL…).LEA dst, \[expr\]— загрузить вычисленный адрес, не обращаясь к памяти. Полезно для быстрого умножения и вычислений:lea eax, [rbx + rcx*4].
В Intel-синтаксисе порядок всегда «приёмник, источник»: mov eax, 1. В AT&T — наоборот, и регистры получают префикс %: movl $1, %eax. Смешивать синтаксисы нельзя — ассемблер либо выдаст ошибку, либо соберёт не то, что вы написали.
Частые ошибки и как их избежать
Большинство ошибок при работе с регистрами видны ещё на этапе сборки. Вот пять ситуаций, с которыми сталкивается почти каждый, кто начинает писать на ассемблере.
- Несовпадение размеров операндов. Попытка скопировать 32-битное значение в 16-битный регистр вызывает ошибку ассемблера. Причина — явное указание разрядности прямо в имени регистра. Решение: следите за парами:
eaxработает с 32-битными значениями,ax— с 16-битными. - Затирание регистров при вызове функций. Если не сохранить EBX перед
call, после возврата его содержимое может оказаться изменённым — это законно в соответствии с соглашением о вызовах cdecl и System V AMD64 ABI. Решение: перед вызовом внешней функции кладите «caller-saved» данные на стек через PUSH. - Использование SP и BP для хранения данных. Запись произвольных значений в RSP или RBP разрушает фрейм стека. Отладчик перестаёт корректно разворачивать вызовы, переменные теряются.
- Путаница Intel/AT&T синтаксиса.
mov eax, 1(Intel) — приёмник слева.movl $1, %eax(AT&T) — источник слева. Смешение двух синтаксисов в одном файле невозможно; выбирайте синтаксис под инструментарий: NASM/MASM используют Intel, GAS — AT&T. - Игнорирование предупреждений ассемблера. Сообщения о несовпадении типов или нестандартных операциях редко бывают ложными. Каждое предупреждение стоит разобрать до запуска, иначе программа собирается, но работает непредсказуемо.
Перед вызовом любой внешней функции сохраняйте «caller-saved» регистры (в cdecl это EAX, ECX, EDX) через PUSH — и восстанавливайте через POP сразу после возврата. Это ещё не полное соглашение о вызовах, но уже закрывает половину причин «исчезновения» данных.
Заключение
Регистры — не синтаксическая деталь ассемблера, а архитектурная основа. Именно в них процессор держит всё, с чем работает прямо сейчас: числа, адреса, состояние вычислений. Понять их устройство — значит понять, что происходит на каждом такте выполнения программы.
Знание традиционных ролей упрощает чтение чужого кода: увидев CX в теле цикла или AX в точке возврата из функции, вы понимаете намерение автора без комментариев. AH/AL дают доступ к отдельным байтам там, где важна точность — при работе с ASCII, портами, сетевыми пакетами.
Следующий шаг после освоения регистров — соглашения о вызовах и устройство стека. Там регистры задействованы напрямую: какие из них сохраняет вызывающая сторона, какие — вызываемая, и почему это критично для написания надёжных процедур.
Вам нужна биржа фриланса для новичков или требуются разработчики сайтов?


Комментарии