OSDev Wiki
Advertisement

Общий формат кода команды[]

Полный код команды процессоров архитектуры IA-32 состоит из набора префиксов (prefixes), кода операции (opcode), байта ModR/M, байта SIB, полей отклонения (displacement) и непосредственного операнда (immediate). Все эти части кода команды, за исключением кода операции, являются необязательными. Общая длина кода команды ограничена 15 байтами, а её формат приведён на рисунке.

InstructionCoding

Общий формат кода команды процессоров архитектуры IA-32

Помимо вышеприведённого, в процессорах архитектуры IA-32 встречается ещё несколько специальных форматов команд. Например, имеются разновидности инструкции MOV, осуществляющие загрузку содержимого прямо адресуемой ячейки памяти в аккумулятор или записи содержимого аккумулятора в прямо адресуемую ячейку памяти. В этих инструкциях сразу за байтом кода операции (A0h–A3h) следует поле адреса (в документации Intel обозначается moffset, так как технически это не полный адрес ячейки памяти, а его вторая часть — смещение) длиной 1, 2, 4 или 8 байт в зависимости от режима работы и разрядности выполняемого кода. Другим примером являются инструкции инкремента и декремента (код операции 40h–4Fh), операндом которых является регистр длиной 16 или 32 бита: номер этого регистра кодируется тремя младшими битами кода операции, поэтому полный код команды занимает один байт (два байта в случае использования префикса изменения размера операнда). Ещё один пример — инструкции условных переходов, в которых адрес перехода задаётся 8-, 16- или 32-разрядным смещением относительно значения указателя инструкции IP/EIP/RIP (в документации для обозначения относительного смещения используется запись rel8, rel16 или rel32).

Примечание. В этой статье в качестве русского эквивалента английского термина displacement используется слово «отклонение» вместо часто используемого термина «смещение». Это связано с тем, что смещением (offset) в архитектуре IA-32 называется вторая часть логического адреса (первой частью является селектор сегмента), а отклонение является лишь одним из компонентов для вычисления смещения.

Кодирование регистров[]

В командах процессоров архитектуры IA-32 одновременно может кодироваться до трёх регистров. Их номера могут находиться в полях Reg/Opcode и R/M байта ModR/M, полях Base и Index байта SIB, а также в поле номера регистра, занимающего младшие разряды кода операции в некоторых командах. В 16- и 32-разрядном режимах все эти поля имеют длину 3 бита, что позволяет кодировать каждым из них один из восьми регистров. В 64-разрядном режиме число доступных регистров удвоено; доступ к дополнительным регистрам возможен с помощью префикса REX, содержащего по одному старшему биту для номеров каждого из трёх используемых в команде регистров (младшие биты по-прежнему кодируются в коде операции и байтах ModR/M и SIB; если в команде используется меньшее число регистров, соответствующие биты в префиксе REX игнорируются). Дополнительные регистры недоступны в 16- и 32-разрядном режимах, поскольку в них префикс REX использован быть не может.

Следующая таблица показывает кодирование регистров при отсутствии префикса REX.

Вид регистра
000
001
010
011
100
101
110
111
Общего назначения
AL / AX / EAX
CL / CX / ECX
DL / DX / EDX
BL / BX / EBX
AH / SP / ESP
CH / BP / EBP
DH / SI / ESI
BH / DI / EDI
MMX
MM0
MM1
MM2
MM3
MM4
MM5
MM6
MM7
SSE
XMM0
XMM1
XMM2
XMM3
XMM4
XMM5
XMM6
XMM7
Сегментные
ES
CS
SS
DS
FS
GS
недопустимо
недопустимо
Управляющие
CR0
CR1
CR2
CR3
CR4
CR5
CR6
CR7
Отладочные
DR0
DR1
DR2
DR3
DR4
DR5
DR6
DR7

В таблице ниже приведено кодирование регистров в 64-разрядном режиме при наличии префикса REX. В верхней части таблицы расположены регистры с нулевым старшим разрядом номера, находящимся в байте префикса REX, в нижней части — с единичным.

Вид регистра
000
001
010
011
100
101
110
111
Общего назначения
AL / AX / EAX / RAX
CL / CX / ECX / RCX
DL / DX / EDX / RDX
BL / BX / EBX / RBX
SPL / SP / ESP / RSP
BPL / BP / EBP / RBP
SIL / SI / ESI / RSI
DIL / DI / EDI / RDI
MMX
MM0
MM1
MM2
MM3
MM4
MM5
MM6
MM7
SSE
XMM0
XMM1
XMM2
XMM3
XMM4
XMM5
XMM6
XMM7
Сегментные
ES
CS
SS
DS
FS
GS
недопустимо
недопустимо
Управляющие
CR0
CR1
CR2
CR3
CR4
CR5
CR6
CR7
Отладочные
DR0
DR1
DR2
DR3
DR4
DR5
DR6
DR7
Общего назначения
R8B / R8W / R8D / R8
R9B / R9W / R9D / R9
R10B / R10W / R10D / R10
R11B / R11W / R11D / R11
R12B / R12W / R12D / R12
R13B / R13W / R13D / R13
R14B / R14W / R14D / R14
R15B / R15W / R15D / R15
MMX
MM0
MM1
MM2
MM3
MM4
MM5
MM6
MM7
SSE
XMM8
XMM9
XMM10
XMM11
XMM12
XMM13
XMM14
XMM15
Сегментные
ES
CS
SS
DS
FS
GS
недопустимо
недопустимо
Управляющие
CR8
CR9
CR10
CR11
CR12
CR13
CR14
CR15
Отладочные
DR8
DR9
DR10
DR11
DR12
DR13
DR14
DR15

