Язык ассемблера - Регистры
Регистр — это высокоскоростной блок хранения внутри процессора, и наиболее часто обрабатываемый объект в программировании на языке ассемблера. Понимание регистров — ключевой первый шаг к хорошему изучению языка ассемблера.
Что такое регистр
Регистры встроены внутрь кристалла процессораСверхвысокоскоростное малое запоминающее устройство, используемое для временного хранения инструкций, данных и адресов.
В отличие от памяти, регистры встроены внутрь процессора. Процессор может получать доступ к регистрам почти с нулевой задержкой, тогда как доступ к памяти требует от десятков до сотен тактов.
В языке ассемблера подавляющее большинство операций вращается вокруг регистров — данные загружаются из памяти в регистры, операции выполняются в регистрах, а затем результаты сохраняются обратно в память.
Можно рассматривать регистры как «верстак» процессора. Инструменты на верстаке доступны в любой момент, тогда как память похожа на склад, к которому нужно идти, чтобы взять и положить предметы.
Классификация 32-битных регистров x86
32-битная архитектура x86 предоставляет множество типов регистров, каждый из которых имеет свое назначение. На следующем рисунке показана полная классификация регистров:
Ниже мы подробно рассмотрим каждый тип регистров:
Регистры общего назначения
Регистры общего назначенияЭто наиболее часто используемые регистры, применяемые для хранения данных операций и временных результатов.
x86 предоставляет 8 32-битных регистров общего назначения:
| 32-битный | 16-битный | Младшие 8 бит | Старшие 8 бит (младших 16 бит) | Основное назначение |
|---|---|---|---|---|
| EAX | AX | AL | AH | Аккумулятор, хранит возвращаемые значения функций и результаты арифметических операций |
| EBX | BX | BL | BH | Базовый регистр, часто используется для хранения базовых адресов памяти |
| ECX | CX | CL | CH | Счётчик, часто используется для подсчёта циклов и сдвигов |
| EDX | DX | DL | DH | Регистр данных, хранит старшие результаты умножения и деления |
| ESI | SI | SIL | - | Регистр индекса источника, адрес источника для строковых операций |
| EDI | DI | DIL | - | Регистр индекса назначения, адрес назначения для строковых операций |
| EBP | BP | BPL | - | Указатель базы, указывает на основание текущего кадра стека |
| ESP | SP | SPL | - | Указатель стека, всегда указывает на вершину стека |
Соглашение об именовании: префикс E означает Extended (расширенный до 32 бит), а суффикс X указывает, что его можно разбить на старший и младший байты.
Пример
; Демонстрация доступа к различным частям регистров
section .text
global _start
_start:
mov eax, 0x12345678 ; Полный 32-битный регистр
; В этот момент: EAX = 0x12345678
; AX = 0x5678 (младшие 16 бит)
; AH = 0x56 (старшие 8 бит, то есть старшие 8 бит AX)
; AL = 0x78 (младшие 8 бит)
mov ax, 0xAABB ; Изменить AX (младшие 16 бит)
; В этот момент: EAX = 0x1234AABB (старшие 16 бит остаются неизменными!)
; AX = 0xAABB
; AL = 0xBB
mov al, 0xCC ; Изменить AL (самые младшие 8 бит)
; В этот момент: EAX = 0x1234AACC (изменились только младшие 8 бит)
; AX = 0xAACC
; AL = 0xCC
mov eax, 1
mov ebx, 0
int 0x80
При изменении младших 16 бит 32-битного регистра (например, AX) старшие 16 бит остаются неизменными. Однако, когда 32-битный регистр используется как операнд назначения, все 32 бита перезаписываются. Это распространённый источник ошибок у начинающих.
Сегментные регистры
Сегментные регистры используются для указания текущего используемого сегмента памяти:
| Регистр | Name | Назначение |
|---|---|---|
| CS | Регистр сегмента кода | Указывает на сегмент, в котором находится текущая инструкция |
| DS | Регистр сегмента данных | Указывает на сегмент, в котором расположены данные |
| SS | Регистр сегмента стека | Указывает на сегмент, в котором расположен стек |
| ES | Регистр дополнительного сегмента | Дополнительный сегмент данных |
| FS | Регистр дополнительного сегмента | Общего назначения, часто используется для локального хранилища потока |
| GS | Регистр дополнительного сегмента | Общего назначения, обычно используется для локального хранилища потока |
При программировании в 32-битном защищённом режиме операционная система уже настроила сегментные регистры, поэтому обычно не нужно изменять их вручную.
Регистры указателей и индексов
Эти регистры в основном используются для доступа к памяти и хранения адресов памяти:
| Регистр | Полное название | Назначение |
|---|---|---|
| EIP | Указатель команд | Указывает на адрес следующей команды, которая должна быть выполнена ЦП (недоступен напрямую) |
| ESP | Указатель стека | Указывает на вершину стека; инструкции PUSH/POP автоматически изменяют его |
| EBP | Указатель базы | Указывает на основание стекового кадра текущей функции; используется для доступа к параметрам функции и локальным переменным |
| ESI | Индекс источника | Адрес источника для строковых операций и операций с памятью |
| EDI | Индекс назначения | Адрес назначения для строковых операций и операций с памятью |
ESP и EBP нельзя свободно использовать как обычные регистры общего назначения. ESP указывает на вершину стека, а push/pop/call/ret изменяют его; EBP имеет ключевое значение для доступа к параметрам функции. Произвольное изменение их приведёт к аварийному завершению программы.
Регистр флагов (EFLAGS)
EFLAGSЭто 32-битный регистр, каждый бит которого представляет флаг состояния.
Нельзя напрямую читать или записывать весь EFLAGS, но CPU автоматически обновляет эти биты флагов на основе результатов операций, а инструкции условного перехода используют их, чтобы решить, следует ли выполнять переход.
| Бит флага | Name | Значение |
|---|---|---|
| CF | Флаг переноса | Устанавливается в 1, когда при беззнаковой арифметике возникает перенос/заём |
| PF | Флаг чётности | Устанавливается в 1, когда количество единиц в младших 8 битах результата чётно |
| AF | Флаг вспомогательного переноса | Устанавливается в 1, когда возникает перенос/заём из младших 4 бит в старшие 4 бита |
| ZF | Флаг нуля | Устанавливается в 1, когда результат операции равен 0 |
| SF | Флаг знака | Устанавливается в 1, когда результат операции отрицательный (равен старшему значащему биту результата) |
| OF | Флаг переполнения | Устанавливается в 1 при переполнении знаковой арифметики |
Пример
; Демонстрирует влияние арифметических операций на биты флагов
section .text
global _start
_start:
mov eax, 10
sub eax, 10 ; 10 - 10 = 0
; ZF = 1 (результат равен нулю)
; SF = 0 (результат неотрицательный)
; CF = 0 (нет заёма)
mov eax, 0xFFFFFFFF
add eax, 1 ; 0xFFFFFFFF + 1 = 0x100000000 (превышает 32 бита)
; ZF = 1 (32-битный результат равен 0)
; CF = 1 (возник перенос)
; OF = 0 (нет переполнения с точки зрения знаковых чисел)
mov eax, 1
mov ebx, 0
int 0x80
Соглашения об использовании регистров
В реальном программировании некоторые регистры имеют традиционное применение — это называетсясоглашением о вызовах:
| Регистр | Назначение в соглашении о вызовах |
|---|---|
| EAX | Сохранение возвращаемого значения функции |
| ECX | Счётчик (количество итераций цикла) |
| EDX | Сохранение старшей части результатов деления, расширение EAX |
| EBX, ESI, EDI, EBP | Сохраняемые вызываемой функцией: вызываемая функция должна сохранять и восстанавливать их |
| EAX, ECX, EDX | Сохраняемые вызывающей стороной: вызывающая сторона отвечает за сохранение |
Другие расширенияПри написании собственных ассемблерных программ не обязательно строго следовать соглашению о вызовах. Но если вы смешиваете код с C, необходимо следовать соглашению о вызовах cdecl.