Вопрос № 10 Способы адресации в ПЭВМ с 32-разрядной архитектурой
Системы адресации, используемые в персональных ЭВМ, будут рассмотрены на примере 32-разрядного процессора 80386 фирмы Intel.
МП 80386 представляет собой высокопроизводительный 32-разрядный микропроцессор. Он отличается полной 32-разрядной архитектурой с физическим адресным пространством 4 Гбайт и встроенными средствами страничной виртуальной памяти, МП 80386 может адресовать 64 Гбайт виртуальной памяти.
Система команд МП 80386 содержит 9 категорий команд:
пересылки данных,
арифметических,
сдвига,
обработки строк,
обработки битов,
передачи управления,
поддержки языков высокого уровня,
поддержки операционной системы,
управления процессором.
Длина команды в среднем составляет 2-3 байта. Операнды могут иметь длину 8, 16 или 32 разряда.
МП 80386 реализует сегментную организацию памяти, при которой физический адрес ячейки вычисляется путем сложения базового адреса сегмента и относительного адреса ячейки внутри сегмента. Базовый адрес определяется содержимым 16-разрядного сегментного регистра и зависит от режима работы микропроцессора.
Если микропроцессор работает в режиме обработки 16-разрядных данных (режим реальных адресов или режим виртуального процессора 8086), то 20-разрядный базовый адрес формируется путем сдвига содержимого сегментного регистра на четыре разряда влево. Т.е. если в сегментном регистре содержится число 45F7, то базовый адрес будет 45F70.
Если микропроцессор работает в режиме обработки 32-разрядных данных (защищенный режим), то 32-разрядный базовый адрес содержится в дескрипторе, выбор которого из таблицы дескрипторов осуществляется с помощью селектора - содержимого соответствующего сегментного регистра. В зависимости от типа обращения к памяти производится выбор сегментного регистра и способа определения относительного адреса. Для некоторых способов обращения к памяти возможны варианты выбора сегментных регистров. Эти варианты могут быть выбраны с помощью префикса замены сегмента SEG. На мнемонике ассемблера это выглядит просто как DS:[?], ES:[?], CS:[?], FS:[?] и т.д. В качестве относительного адреса используется содержимое регистров EIP(IP), ESP(SP), ESI(SI), EDI(DI) или эффективный адрес EA, который формируется в соответствии с заданным способом адресации (косвенный, индексный и т.д.). 1. Выборка команд. Сегментный регистр CS (Code Segment) Относительный адрес EIP(IP) (Instruction Pointer) 2. Обращение к стеку. Сегментный регистр SS (Stack Segment) Относительный адрес ESP(SP) (Stack Pointer) 3. Адресация операнда. Сегментный регистр DS или (CS,SS,ES,FS,GS) Относительный адрес EA 4. Адресация элемента строки-источника. Сегментный регистр DS или (CS,SS,ES,FS,GS) Относительный адрес ESI(SI) 5. Адресация элемента строки-приемника. Сегментный регистр ES Относительный адрес EDI(DI) 6. Адресация операнда с использованием в качестве базового регистра EBP(BP) или ESP(SP). Сегментный регистр SS или (CS,DS,ES,FS,GS) Относительный адрес EA Эффективный адрес операнда EA является 16- или 32-разрядным и формируется в зависимости от значения определенных полей в представлении команды. В общем случае EA образуется путем сложения трех компонент:
- содержимого базового регистра EBP(BP) или EBX(BX); - содержимого индексного регистра ESI(SI) или EDI(DI); - 8-, 16- или 32-разрядного смещения, заданного непосредственно в команде. В разных случаях для формирования EA используются либо все, либо часть этих слагаемых.
Теперь рассмотрим конкретные способы адресации.
НЕПОСРЕДСТВЕННАЯ АДРЕСАЦИЯ. В качестве операнда используется один, два или четыре последних байта команды. Такой способ адресации реализуется при выполнении ряда команд пересылки (MOV, PUSH), арифметических операциях (ADD, ADC, SUB, SBB, CMP, IMUL), и логических (AND, OR, XOR, TEST).
Рассмотрим простой пример - умножение содержимого регистра на константу: Регистры до выполнения IMUL AX,5 AX = 10 После... AX = 50
РЕГИСТРОВАЯ АДРЕСАЦИЯ. При этом способе адресации операндом берется содержимое регистра. Например, для операции занесения на стек, реализация регистрового способа адресации будет выглядеть как PUSH DS. Регистры до выполнения PUSH DS DS = 5678h SP = FFFEh SS:FFFE = 0000h После... DS = 5678 SP = FFFDh SS:FFFD = 5678 SS:FFFE = 0000 Как видно из примера, значение регистра DS было занесено на стек.
КОСВЕННО-РЕГИСТРОВАЯ АДРЕСАЦИЯ. При такой адресации относительный адрес содержится в индексном (SI, DI, ESI, EDI) или базовом (BX, BP, EBX, EBP) регистрах или в регистрах общего назначения EAX, ECX, EDX.
Адрес операнда вычисляется как сегмент:смещение. Смещение представлено эффективным адресом. Для примера рассмотрим команду MOV EBX,[EDI], которая пересылает содержимое ячейки DS:[EDI] в регистр EBX. В качестве размера операнда берется двойное слово - 4 байта. Регистры до выполнения MOV EBX,[EDI] EBX = 5678h EDI = 0100h DS:0100h = 1221h (edi) После... EBX = 1221h <——— 1221h EDI = 0100h DS:0100h = 1221h Пример в комментариях не нуждается. Число, адрес которого DS:EDI было успешно занесено в регистр EBX. Регистр DS использовался по умолчанию.
ПРЯМАЯ АДРЕСАЦИЯ. При этом способе адресации смещение в сегменте до операнда задано в виде слова или двойного слова в коде команды. Для примера возьмем операцию MOV EAX,[1994h], пересылающую двойное слово по адресу DS:1994 в регистр EAX. Регистры до выполнения MOV EAX,[1994h] EAX = 0000h DS:1994h = 5000h После... EAX = 5000h <—— 5000h DS:1994h = 5000h Из примера видно, что число, прямо адресованное как ds:[1994h] было скопировано в заданный регистр.
БАЗОВАЯ АДРЕСАЦИЯ. Относительный адрес операнда формируется при сложении содержимого базового регистра с непосредственным смещением. Смещение может быть представлено словом или двойным словом. Рассмотрим инструкцию ADD AX,[BP+10h], которая к содержимому регистра AX прибавляет число, адрес которого DS:[BP+10h]... Регистры до выполнения ADD AX,[BP+10h] AX = 0067h BP = 0100h DS:0100h = 0000h (bp) DS:0101h = 0001h · · · · · · DS:0109h = 0009h DS:0110h = 0010h (bp+10h) После... AX = 0077h <—— (67h+10h = 77h) BP = 0100h DS:0100h = 0000h DS:0101h = 0001h · · · · · · DS:0109h = 0009h DS:0110h = 0010h Как видно из примера, к содержимому BP было добавлено 10h и получилось смещение 110h, по которому и было взято число, прибавленное к AX.
ИНДЕКСНАЯ АДРЕСАЦИЯ. Примерно тоже самое, что и базовая адресация, однако здесь и используются индексные регистры (SI,DI) и смещение заданное байтом или словом. При формировании 32-разрядных адресов, в качестве базового или индексного регистра может использоваться любой из регистров EAX, ECX, EDX, EBX, EBP, ESI, EDI..
Для примера возьмем инструкцию MOV WORD PTR ES:[DI+2],AX, которая зашлет слово из AX по адресу ES:[DI+2]. Регистры до выполнения MOV WORD PTR ES:[DI+2],AX AX = 0099h DI = 000Dh ES:000Dh = 0000h (di) ES:000Fh = 0000h (di+2) После... AX = 0099h DI = 000Dh ES:000Dh = 0000h ES:000Fh = 0099h <—— 99h Адрес ячейки ES:000Fh был посчитан как содержимое DI плюс 2 = F. Число 99h из регистра AX успешно скопировано в эту ячейку памяти.
БАЗОВО-ИНДЕКСНАЯ АДРЕСАЦИЯ. При использовании этого способа относительный адрес образуется путем сложения содержимого базового (BX,BP) и индексного (SI,DI) регистров. Например, для операции MOV AX,[BP+SI] мы получим: Регистры до выполнения MOV AX,[BP+SI] AX = 00AAh BP = 0100h SI = 0050h DS:0100h = 0001h (bp) DS:0150h = 0002h (bp+si) После... AX = 0002h <—— 02h BP = 0100h SI = 0050h DS:0100h = 0001h DS:0150h = 0002h Адрес ячейки DS:0150h получен путем сложения значений регистров BP и SI, после чего число из этой ячейки памяти был загружен в регистр AX.
БАЗОВО-ИНДЕКСНАЯ АДРЕСАЦИЯ СО СМЕЩЕНИЕМ. Это вариант базово индексной адресации, при котором к относительному адресу прибавляется 8- или 16-разрядное смещение. Дополнительные способы адресации реализуются при использовании 32-разрядных адресов, когда задано специальное поле (SIB) в коде команды. Тогда возможны конструкции типа [EAX+EBX], [EAX+EAX], [ECX+EDX]. В качестве индексного регистра можно использовать любой регистр, кроме ESP. Содержимое этого регистра умножается на масштабный коэффициент F, т.е. сдвигается влево на число разрядов 0,1,3 или 4. Значение F зависит от размера данных.
Для примера рассмотрим инструкцию MOV EAX,[EAX+EAX]. Регистры до выполнения MOV EAX,[EAX+EAX] EAX = 0010h DS:0010h = 000Ah (eax) DS:0020h = 000Bh (eax+eax) После... EAX = 000Bh <—— 0Bh DS:0010h = 000Ah DS:0020h = 000Bh Базовый адрес берется из EAX, складывается со смещением из EAX и двойное слово по адресу DS:0020h заносится в EAX.
ИНДЕКСНАЯ АДРЕСАЦИЯ С МАСШТАБИРОВАНИЕМ. При использовании этого способа адресации относительный адрес формируется при сложении масштабированного индекса (содержимого индексного регистра) и 32-разрядного смещения. Поясним идею масштабирования индекса: содержимое индексного регистра умножается на масштабный коэффициент, чтобы правильно адресовать данные размером более одного байта.
Для примера рассмотрим фрагмент программы, осуществляющей работу с цепочкой двойных слов. Start: jmp Begin Var dd 5 dup (0) Begin: mov edi,2; 1 mov [Var+edi*4],12345678h; 2 Переменная Var представляет собой массив из 5-ти 32-разрядных слов. В строке 1 мы заносим в индексный регистр 2, имея в виду обработку второго элемента массива, а далее в строке 2 мы заносим во второй элемент число 1234567h. На языке Паскаль это выглядело бы как Y[2]:=$1234567. Если бы мы не произвели масштабирование, то число 1234567h было бы занесено по адресу [Y+2], а при масштабировании реальный адрес был вычислен как [Y+2*4] т.е. [Y+8] и число попало "по адресу". Использование масштабирования играет огромную роль при работе с массивами слов и двойных слов, избавляя программиста от необходимости дополнительного индексирования переменных и работы с удвоенными и учетверенными индексами элементов. Все это за него выполняет процессор, сокращая и размер кода и скорость его выполнения.
БАЗОВО-ИНДЕКСНАЯ АДРЕСАЦИЯ С МАСШТАБИРОВАНИЕМ Относительный адрес формируется сложением масштабированного индекса и базы, в качестве которой используется содержимое одного из регистров EAX,EBX,ECX,EDX,ESI или EDI. Приведем пример такой адресации данных: Инструкция MOV [EAX+EDI*4],666h поместит число 666h по адресу EAX плюс учетверенное содержимое EDI. Регистры до выполнения MOV [EAX+EDI*4],666h EAX = 0100h EDI = 0002h DS:0100h = 000Ah (eax) DS:0104h = 000Bh DS:0108h = 000Ch (eax+edi*4) После... EAX = 0100h EDI = 0002h DS:0100h = 000Ah (eax) DS:0104h = 000Bh DS:0108h = 0666h <—— 666h Масштабирование позволило адресовать именно 2-е 32-разрядное слово, а не записать число 666h по адресу EAX+2, т.е. 102h, который попадает по середине двух элементов массива.
БАЗОВО-ИНДЕКСНАЯ АДРЕСАЦИЯ СО СМЕЩЕНИЕМ И МАСШТАБИРОВАНИЕМ. Этот способ адресации рассмотрен в пункте 3.6 при использовании 32-разрядной адресации. Поэтому приведем лишь пример инструкции и опишем ее действия. Инструкция INC [EAX+EDI*4+12345678h] увеличит на единицу число, адрес которого вычисляется, как базовый плюс 32-разрядное смещение 12345678h и плюс масштабированный по основанию 4 индекс, содержащийся в регистре EDI.
ОТНОСИТЕЛЬНАЯ АДРЕСАЦИЯ Относительная адресация используется в микропроцессоре 80386 при выполнении ряда команд управления (условные и безусловные переходы, вызовы подпрограмм, управление циклами и т.д.), чтобы адресовать ячейку памяти, содержащую следующую команду. При этом способе адрес формируется как сумма содержимого регистра EIP(IP) (instruction pointer - указатель инструкции), соответствующего текущей команде, и 8-, 16- или 32-разрядного смещения, определяющего положение следующей команды, относительно текущей.
Например, рассмотрим операцию безусловного перехода в приведенном фрагменте: . . mov ax,5 jmp @1 sub ax,ax @1: mov bx,ax . . Безусловный переход будет представлен как JMP 02, т.е. на 2 байта вперед, после инструкции. SUB AX,AX занимает в памяти слово, значит, переход будет передан на команду MOV BX,AX т.е. по адресу CS:IP+2.
Это немного упрощенный пример внутрисегментного перехода. На самом деле при переходах менее чем на 128 байт используется короткий переход EB??90, где?? - это размер кода который надо пропустить + 1 байт. Дополнительный байт прибавляется за счет кода 90h следующего за смещением. Этот код представляет собой код инструкции NOP (no operation), присутствие его абсолютно бесполезно, но сложилось исторически, и больше не исправлялось. Байт?? - это байт со знаком, так что переход возможен как вперед, так и назад. А вот при переходах более чем на 127 байт используется команда E9????, описанная выше.
|