Главная /
Ассемблер /
Для чайников /
Введение в Ассемблер /
Адресация памяти в Intel 8086: Регистры BX, SI, DI, BP и работа с типами данных
Программирование на ассемблере для процессора Intel 8086 требует глубокого понимания того,
как процессор обращается к памяти. В отличие от современных процессоров с плоской моделью памяти,
8086 использует адресацию сегмент-смещение. В этой статье мы рассмотрим, какую роль играют
регистры BX, SI, DI, BP
в вычислении адресов, что такое сегмент и смещение, а также как сообщить компилятору (ассемблеру),
с данными какого размера мы работаем, используя директивы BYTE PTR и WORD PTR.
1. Сегментно-смещенная модель памяти
Процессор Intel 8086 имеет 20-битную шину адреса. Это означает, что он может адресовать
220 = 1 мегабайт памяти (адреса от 0 до 0xFFFFF). Однако все внутренние регистры процессора были 16-разрядными, и они могли хранить числа только от 0 до 65535 (64 КБ).
Как же 16-битными регистрами адресовать 20-битное пространство?
Был найден элегантный способ: физический адрес вычисляется как сумма двух 16-битных значений, одно из которых предварительно сдвигается на 4 бита влево (умножается на 16).
Формула вычисления физического адреса:
Физический адрес = (Сегмент << 4) + Смещение
или
Физический адрес = Сегмент * 16 + Смещение
- Сегмент хранится в одном из сегментных регистров: CS (код), DS (данные), SS (стек), ES (дополнительный).
- Смещение (Offset) — это расстояние в байтах от начала сегмента до нужных данных. Оно вычисляется процессором на основе используемого режима адресации и хранится в регистрах общего назначения.
2. Роль регистров BX, SI, DI, BP в вычислении смещения
В командах, работающих с памятью (например, MOV AX, [BX]), процессор должен вычислить смещение. Для этого он может использовать комбинации определенных регистров. Не все регистры для этого подходят — есть строгое разделение.
Регистры для вычисления смещения (Effective Address - EA):
- BX (Base Register): Базовый регистр. Обычно используется как указатель на базу структуры или массива в сегменте данных.
- BP (Base Pointer): Указатель базы стека. По умолчанию работает в связке с сегментным регистром SS (стек).
- SI (Source Index): Индекс источника. Часто используется в строковых операциях, может хранить смещение элемента массива.
- DI (Destination Index): Индекс приемника. Аналогично SI, но для приемника.
Режимы адресации (как комбинировать регистры):
1. Прямая адресация (Direct):
Смещение задается непосредственно константой.
MOV AX, [1234h] ; Смещение = 0x1234
2. Косвенная адресация через регистр (Register Indirect):
Смещение хранится в одном регистре.
MOV AX, [BX] ; Смещение = BX
MOV AX, [SI] ; Смещение = SI
MOV AX, [DI] ; Смещение = DI
MOV AX, [BP] ; Смещение = BP
3. Адресация по базе (Based Addressing):
Смещение = BX (или BP) + константа.
Удобно для обращения к полям структуры, если BX указывает на её начало.
MOV AX, [BX + 4] ; Смещение = BX + 4
MOV AX, [BP + 2] ; Смещение = BP + 2 (обычно для доступа к параметрам функции в стеке)
4. Индексная адресация (Indexed Addressing):
Смещение = SI (или DI) + константа.
Удобно для обращения к элементу массива по индексу.
MOV AX, [SI + 10] ; Смещение = SI + 10 (например, 10-й элемент массива)
5. Адресация по базе с индексированием (Based Indexed Addressing):
Смещение = BX или BP + SI или DI (возможно, + константа).
Самый мощный режим. Удобен для работы с двумерными массивами или таблицами.
MOV AX, [BX + SI] ; Смещение = BX + SI
MOV AX, [BX + SI + 8] ; Смещение = BX + SI + 8
MOV AX, [BP + DI] ; Смещение = BP + DI (работа со стеком)
3. Сегмент по умолчанию: важное отличие BP
Это очень важный момент, который часто приводит к ошибкам у начинающих. Процессор не просто берет любой сегментный регистр. Он использует сегмент по умолчанию в зависимости от того, какой регистр участвует в вычислении смещения.
- Правило 1: Если в вычислении смещения участвует BP, то сегментом по умолчанию является SS (Stack Segment). Это логично, так как BP предназначен для работы со стеком.
- Правило 2: Во всех остальных случаях (если используется BX, SI, DI или просто константа) сегментом по умолчанию является DS (Data Segment).
Пример:
MOV AX, [BX] ; Адрес = DS * 16 + BX
MOV AX, [BP] ; Адрес = SS * 16 + BP
MOV AX, [BX + SI] ; Адрес = DS * 16 + BX + SI
MOV AX, [BP + DI] ; Адрес = SS * 16 + BP + DI
Если вам нужно обратиться к данным через BP, но данные лежат в сегменте данных (DS), вы можете явно указать сегментный префикс:
MOV AX, DS:[BP] ; Теперь адрес будет = DS * 16 + BP
4. Как указать тип данных: BYTE PTR и WORD PTR
Процессор 8086 может работать как с байтами (8 бит), так и со словами (16 бит).
Однако, глядя на инструкцию MOV [BX], 10, процессор (и компилятор) не может понять: мы хотим записать число 10 в байт по адресу BX или в слово (два байта)?
Возникает неоднозначность. Чтобы её разрешить, в ассемблере используются операторы-указатели
типов (PTR — PoinTeR):
BYTE PTR — работать с одним байтом.
WORD PTR — работать с двумя байтами (словом).
DWORD PTR — работать с двойным словом (4 байта) (актуально для 32-битных расширений, но на 8086 используется реже, обычно для дальних указателей или 32-битных данных).
Зачем это нужно?
1. Для однозначности инструкций MOV:
MOV BYTE PTR [BX], 10 ; Записать 10 (0x0A) в байт по адресу DS:BX
MOV WORD PTR [BX], 10 ; Записать 10 (0x000A) в слово (займет байты [BX] и [BX+1])
2. Для работы с разными типами данных из одного места:
; Пусть по адресу BX хранится слово 0x1234
MOV AX, [BX] ; AX = 0x1234
MOV AL, BYTE PTR [BX] ; AL = 0x34 (младший байт)
MOV AH, BYTE PTR [BX+1] ; AH = 0x12 (старший байт)
3. Для арифметических операций:
ADD BYTE PTR [SI], 5 ; Сложить байт по адресу SI с числом 5
SUB WORD PTR [DI], 100 ; Вычесть 100 из слова по адресу DI
Если вы работаете с метками, объявленными как переменные (например, MyVar DB 10),
то ассемблер часто может определить тип автоматически, и PTR не требуется.
Однако при косвенной адресации через регистры он необходим всегда, если только вы не перемещаете
данные из регистра такого же размера (MOV [BX], AX — тут тип понятен, так как AX 16-битный).
Заключение
Понимание сегментно-смещенной адресации и роли регистров BX, SI, DI, BP является ключом к эффективному программированию на ассемблере для x86-16. Запомните главное:
- BX, SI, DI по умолчанию работают с сегментом DS.
- BP по умолчанию работает с сегментом SS (стек).
- Физический адрес всегда вычисляется по формуле
Сегмент * 16 + Смещение.
- Используйте
BYTE PTR и WORD PTR, чтобы сказать ассемблеру, какого размера данные лежат в памяти по вычисленному адресу.
Освоив эти правила, вы сможете легко манипулировать данными в памяти, организовывать сложные структуры данных и понимать, как работают компиляторы с высокоуровневых языков на самом низком уровне.
На этом пока всё. Подключайтесь к группе Основы программирования, или к другим каналам (ссылки ниже), чтобы ничего не пропустить.