§ Общая схема

Процессор x86 это процессор семейства CISC, характеризуются сложными инструкциями, причем разной длины. Обычно такие инструкции выполняются с помощью микрокода.
Секция Префиксы Опкод Байт ModRM Байт SIB Смещение Непосредственное значение
Размер 0-5 и выше 1-2 1 1 0-4 0-8
Как можно увидеть, размер инструкции может достигать 21 байт! На самом деле, процессор позволяет кодировать инструкцию до 15 байт. Если же инструкция свыше 15 байт, то генерируется ошибка Invalid Opcode. Я не знаю, насколько это актуально, но для x86-64 инструкция может занимать и 21 байт, что удивительно. Получить такой огромный размер инструкции фактически невозможно, кроме как просто составить ее вручную. Естественно, процессор отвергнет такую инструкцию.

§ Префиксы

Существуют несколько типов префиксов - это сегментные, строковые, префиксы расширения, и еще совершенно не нужный и не актуальный сейчас префикс LOCK.
  • Сегментные префиксы ES: CS: SS: DS: FS: и GS: указывают, что будет обязательным образом при обращении в память браться данные из указанного сегмента вместо DS: или SS:
  • Строковые префиксы REPZ и REPNZ указывают, что STOSx, LODSx, MOVSx, CMPSx, INSx, OUTSx, SCASx инструкции могут повторяться согласно значению в регистре eCX или флагу Z, для инструкции SCASx, CMPSx
  • Префикс LOCK был нужен для эксклюзивной блокировки шины процессором на время исполнения этой инструкции и работает только перед арифметически-логическими инструкциями
  • Префикс расширения операнда (66h) указывает, что 16-битные регистры или 16-битные immediate будут увеличены до 32-х битных
  • Расширение адреса (67h) указывает, что вместо механизма modrm16 будет взят механизм modrm32+sib для вычисления эффективного адреса в память
  • Префикс 0Fh расширяет опкод, включая дополнительные 256-512 кодов операции. Строго говоря, префикс 0Fh считается частью 2х-байтного опкода.

§ Байт ModRM 16

Байт mod/rm предназначен для того, чтобы задавать операнды, обычно их два, и один либо указывает на память, другой на регистр, либо же оба указывают на регистр. В некоторых инструкциях регистровая часть выбирает некоторое действие.
Формат байта
ИмяMODREGR/M
Биты7:65:32:0
Для регистров выделены по 3 бита. В качестве регистра может выступать не только регистр 8,16,32 или 64 бита, но еще и сегментные регистры, а также другие, такие как cr0-7, dr0-7, st0-7, а также различные команды.
Байт mod определяет то, каким образом будет извлечено значение из rm-части (биты 0-2):
  • mod=0, то тогда на основе значения rm вычислится эффективный адрес, откуда будет взят операнд для работы. Бывают инструкции, которые извлекают только один операнд - первый, второй операнд (reg-часть) используется для определения кода операции, а второй берется из непосредственного значения (immediate) после разбора.
  • mod=0 и rm=6, то будет извлечено 16-битное значение offset вместо вычисления суммы регистров. В случае если участвует в вычислении эффективного адреса регистр bp, то по умолчанию будет взято значение из памяти с сегментом SS: вместо DS:
  • mod=1, то к эффективному адресу добавится +1 знаковый байт индекса (-128..127)
  • mod=2, то добавится +2 знаковый байт индекса (-32768..32767)
  • mod=3, вместо операнда из памяти будет использован регистр
Примеры при mod=0:
  • [bx+si], al
  • [bp], ax
  • [bx], eax
Примеры при mod=1
  • [bx+di-1], dl
  • [di+127], dx
  • [bp-128], edx
Примеры при mod=2
  • [bx+2356], cl
  • [si-32768], cx
  • [di+32767], ecx
Примеры при mod=3
  • ah, al
  • bx, sp
  • esi, edi

§ Байт 32-х modrm