Как видно из таблицы, для сегментных регистров и регистров MMX старший бит, кодируемый в префиксом REX, игнорируется. Кроме того, байтовые регистры AH, BH, CH и DH недоступны, если в команде имеется префикс REX.

Префиксы[]

Процессоры IA-32 распознают четыре группы так называемых унаследованных (legacy) префиксов, которые могут использоваться в 16-, 32- и 64-разрядном коде. В каждой инструкции может присутствовать лишь один префикс из каждой группы, однако реакция процессора на наличие нескольких префиксов одной группы не оговаривается. Каждый префикс имеет смысл лишь для определённых команд; последствия использования префикса с командой, для которой он не предназначен, не определены. Существующие процессоры обычно игнорируют префиксы, не имеющие смысла с данной командой, а при наличии нескольких префиксов одной группы используют только последний из них, однако злоупотреблять этим не стоит: в будущем поведение процессоров может измениться.

Помимо унаследованных префиксов, в 64-разрядном коде может использоваться новый префикс REX. В 16- и 32-разрядном коде он недоступен.

Обычно порядок следования префиксов из разных групп не играет роли, однако из этого правила есть два исключения:

  • префикс REX всегда непосредственно предшествует коду операции; таким образом, унаследованные префиксы должны помещаться перед ним;
  • в некоторых командах определённый префикс должен присутствовать в обязательном порядке (де-факто он становится частью кода операции, хотя де-юре продолжает оставаться префиксом). Такой префикс должен быть последним из числа унаследованных префиксов, между ним и собственно кодом операции может размещаться только префикс REX. Например, инструкция CVTDQ2PD кодируется префиксом F3, за которым сразу следуют два байта кода операции: 0F E6.

Унаследованные префиксы[]

Группа 1 — префиксы блокировки и повторения:

  • F0h — префикс блокировки шины LOCK;
  • F2h — префикс повторения REPNE/REPNZ;
  • F3h — префикс повторения REP или REPE/REPZ.

Префиксы повторения используются только со строковыми операциями CMPS, INS, LODS, MOVS, OUTS, SCAS, STOS. Кроме того, в некоторых других командах они являются обязательными, фактически превратившись в часть кода операции и утратив свой исходный смысл.

Префикс блокировки применяется с некоторыми инструкциями, обращающимися к памяти для чтения и последующей записи по одному и тому же адресу, для реализации атомарного доступа (т.е. для предотвращения доступа со стороны других процессоров к этой ячейке памяти на всё время выполнения команды): ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD и XCHG. С последней инструкцией прямое указание префикса LOCK смысла не имеет, поскольку она всегда осуществляет атомарный доступ. Все перечисленные команды должны иметь своим операндом-приёмником ячейку памяти. Попытка использования префикса блокировки для этих инструкций, если приёмником является регистр, а также его использование с другими командами будет либо проигнорирована, либо вызовет исключение по неопределённой операции (#UD).

Заметим, что в документации фирмы AMD префиксы повторения и блокировки отнесены к разным группам, поэтому там считается, что групп префиксов пять. Однако эти префиксы не могут использоваться вместе, поэтому объединение их в одну группу вполне уместно.

Группа 2 — префиксы замены сегмента и оптимизации переходов:

  • 2Eh — префикс замены сегмента CS: или маловероятного перехода;
  • 36h — префикс SS:;
  • 3Eh — префикс DS: или вероятного перехода;
  • 26h — префикс ES:;
  • 64h — префикс FS:;
  • 65h — префикс GS:.

Префиксы замены сегмента могут использоваться с большинством инструкций, обращающихся к явно заданным операндам в памяти с использованием селекторов сегментов DS или SS (последний применяется в тех случаях, когда при вычислении смещения в роли базового регистра выступает BP/EBP/RBP или ESP/RSP; подробнее об этом говорится ниже в подразделах «Байт ModR/M» и «Байт SIB»). Кроме того, префикс замены сегмента может использоваться в командах обработки строк, осуществляющих выборку данных (в них используется адрес, содержащийся в регистровой паре DS:SI/ESI/RSI). Во всех этих случаях доступ к памяти будет выполнен с использованием сегментного регистра, задаваемого префиксом, а не используемого по умолчанию. Сегментный регистр ES, используемый в ряде строковых операций, заменён быть не может. Не может быть заменён и сегментный регистр SS, используемый при неявных обращениях к стеку (в командах типа PUSH и CALL).

В 64-разрядном режиме префиксы замены сегмента DS:, ES:, CS: и SS: игнорируются, а два оставшихся действуют специальным образом: сегментные регистры FS и GS с их помощью используются в качестве дополнительных базовых регистров.

В инструкциях перехода наличие префикса замены сегмента приводит, вообще говоря, к непредсказуемым результатам. Однако на практике эти префиксы игнорируются, а в последних версиях процессоров два из них (DS: и SS:) стали использоваться в качестве «подсказок» процессору относительно вероятности перехода по той или иной ветке. Теперь эти два префикса официально поддерживаются во всех инструкциях условного перехода.

Группа 3 — префикс изменения размера операнда 66h.

Если для текущего сегмента кода размер данных по умолчанию равен 16 битам, то наличие этого префикса заставляет команду выполнить не 16-, а 32-разрядную операцию; если же размер данных по умолчанию равен 32 битам, то при наличии этого префикса команда выполнит не 32-, а 16-разрядную операцию. В командах обработки байтов этот префикс игнорируется.

В 64-разрядном режиме размер данных по умолчанию равен 32 битам, а использование префикса 66h даёт возможность обрабатывать 16-разрядные данные. 64-разрядные операнды, за исключением нескольких команд, используются только при наличии префикса REX с установленным битом W; в такой ситуации префикс 66h игнорируется.

Префикс изменения размера операнда программистом явным образом не указывается, транслятор самостоятельно определяет его необходимость.

Группа 4 — префикс изменения размера адреса 67h.

Этот префикс позволяет переключать размер эффективного адреса. В 16-разрядном режиме наличие префикса 67h заставляет процессор вычислять 32-разрядный эффективный адрес (однако при обращении к памяти он будет всё равно усечён до 16 бит; главная польза от использования префикса 67h в этом случае заключается в расширенных возможностях адресации — см. расположенный ниже раздел «Байт ModR/M»), в 32-разрядном режиме — 16-разрядный адрес, дополняемый перед доступом к памяти нулями до 32 бит, а в 64-разрядном режиме — 32-разрядный адрес, дополняемый нулями до 64 бит.

Префикс 67h не оказывает влияния на размер адреса, по которому осуществляется неявный доступ к стеку: он всегда будет равен размеру адреса по умолчанию.

Кроме размера адреса, префикс 67h изменяет размер неявно используемого регистра CX/ECX/RCX.

Этот префикс программистом явным образом не задаётся: транслятор при необходимости вставляет его сам.

Документация фирмы Intel не оговаривает чётко, оказывает ли влияние префикс 67h на размер поля прямо заданного адреса памяти (moffset), используемого в разновидностях инструкции MOV с кодами операции A0h–A3h. Однако есть основания предполагать, что на этих командах префикс 67h не сказывается, и поле адреса всегда будет иметь длину, соответствующую размеру адреса по умолчанию.

Префикс REX[]

Префикс REX может использоваться только в 64-разрядном коде и располагается непосредственно перед кодом операции, после всех унаследованных префиксов. Он необходим в следующих случаях:

  • когда команда использует новые регистры, введённые вместе с 64-разрядным расширением архитектуры IA-32 (см. расположенный выше раздел «Кодирование регистров»);
  • когда команда использует 64-разрядные операнды.

В некоторых командах размер операндов не зависит от наличия или отсутствия префикса REX и состояния его бита W. Например, в командах переходов и циклов, использующих относительные смещения (rel8, rel16, rel32), размер смещения определяется кодом операции и разрядностью выполняемого кода. В командах циклов размер регистра-счётчика (CX/ECX/RCX) равен используемой разрядности адреса и может быть изменён префиксом изменения размера адреса 67h, но не префиксами, влияющими на размер операндов (66h и REX). Команды, осуществляющие неявный доступ к стеку (CALL, ENTER, LEAVE, POP, PUSH, RET), в 64-разрядном режиме всегда используют 64-разрядный указатель стека RSP, а ENTER и LEAVE — ещё и регистр RBP независимо от наличия или отсутствия префиксов (однако команда CALL использует бит W префикса REX и префикс изменения размера операндов 66h, чтобы определить размер смещения при выполнении дальнего перехода, т.е. в этом случае ведёт себя, как большинство других команд). Наконец, ряд команд, осуществляющих доступ к специальным регистрам, например, LTR, использует операнды строго определённого размера, не зависящего от режима работы процессора, разрядности кода и наличия префиксов.

Если префикс REX используется в команде, для которой он бесполезен, он игнорируется.

Старший полубайт префикса REX всегда содержит биты 0100; таким образом, префикс REX может иметь коды 40h–4Fh. В 16- и 32-разрядных режимах этим кодам соответствуют однобайтовые инструкции INC и DEC, поэтому в 64-разрядном режиме они недоступны. Вместо этого для реализации тех же функций применяются формы инструкций INC и DEC, использующие байт ModR/M (код операции FF/0 и FF/1).

Младший полубайт префикса REX состоит из четырёх битов, каждый из которых имеет свою функцию:

  • бит W (3) участвует в определении размера операндов. Когда он равен 0, а префикс изменения размера операндов 66h отсутствует, размер операндов будет равен 32 битам; при нулевом бите W и наличии префикса 66h используются 16-разрядные операнды; при W=1 размер операндов равен 64 битам независимо от наличия префикса 66h. Для обработки 8-разрядных операндов предусмотрены отдельные коды операций; в этом случае бит W и префикс 66h игнорируются.
  • бит R (2) — разряд, расширяющий поле Reg/Opcode байта ModR/M для обращения к новым регистрам общего назначения, SSE, управляющим и отладочным;
  • бит X (1) — разряд, расширяющий поле Index байта SIB для обращения к новым регистрам общего назначения;
  • бит B (0) — разряд, расширяющий поле R/M байта ModR/M, поле Base байта SIB или поле номера регистра, находящееся в байте кода операции, для доступа к новым регистрам.

Биты R, X, B игнорируются, если соответствующие поля не используются для кодирования регистров перечисленных категорий.

Код операции[]

Код операции является единственным обязательным полем любой команды. Он имеет длину от 1 до 3 байт. В двух- и трёхбайтовых командах первым байтом кода операции является 0Fh. Кроме того, некоторые команды требуют обязательного наличия префикса F2h, F3h или 66h, который должен размещаться непосредственно перед кодом операции (между таким обязательным префиксом, фактически утратившим свои первоначальные функции и превратившимся в часть кода операции, и собственно перед кодом операции может находиться только префикс REX).

В некоторых командах, использующих только один явно заданный операнд, часть кода операции содержится в трёхбитовом поле Reg/Opcode байта ModR/M (в двухоперандных командах это поле содержит номер регистра, используемого в качестве второго операнда). Документация Intel в таких случаях указывает значение этих разрядов одной цифрой после знака косой черты, следующего за значением байтов кода операции. Например, команде INC соответствуют коды операции FE/0 и FF/0, т.е. собственно байт кода операции должен быть равен FEh или FFh, а поле Reg/Opcode следующего за кодом операции байта ModR/M — 000.

В ряде команд некоторые разряды кода операции используются для определения номера используемого регистра. Например, в 16- и 32-разрядном коде для инкремента регистра общего назначения длиной слово или двойное слово может использоваться однобайтовый вариант команды INC, имеющий код операции от 40h до 47h включительно, где младшие три бита определяют регистр; в байте ModR/M этот вариант не нуждается. В 64-разрядном режиме такой вариант команды INC использоваться не может, поскольку этот диапазон кодов отведён для префикса REX.

Байт ModR/M[]

Большинство инструкций обработки данных и ряд других команд используют явно заданные операнды, которые кодируются этим байтом. Помимо него, для определения местоположения операндов могут использоваться байт SIB и поле отклонения, а в 64-разрядном коде — ещё и префикс REX. В некоторых командах операнды заданы неявным образом либо кодируются несколькими битами из байта кода операции; имеются также инструкции, одним из операндов которых является константа, размещающаяся в поле непосредственного операнда. В некоторых случаях существуют «параллельные» версии одной и той же инструкции: например, инкремент содержимого регистра в 16- и 32-разрядных режимах возможен либо с помощью однобайтовой команды (код операции от 40h до 47h, причём его младшие три бита обозначают регистр), либо с помощью двухбайтовой (байт кода операции, равный FFh, и байт ModR/M со значением от C0h до C7h; его младшие три бита — поле R/M — указывают используемый регистр).

Байт ModR/M позволяет закодировать два операнда. Первый (или единственный) операнд может размещаться в каком-либо регистре либо в памяти, а второй — только в регистре. Номера регистров, выступающих в роли операндов, кодируются прямо в байте ModR/M (исключением являются дополнительные регистры, появившиеся вместе с 64-разрядным расширением архитектуры IA-32: для обращения к ним, помимо байта ModR/M, необходимо указывать префикс REX). Способ вычисления адреса операнда в памяти задаётся либо байтом ModR/M, либо совместно байтами ModR/M и SIB.

Байт ModR/M состоит из трёх полей: двухбитового Mod и трёхбитовых Reg/Opcode и R/M, расположенных в байте в указанном порядке. Поле Mod определяет вид адресации первого операнда; поле Reg/Opcode задаёт номер регистра второго операнда или дополнительные разряды кода операции, если команда использует лишь один явно заданный операнд; поле R/M кодирует номер регистра или вид адресации первого операнда и используется совместно с полем Mod.

Способ кодирования первого операнда команды с помощью полей Mod и R/M зависит от используемой разрядности адреса, на которую влияет префикс изменения разрядности адреса 67h, и подробно описано в следующих подразделах. Кодирование второго операнда, в роли которого всегда выступает регистр, описано в расположенном выше разделе «Кодирование регистров» и от разрядности адреса не зависит, за исключением того, что при 64-разрядной адресации возможно использование префикса REX, чей бит R выступает в роли дополнительного разряда номера регистра, содержащегося в поле Reg/Opcode байта ModR/M.

Вычисление 16-разрядных адресов[]

Когда процессор работает в реальном или 16-разрядном защищённом режиме, а также в режиме V86, по умолчанию используются 16-разрядные эффективные адреса операндов, правила вычисления которых определяются содержимым полей Mod и R/M байта ModR/M, как описано в следующей таблице. При наличии префикса изменения размера адреса 67h вместо 16-разрядного вычисляется 32-разрядный эффективный адрес; применяемые для этого правила будут описаны в следующем подразделе. Заметим, что, когда процессор работает в режиме с 16-разрядной адресацией, старшие 16 бит вычисленного 32-разрядного эффективного адреса перед обращением к памяти всё равно будут отброшены; единственная польза от префикса 67h заключается в том, что он расширяет сами возможности вычисления эффективного адреса (например, в качестве базового может использоваться любой регистр общего назначения, а не только BX и BP, как при вычислении 16-разрядного адреса).

Mod
00
01
10
11
R/M
000
(BX, SI)
disp8 (BX, SI)
disp16 (BX, SI)
регистр
001
(BX, DI)
disp8 (BX, DI)
disp16 (BX, DI)
регистр
010
(BP, SI)
disp8 (BP, SI)
disp16 (BP, SI)
регистр
011
(BP, DI)
disp8 (BP, DI)
disp16 (BP, DI)
регистр
100
(SI)
disp8 (SI)
disp16 (SI)
регистр
101
(DI)
disp8 (DI)
disp16 (DI)
регистр
110
disp16
disp8 (BP)
disp16 (BP)
регистр
111
(BX)
disp8 (BX)
disp16 (BX)
регистр

Когда поле Mod равно 11, операнд находится в каком-либо регистре. Кодирование регистров всегда стандартное и описано в одноимённом разделе выше.

Когда поле Mod не равно 11, операнд размещается в памяти. Его эффективный адрес в общем случае вычисляется как сумма содержимого указанных регистров (BX, BP, SI, DI) и отклонения. Наличие и размер отклонения (8 или 16 бит) определяется главным образом полем Mod: когда оно равно 00, отклонения нет, когда равно 01, используется 8-разрядное отклонение (рассматриваемое как число со знаком и перед сложением с содержимым регистров расширяемое до 16 бит), когда равно 10 — 16-разрядное. Исключением является случай, когда поле Mod равно 00, а поле R/M — 110: в этом случае эффективный адрес будет равен 16-разрядному отклонению без учёта значений регистров.

Операнд, адрес которого сформирован с использованием содержимого регистра BP, расположен по умолчанию в сегменте стека; операнд, адресация которого осуществляется без использования регистра BP, — в сегменте данных. Возможно размещение этих операндов в любом другом сегменте; для этого в состав кода команды должен входить префикс замены сегмента.

Вычисление 32- и 64-разрядных адресов[]

В 32-разрядном защищённом режиме и в режиме управления системой по умолчанию формируются 32-разрядные эффективные адреса, а в 64-разрядном режиме — 64-разрядные. Префикс изменения размера адреса 67h, использованный в 32-разрядном режиме, заставляет процессор вычислить 16-разрядный эффективный адрес по изложенным выше правилам, а в 64-разрядном режиме — 32-разрядный. В обоих случаях перед использованием адрес будет расширен до нужной разрядности (32 и 64 бита соответственно) путём добавления к нему лидирующих нулей.

Способы вычисления эффективного адреса в 32- и 64-разрядном режимах в целом одинаковы и задаются полями Mod и R/M. В отличие от 16-разрядной адресации, в 32- и 64-разрядных режимах для вычисления адреса может использоваться также байт SIB.

В следующей таблице описано кодирование эффективного адреса для 32-разрядного режима.

Mod
00
01
10
11
R/M
000
(EAX)
disp8 (EAX)
disp32 (EAX)
регистр
001
(ECX)
disp8 (ECX)
disp32 (ECX)
регистр
010
(EDX)
disp8 (EDX)
disp32 (EDX)
регистр
011
(EBX)
disp8 (EBX)
disp32 (EBX)
регистр
100
SIB
disp8 + SIB
disp32 + SIB
регистр
101
disp32
disp8 (EBP)
disp32 (EBP)
регистр
110
(ESI)
disp8 (ESI)
disp32 (ESI)
регистр
111
(EDI)
disp8 (EDI)
disp32 (EDI)
регистр

Как и в 16-разрядном режиме, при Mod=11 операнд находится в регистре; правила кодирования регистров приведены выше в соответствующем разделе.

Другие значения Mod задают наличие и размер отклонения: 00 соответствует его отсутствию, 01 — однобайтовому отклонению, 10 — четырёхбайтовому. Однобайтовое отклонение рассматривается как число со знаком и перед сложением с другими компонентами адреса расширяется до 32 бит.

При Mod≠11 и R/M=100 правила вычисления адреса задаются байтом SIB; в остальных случаях этот байт отсутствует. К значению, полученному в соответствии с содержимым байта SIB, может быть прибавлено отклонение, что определяется полем Mod.

При Mod=00 и R/M=101 эффективным адресом будет значение 32-разрядного отклонения; регистры в формировании адреса в этом случае не участвуют.

Адресация относительно регистра ESP возможна только с помощью байта SIB, а для обращения по адресу, находящемуся в EBP, необходимо использовать нулевое отклонение (адресация по этому регистру без отклонения не предусмотрена).

Если Mod=01 или 00, а R/M=101, т.е. при обращении к памяти относительно регистра EBP, доступ по умолчанию выполняется к сегменту стека, а при других комбинациях Mod и R/M (кроме Mod=11 или R/M=100) — к сегменту данных. Возможно обращение к любому другому сегменту, для чего следует использовать префикс замены сегмента.

В 64-разрядном режиме имеются следующие особенности:

  • для вычисления адреса могут использоваться или 32-, или 64-разрядные регистры: при отсутствии префикса 67h для вычисления адреса используются 64-разрядные регистры (RAX, RCX и т.д.), а при наличии этого префикса — 32-разрядные;
  • при Mod=00 и R/M=101 используется не прямая адресация (когда эффективным адресом операнда является 32-разрядное смещение), а адресация относительно указателя инструкции: disp32 (RIP). Регистр RIP содержит адрес следующей инструкции, а отклонение рассматривается как 32-разрядное число со знаком и расширяется до 64 бит, что позволяет обратиться к любой ячейке памяти в пределах 2 Гбайт до или после текущего значения RIP;
  • для адресации данных в памяти с помощью байта ModR/M можно использовать не семь, а 14 регистров общего назначения. Дополнительные регистры R8–R11 и R13–R15 доступны с помощью префикса REX, в котором должен быть установлен бит B, выступающий в роли старшего разряда номера регистра (младшие три разряда содержатся в поле R/M). Когда этот бит сброшен, адресация выполняется с помощью регистров RAX/EAX, RCX/ECX и т.д., кодируемых полем R/M обычным образом;
  • адресация относительно регистров RSP/ESP и R12/R12D возможна только с помощью байта SIB;
  • для обращения к ячейке памяти по адресу, находящемуся в RBP/EBP, необходимо использовать нулевое отклонение;
  • поле отклонения, если присутствует, имеет длину 8 или 32 бита и рассматривается как число со знаком. Перед его сложением с содержимым заданного в поле R/M регистра или со значением, полученным при обработке байта SIB, оно расширяется до 64 бит;
  • если в 64-разрядном режиме используется 32-разрядная адресация, перед обращением к памяти адрес расширяется до 64 бит путём дописывания лидирующих нулей.

Байт SIB[]

Байт SIB располагается после байта ModR/M в некоторых командах, использующих 32- и 64-разрядную адресацию; его наличие определяется содержимым байта ModR/M (Mod≠11 и R/M=100). Байт SIB задаёт адрес операнда в виде суммы значений базового и индексного регистров, причём индексный регистр может быть промасштабирован путём умножения на 2, 4 или 8. С помощью байта ModR/M к полученной сумме может быть прибавлено 8- или 32-разрядное отклонение.

Байт SIB делится на три поля: двухбитовое Scale и трёхбитовые Index и Base, расположенные в байте в этом порядке.

Поле Scale указывает масштаб индекса:

  • 00 — 1;
  • 01 — 2;
  • 10 — 4;
  • 11 — 8.

Поле Index задаёт индексный регистр, а поле Base — базовый. Номера регистров кодируются обычным образом и приведены выше в соответствующем разделе. Эффективный адрес, задаваемый совместно байтами ModR/M и SIB, вычисляется следующим образом:

EA = Disp + Base + Index * Scale

Здесь Disp — отклонение, наличие и разрядность которого определяется байтом ModR/M, а значение располагается в коде команды после байта SIB; Base — содержимое базового регистра, номер которого задан одноимённым полем байта SIB; Index — содержимое индексного регистра, номер которого задан одноимённым полем байта SIB; Scale — множитель для масштабирования индекса, который закодирован в одноимённом поле байта SIB.

Из указанной выше формулы вычисления эффективного адреса имеются два исключения:

  • регистр ESP не может выступать в роли индекса. Если в поле Index находится значение 100, считается, что индекс равен нулю, и эффективный адрес операнда определяется как сумма содержимого базового регистра и отклонения. Например, байт SIB со значением E7h кодирует операнд (EDI) (с возможным дополнительным отклонением), а не (EDI, ESP*8);
  • поле Base, равное 101, не всегда соответствует базовому регистру EBP, что зависит от значения поля Mod байта ModR/M. Когда Mod=00, базовый регистр отсутствует, и эффективный адрес операнда равен сумме промасштабированного содержимого индексного регистра и 32-разрядного отклонения (операнд вида disp32 (Index*Scale)). Когда Mod=01 или 10, эффективный адрес формируется по общим правилам, т.е. как сумма базового регистра EBP, промасштабированного индекса и 8- или 32-разрядного отклонения.

Полная сводка возможных эффективных адресов, задаваемых байтом SIB в 32-разрядном режиме (без учёта возможного наличия отклонения, что определяется байтом ModR/M), приведена в следующей таблице.

Base
000
001
010
011
100
101
110
111
Scale
Index
00
000
(EAX, EAX)
(ECX, EAX)
(EDX, EAX)
(EBX, EAX)
(ESP, EAX)
(ESI, EAX)
(EDI, EAX)
00
001
(EAX, ECX)
(ECX, ECX)
(EDX, ECX)
(EBX, ECX)
(ESP, ECX)
(ESI, ECX)
(EDI, ECX)
00
010
(EAX, EDX)
(ECX, EDX)
(EDX, EDX)
(EBX, EDX)
(ESP, EDX)
(ESI, EDX)
(EDI, EDX)
00
011
(EAX, EBX)
(ECX, EBX)
(EDX, EBX)
(EBX, EBX)
(ESP, EBX)
(ESI, EBX)
(EDI, EBX)
00
100
(EAX)
(ECX)
(EDX)
(EBX)
(ESP)
(ESI)
(EDI)
00
101
(EAX, EBP)
(ECX, EBP)
(EDX, EBP)
(EBX, EBP)
(ESP, EBP)
(ESI, EBP)
(EDI, EBP)
00
110
(EAX, ESI)
(ECX, ESI)
(EDX, ESI)
(EBX, ESI)
(ESP, ESI)
(ESI, ESI)
(EDI, ESI)
00
111
(EAX, EDI)
(ECX, EDI)
(EDX, EDI)
(EBX, EDI)
(ESP, EDI)
(ESI, EDI)
(EDI, EDI)
01
000
(EAX, EAX*2)
(ECX, EAX*2)
(EDX, EAX*2)
(EBX, EAX*2)
(ESP, EAX*2)
(ESI, EAX*2)
(EDI, EAX*2)
01
001
(EAX, ECX*2)
(ECX, ECX*2)
(EDX, ECX*2)
(EBX, ECX*2)
(ESP, ECX*2)
(ESI, ECX*2)
(EDI, ECX*2)
01
010
(EAX, EDX*2)
(ECX, EDX*2)
(EDX, EDX*2)
(EBX, EDX*2)
(ESP, EDX*2)
(ESI, EDX*2)
(EDI, EDX*2)
01
011
(EAX, EBX*2)
(ECX, EBX*2)
(EDX, EBX*2)
(EBX, EBX*2)
(ESP, EBX*2)
(ESI, EBX*2)
(EDI, EBX*2)
01
100
(EAX)
(ECX)
(EDX)
(EBX)
(ESP)
(ESI)
(EDI)
01
101
(EAX, EBP*2)
(ECX, EBP*2)
(EDX, EBP*2)
(EBX, EBP*2)
(ESP, EBP*2)
(ESI, EBP*2)
(EDI, EBP*2)
01
110
(EAX, ESI*2)
(ECX, ESI*2)
(EDX, ESI*2)
(EBX, ESI*2)
(ESP, ESI*2)
(ESI, ESI*2)
(EDI, ESI*2)
01
111
(EAX, EDI*2)
(ECX, EDI*2)
(EDX, EDI*2)
(EBX, EDI*2)
(ESP, EDI*2)
(ESI, EDI*2)
(EDI, EDI*2)
10
000
(EAX, EAX*4)
(ECX, EAX*4)
(EDX, EAX*4)
(EBX, EAX*4)
(ESP, EAX*4)
(ESI, EAX*4)
(EDI, EAX*4)
10
001
(EAX, ECX*4)
(ECX, ECX*4)
(EDX, ECX*4)
(EBX, ECX*4)
(ESP, ECX*4)
(ESI, ECX*4)
(EDI, ECX*4)
10
010
(EAX, EDX*4)
(ECX, EDX*4)
(EDX, EDX*4)
(EBX, EDX*4)
(ESP, EDX*4)
(ESI, EDX*4)
(EDI, EDX*4)
10
011
(EAX, EBX*4)
(ECX, EBX*4)
(EDX, EBX*4)
(EBX, EBX*4)
(ESP, EBX*4)
(ESI, EBX*4)
(EDI, EBX*4)
10
100
(EAX)
(ECX)
(EDX)
(EBX)
(ESP)
(ESI)
(EDI)
10
101
(EAX, EBP*4)
(ECX, EBP*4)
(EDX, EBP*4)
(EBX, EBP*4)
(ESP, EBP*4)
(ESI, EBP*4)
(EDI, EBP*4)
10
110
(EAX, ESI*4)
(ECX, ESI*4)
(EDX, ESI*4)
(EBX, ESI*4)
(ESP, ESI*4)
(ESI, ESI*4)
(EDI, ESI*4)
10
111
(EAX, EDI*4)
(ECX, EDI*4)
(EDX, EDI*4)
(EBX, EDI*4)
(ESP, EDI*4)
(ESI, EDI*4)
(EDI, EDI*4)
11
000
(EAX, EAX*8)
(ECX, EAX*8)
(EDX, EAX*8)
(EBX, EAX*8)
(ESP, EAX*8)
(ESI, EAX*8)
(EDI, EAX*8)
11
001
(EAX, ECX*8)
(ECX, ECX*8)
(EDX, ECX*8)
(EBX, ECX*8)
(ESP, ECX*8)
(ESI, ECX*8)
(EDI, ECX*8)
11
010
(EAX, EDX*8)
(ECX, EDX*8)
(EDX, EDX*8)
(EBX, EDX*8)
(ESP, EDX*8)
(ESI, EDX*8)
(EDI, EDX*8)
11
011
(EAX, EBX*8)
(ECX, EBX*8)
(EDX, EBX*8)
(EBX, EBX*8)
(ESP, EBX*8)
(ESI, EBX*8)
(EDI, EBX*8)
11
100
(EAX)
(ECX)
(EDX)
(EBX)
(ESP)
(ESI)
(EDI)
11
101
(EAX, EBP*8)
(ECX, EBP*8)
(EDX, EBP*8)
(EBX, EBP*8)
(ESP, EBP*8)
(ESI, EBP*8)
(EDI, EBP*8)
11
110
(EAX, ESI*8)
(ECX, ESI*8)
(EDX, ESI*8)
(EBX, ESI*8)
(ESP, ESI*8)
(ESI, ESI*8)
(EDI, ESI*8)
11
111
(EAX, EDI*8)
(ECX, EDI*8)
(EDX, EDI*8)
(EBX, EDI*8)
(ESP, EDI*8)
(ESI, EDI*8)
(EDI, EDI*8)

Колонка со значением «*» соответствует второму из описанных выше исключений для регистра EBP, возможность использования которого в качестве базового зависит от значения поля Mod байта ModR/M.

Использование 64-разрядной адресации связано со следующими особенностями:

  • с помощью префикса REX разрядность полей базы и индекса увеличивается, что позволяет исопльзовать в качестве базовых и индексных дополнительные восемь регистров R8/R8D–R15/R15D. Когда в поле Index байта SIB содержится 100, а бит X префикса REX равен нулю, индексный регистр отсутствует (т.е., как и в 32-разрядном режиме, регистр RSP/ESP не может выступать в роли индекса). Когда Index=100, а REX.X=1, в качестве индекса используется регистр R12/R12D;
  • регистр RBP/EBP по-прежнему не может использоваться в качестве базового без явного указания отклонения. Кроме того, байт SIB не позволяет закодировать использование регистра R13/R13D как базового без явно заданного отклонения, поскольку при Mod=00 и Base=101 считается, что базового регистра нет, независимо от состояния бита B префикса REX.

Отклонение[]

Поле отклонения имеет размер 1, 2 или 4 байта либо может отсутствовать, что определяется байтом ModR/M. Находящееся в этом поле 8-, 16- или 32-разрядное отклонение используется при вычислении эффективного адреса операнда. 16-разрядное поле применяется при 16-разрядной адресации, 32-разрядное — при 32- и 64-разрядной, 8-разрядное — при любой разрядности адреса. Отклонение, чья разрядность меньше разрядности адресации, считается числом со знаком и перед вычислением эффективного адреса расширяется до размера адреса; например, при 16-разрядной адресации отклонение FFh расширяется до FFFFh.

Непосредственный операнд[]

Поле непосредственного операнда имеется в командах, использующих непосредственную адресацию; его наличие определяется по коду операции. Размер этого поля обычно равен размеру операндов данной команды; исключением является 64-разрядный режим, в котором действуют следующие правила:

  • если операнд имеет размер 8, 16 или 32 бита, то размер поля непосредственного операнда будет равен размеру операнда;
  • если операнд имеет размер 64 бита (присутствует префикс REX с установленным битом W), то во всех инструкциях, кроме MOV reg64, const (коды операции B8h–BFh), используется 32-разрядное поле непосредственного операнда, причём сам непосредственный операнд рассматривается как число со знаком и перед выполнением операции расширяется до 64 бит;
  • в инструкциях MOV reg64, const (коды операции B8h–BFh c префиксом REX, имеющим единичный бит W) используется 64-разрядное поле непосредственного операнда.
Advertisement