Этот байт, как и 16-битный, предназначен для кодирования операндов, но он отличается достаточно сильно от своего предшественника. Во-первых, для вычисления эффективного адреса там уже используются все 8 регистров общего назначения, во-вторых, там можно складывать два регистра, в-третьих, регистры можно еще и масштабировать, умножая один на 2,4,8, что обеспечивает SIB-байт, который может идти после modrm (но может и не быть).
Также как и в modrm16, если выбран mod=0, то не используется смещение, кроме случая rm=5 (не 6), так что при mod=0, rm=5 эффективный адрес будет 32-х битный абсолютный адрес disp32. Если mod=1, то в качестве смещения используется 1 знаковый байт (-128..127), если же mod=2, то будет использован 32-х битный адрес смещения. Так же как и ранее, mod=3 дает регистр.
Если mod=0,1 или 2, и rm=4 то прочитается байт SIB, который расширяет возможности адресации операнда, который находится в r/m-части. Второй операнд - регистр, остается без изменений. Байт SIB только расширяет адресацию эффективного адреса: он позволяет использовать сумму двух регистров общего назначения.
Как можно отметить, адресации по ESP не существует в данной схеме, поэтому необходимо пользоваться регистром EBP. Адресация по ESP возможна только при использовании байта SIB.

§ Формат байта MODRM32

r8(/r) AL CL DL BL AH CH DH BH
r16(/r) AX CX DX BX SP BP SI DI
r32(/r) EAX ECX EDX EBX ESP EBP ESI EDI
mm(/r) MM0 MM1 MM2 MM3 MM4 MM5 MM6 MM7
xmm(/r) XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7
sreg ES CS SS DS FS GS res. res.
eee CR0 invd CR2 CR3 CR4 invd invd invd
eee DR0 DR1 DR2 DR3 DR41 DR51 DR6 DR7
(In decimal) /digit (Opcode) 0 1 2 3 4 5 6 7
(In binary) REG = 000 001 010 011 100 101 110 111
Effective Address Mod R/M Value of ModR/M Byte (in Hex)
[EAX] 00 000 00 08 10 18 20 28 30 38
[ECX] 001 01 09 11 19 21 29 31 39
[EDX] 010 02 0A 12 1A 22 2A 32 3A
[EBX] 011 03 0B 13 1B 23 2B 33 3B
[sib] 100 04 0C 14 1C 24 2C 34 3C
disp32 101 05 0D 15 1D 25 2D 35 3D
[ESI] 110 06 0E 16 1E 26 2E 36 3E
[EDI] 111 07 0F 17 1F 27 2F 37 3F
[EAX]+disp8 01 000 40 48 50 58 60 68 70 78
[ECX]+disp8 001 41 49 51 59 61 69 71 79
[EDX]+disp8 010 42 4A 52 5A 62 6A 72 7A
[EBX]+disp8 011 43 4B 53 5B 63 6B 73 7B
[sib]+disp8 100 44 4C 54 5C 64 6C 74 7C
[EBP]+disp8 101 45 4D 55 5D 65 6D 75 7D
[ESI]+disp8 110 46 4E 56 5E 66 6E 76 7E
[EDI]+disp8 111 47 4F 57 5F 67 6F 77 7F
[EAX]+disp32 10 000 80 88 90 98 A0 A8 B0 B8
[ECX]+disp32 001 81 89 91 99 A1 A9 B1 B9
[EDX]+disp32 010 82 8A 92 9A A2 AA B2 BA
[EBX]+disp32 011 83 8B 93 9B A3 AB B3 BB
[sib]+disp32 100 84 8C 94 9C A4 AC B4 BC
[EBP]+disp32 101 85 8D 95 9D A5 AD B5 BD
[ESI]+disp32 110 86 8E 96 9E A6 AE B6 BE
[EDI]+disp32 111 87 8F 97 9F A7 AF B7 BF
AL/AX/EAX/ST0/MM0/XMM0 11 000 C0 C8 D0 D8 E0 E8 F0 F8
CL/CX/ECX/ST1/MM1/XMM1 001 C1 C9 D1 D9 E1 E9 F1 F9
DL/DX/EDX/ST2/MM2/XMM2 010 C2 CA D2 DA E2 EA F2 FA
BL/BX/EBX/ST3/MM3/XMM3 011 C3 CB D3 DB E3 EB F3 FB
AH/SP/ESP/ST4/MM4/XMM4 100 C4 CC D4 DC E4 EC F4 FC
CH/BP/EBP/ST5/MM5/XMM5 101 C5 CD D5 DD E5 ED F5 FD
DH/SI/ESI/ST6/MM6/XMM6 110 C6 CE D6 DE E6 EE F6 FE
BH/DI/EDI/ST7/MM7/XMM7 111 C7 CF D7 DF E7 EF F7 FF

§ Формат байта SIB

r32 EAX ECX EDX EBX ESP 1 ESI EDI
(In decimal) Base = 0 1 2 3 4 5 6 7
(In binary) Base = 000 001 010 011 100 101 110 111
Scaled Index SS Index Value of SIB Byte (in Hexadecimal)
[EAX] 00 000 00 01 02 03 04 05 06 07
[ECX] 001 08 09 0A 0B 0C 0D 0E 0F
[EDX] 010 10 11 12 13 14 15 16 17
[EBX] 011 18 19 1A 1B 1C 1D 1E 1F
none 100 20 21 22 23 24 25 26 27
[EBP] 101 28 29 2A 2B 2C 2D 2E 2F
[ESI] 110 30 31 32 33 34 35 36 37
[EDI] 111 38 39 3A 3B 3C 3D 3E 3F
[EAX*2] 01 000 40 41 42 43 44 45 46 47
[ECX*2] 001 48 49 4A 4B 4C 4D 4E 4F
[EDX*2] 010 50 51 52 53 54 55 56 57
[EBX*2] 011 58 59 5A 5B 5C 5D 5E 5F
none 100 60 61 62 63 64 65 66 67
[EBP*2] 101 68 69 6A 6B 6C 6D 6E 6F
[ESI*2] 110 70 71 72 73 74 75 76 77
[EDI*2] 111 78 79 7A 7B 7C 7D 7E 7F
[EAX*4] 10 000 80 81 82 83 84 85 86 87
[ECX*4] 001 88 89 8A 8B 8C 8D 8E 8F
[EDX*4] 010 90 91 92 93 94 95 96 97
[EBX*4] 011 98 99 9A 9B 9C 9D 9E 9F
none 100 A0 A1 A2 A3 A4 A5 A6 A7
[EBP*4] 101 A8 A9 AA AB AC AD AE AF
[ESI*4] 110 B0 B1 B2 B3 B4 B5 B6 B7
[EDI*4] 111 B8 B9 BA BB BC BD BE BF
[EAX*8] 11 000 C0 C1 C2 C3 C4 C5 C6 C7
[ECX*8] 001 C8 C9 CA CB CC CD CE CF
[EDX*8] 010 D0 D1 D2 D3 D4 D5 D6 D7
[EBX*8] 011 D8 D9 DA DB DC DD DE DF
none 100 E0 E1 E2 E3 E4 E5 E6 E7
[EBP*8] 101 E8 E9 EA EB EC ED EE EF
[ESI*8] 110 F0 F1 F2 F3 F4 F5 F6 F7
[EDI*8] 111 F8 F9 FA FB FC FD FE FF


Mod bits base
00 disp32
01 EBP+disp8
10 EBP+disp32
Наверное это надо все попытаться объяснить. Когда выбран байт SIB, он складывает два регистра, а также смещение, если оно есть. Как и в байте modrm, вместо mod используется поле ss.
ИмяSSINDEXBASE
Биты7:65:32:0
При ss=0, будет выбран регистр из index и добавлен к регистру base, например [eax + ebx]. Если index=4, то он выбран не будет, получается что будет только одно лишь значение из базы, например [ebx]. Это является синонимом того, что можно было бы сделать и без байта SIB. В случае если base=5, то есть должен быть ebp, то тут появляются некоторые исключения.
  • mod=0, base=5, выбирается вместо ebp смещение 32-х бит
  • mod=1, base=5, выбирается ebp + смещение 8 бит
  • mod=2, base=5, выбирается ebp + смещение 32 бит
Получается что на байт SIB в данном случае влияет то, что находится в mod байта modrm.
При ss=1 индексный регистр домножается на 2, например [2*ecx], ss=2 - домножение на 4, ss=3 - на 8.