ГЛАВА 7
КОМАНДЫ ПЕРЕДАЧИ ДАННЫХ
(часть 1/2)
Команды пересылки
Среди команд микропроцессора семейства x86 достаточно много команд пересылки. Здесь мы рассмотрим следующие: MOV, LEA, XCHG и BSWAP. Кроме того, рассмотрим оператор PTR.
Команда MOV
(пересылка =”MOVE operand”)
Команда пересылки байта, слова или двойного слова. Пересылаемая
величина берется из команды, регистра или ячейки памяти, а записывается в регистр или ячейку памяти. Таких команд много, но в языке ассемблера все они записываются одинаково: MOV <
DEST>,<
SRC>
Алгоритм работы: копирование второго операнда в первый операнд.
| | P | PII | K6 | 3D! | 3Mx+ | SSE | SSE2 | A64 | SSE3 | E64T |
|---|
| LDDQU | | | | | | | | | √ | √ |
| MOV | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
| MOVAPD | | | | | | | √ | √ | √ | √ |
| MOVAPS | | | | | | √ | √ | √ | √ | √ |
| MOVD | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
| MOVDQA | | | | | | | √ | √ | √ | √ |
| MOVDQU | | | | | | | √ | √ | √ | √ |
| MOVQ | | | √ | √ | √ | √ | √ | √ | √ | √ |
| MOVUPD | | | | | | | √ | √ | √ | √ |
| MOVUPS | | | | | | √ | √ | √ | √ | √ |
Возможные варианты команды:
классический 
mov reg/mem, reg
mov reg, mem
mov mem/reg(8/16/32/64), imm(8/16/32)
mov sreg, m/r16
mov m/r16, sreg
MMX movd mmx,m64/mmx
movq mmx,m64/mmx
SSE
movups xmm,m128/xmm
movups m128,xmm
movaps xmm,m128/xmm
movaps m128,xmm
SSE2
movdqu xmm,m128/xmm
movdqu m128,xmm
movupd xmm,m128/xmm
movupd m128,xmm
movdqa xmm,m128/xmm
movdqa m128,xmm
3DNow!
movq mmx,m64/mmx
lddqu xmm,m128
Псевдокод команды:
DST
SRC
Применение: команда MOV применяется для различного рода пересылок данных, при этом, несмотря на всю простоту этого действия, необходимо помнить о некоторых ограничениях и особенностях выполнения данной операции:
- направление пересылки в команде MOV всегда справа налево, то есть из операнда SRC в операнд DEST;
- значение операнда SRC не изменяется;
- оба операнда не могут быть из памяти (при необходимости можно использовать цепочечную команду MOVS или сочетание PUSH/POP);
- лишь один из операндов может быть сегментным регистром;
- лишь один из операндов может быть управляющим регистром;
- лишь один из операндов может быть тестовым регистром;
- лишь один из операндов может быть регистром отладки;
- лишь один из операндов может быть непосредственным значением;
- желательно использовать в качестве одного из операндов регистр AL/AX/EAX/RAX, так как в этом случае транслятор генерирует более короткую форму команды MOV.
Примеры:
| Assembler |
1
2
3
4
5
6
| MOV AL,5;непосредственная запись байта в регистр
MOV BL,AL;пересылка байта из регистра в регистр
MOV Omega,CX ;пересылка слова из регистра в ячейку памяти
MOV EAX,0FFFFFFFFh;непосредственная запись двойного слова в регистр
MOV BX,DS ;пересылка байта из сегментного регистра в регистр
;общего назначения |
|
Команда MOV применяется для обмена данными между системными регистрами. Это одна из немногих возможностей доступа к содержимому этих регистров. Данную команду можно использовать только на нулевом уровне привилегий либо в реальном режиме работы микропроцессора.
| Assembler |
1
2
3
| MOV EAX,CR0;переключение микропроцессора в защищенный режим
OR EAX,1 ;помещаем в нулевой бит EAX 1
MOV CR0,EAX |
|
Если необходимо переслать в регистр адрес какой-то переменной, то Вам придется использовать в команде MOV оператор OFFSET (оператор получения смещения выражения). OFFSET позволяет получить значение
смещения выражения в байтах относительно начала того сегмента, в котором выражение определено. А если эта переменная еще и находится в другом сегменте, то в паре с оператором OFFSET Вам придется использовать оператор SEG (оператор получения сегментной составляющей адреса выражения). Оператор SEG возвращает физический адрес сегмента для выражения, в качестве которого могут выступать метка, переменная, имя сегмента, имя группы или некоторое символическое имя.
Например, если в сегменте данных содержится POLE, то следующие команды пересылают в пару ES: DX полный адрес этой переменной:
| Assembler |
1
2
3
| MOV AX,SEG POLE
MOV ES,AX
MOV DX,OFFSET POLE |
|
Здесь и далее будут показаны эквиваленты команд с целью показать вам либо более короткий код (используется при оптимизации программ по размеру), либо более длинный код (используется при написании самомодифицируемого кода), либо фрагменты кода, приводящие к эквивалентному результату (можно «размазать» данный код по программе, чтобы спрятать алгоритм от «кодокопателей»). В общем, автор показывает возможность, а уж как вы это будете использовать, это уже ваше дело...
| Команда | Эквивалент |
|---|
| MOV EAX,imm | LEA EAX,[imm] |
| MOV EAX,0 | XOR EAX,EAX/SUB EAX,EAX/AND EAX,0/IMUL EAX,0 |
| MOV [imm],0 | Если вы знаете, что регистр EAX=0, то MOV [imm],EAX |
| MOV EAX,imm | Если вы знаете, что регистр EBX=0, а число imm находится в диапазоне от -128 до +127, то LEA EAX,[EBX+imm] |
| MOV EAX,0FFFFFFFFh | MOV не делает знаковое расширение непосредственного оператора, а логические операторы делают, поэтому код MOV EAX,-1 длиннее чем OR EAX,-1 |
| MOV EDX,0 | CWDE если EAX<80000000h |
| MOV ECX,0 | a1: loop a1 |
Программирование на уровне битовДля начала учим наизусть таблицу 7.1.1 с номерами регистров:
Бит W и кодировка регистров
| dec | bin | W=0 | W=1 | Segment | Control | Debug | Test |
| | | rb | rw | rd | | | | |
| 0 | 000 | AL | AX | EAX | ES | CR0 | DR0 | |
| 1 | 001 | CL | CX | ECX | CS | | DR1 | |
| 2 | 010 | DL | DX | EDX | SS | CR2 | DR2 | |
| 3 | 011 | BL | BX | EBX | DS | CR3 | DR3 | TR3 |
| 4 | 100 | AH | SP | ESP | FS | CR4 | | TR4 |
| 5 | 101 | CH | BP | EBP | GS | | | TR5 |
| 6 | 110 | DH | SI | ESI | | | DR6 | TR6 |
| 7 | 111 | BH | DI | EDI | | | DR7 | TR7 |
Таблица 7.1.1 При попытке использовать несуществующие регистры будет сгенерировано исключение
invalid opcode.
| Инструкция | Bin-код | Hex- код | Мнемоника |
|---|
| | | B0+rb | mov r8,imm8 |
| Поместить число imm в регистр | 1011 w reg: imm | B8+rw | mov r16,imm16 |
| | | B8+rd | mov r32,imm32 |
| | | C6 | mov r8,imm8 |
| Поместить число imm в регистр | 1100 011w:11 000 reg: imm | C7 | mov r16,imm16 |
| (альтернативная кодировка) | | C7 | mov r32,imm32 |
| | | C6 | mov m8,imm8 |
| Поместить число imm в ячейку памяти mem | 1100 011w:mod 000 r/m: imm | C7 | mov m16,imm16 |
| | | C7 | mov m32,imm32 |
Мы хотим поместить в EAX число 1. Для этого к опкоду добавляем номер регистра (EAX=000).
1011:w:reg:imm

1011:1:000:imm=0B8:imm
Число, которое надо поместить в регистр, пишем сразу после опкода. То есть кодировка выглядит следующим образом:
| Assembler |
1
2
| db 0B8h
dd 1 ; mov eax,1 |
|
| | опкод | w | Reg | imm32 |
|---|
| bin | 1011 | 1 | 000=EAX | 00000001.00000000.00000000.00000000 |
| hex | 0B8h | 1,0,0,0 |
Обратите внимание: единица вставлена с помощью директивы dd, которую можно заменить на последовательность байтов 1,0,0,0. В этой последовательности байт с единицей идёт первым, так как в памяти байты слов и двойных слов располагаются от младшего к старшему, а единица находится в младшем байте. Вышеприведённую команду можно переписать так:
| Assembler |
1
| db 0B8h,1,0,0,0; mov eax,1 |
|
А можно и так, хотя это на 1 байт длиннее:
| Assembler |
1
| db 0C7h,0C0h,1,0,0,0 ; mov eax,1 |
|
| | опкод | w | mod | opcode | Reg/Mem | imm32 |
|---|
| bin | 1100011 | 1 | 11 | 000 | 000=EAX | 00000001.00000000.00000000.00000000 |
| hex | 0C7h | 0C0h | 1,0,0,0 |
Инструкция mov mem/reg,imm имеет следующий формат:
1100 011:w:Mod:000:r/m [SIB и/или смещение] imm.
В зависимости от значений в ModR/M в этот блок помимо байта ModR/M могут быть включены последовательно байт
SIB и/или смещение. Обычно этот формат используется только для mov mem,imm но ничто не мешает использовать его и для кодирования
mov reg,imm, поместив в поле
Mod=11. При этом этот вариант будет на байт длиннее, чем команда в формате 1011:w:reg imm.
Лишний байт можно использовать в целях выравнивания для того, чтобы обойтись без дополнительных команд и тактов процессора. Поле
Reg/Opcode=000. В противном случае возникнет исключение
invalid opcode.
Вместо единицы можно подставить любое 32-битное число
imm. Число
imm будет занимать столько байт сколько и приемник
reg.
Определение размера
reg происходит так же, как и в других случаях, при помощи бита
w. Если
w = 0, то размер
reg 1 байт и трёхбитовое поле reg определяет один из восьми 8-битных регистров. Если
w = 1, то размер операнда слово или двойное слово (Таблица 7.1.1).
Наличие префикса 66h переключает команду между полным и альтернативным полным размером (при размере по умолчанию 16 бит – делает операнд 32-битным и наоборот). Например, код 8BC2h соответствует сразу двум командам mov ax,dx и mov eax,edx. Какой вариант выберет процессор будет зависеть от того, в каком режиме работает программа. Если в 32-битном режиме, то, встретив опкод 8BC2h, процессор выполнит инструкцию mov eax,edx. Если в 16-битном, то mov ax,dx. Чтобы выполнить команду mov ax,dx в 32-битном режиме, непосредственно перед инструкцией помещаем префикс 66h. Таким образом, код команды
mov ax,dx в 32-битном режиме будет выглядеть так:
| Assembler |
1
2
3
| .386
. . . .
db 66h,8Bh,0C2h ;mov ax,dx |
|
Тот же префикс в 16-битном режиме выполняет обратную работу – заставляет процессор выполнить данную инструкцию как 32-битную.
| Assembler |
1
2
3
| .286
. . . .
db 66h,8Bh,0C2h ;mov eax,edx |
|
Если нужно поместить какое-либо число не в EAX, а в EDI, тогда к базовому опкоду (0B8h) следует прибавить номер регистра (EDI=111b=7).
1011:w:reg

1011:1:111b=0B8h+7=0BFh
| Assembler |
1
| db 0B9h,50h,0,0,0;mov ecx,50h |
|
| | опкод | w | Reg | imm32 |
|---|
| bin | 1011 | 1 | 001=ECX | 00000101.00000000.00000000.0000000 |
| hex | 0B9h | 50h,0,0,0 |
| Assembler |
1
| db 0BAh,20h,0,0,0;mov edx,20h |
|
| | опкод | w | Reg | imm32 |
|---|
| bin | 1011 | 1 | 010=EDX | 00000010.00000000.00000000.00000000 |
| hex | 0BAh | 20h,0,0,0 |
От копирования числа в регистры общего назначения переходим к копированию содержимого регистра в регистр.
В языке ассемблера для копирования непосредственного значения
imm в регистр, как и содержимого регистра в другой регистр или в память, применяется одна мнемоника – MOV. В тоже время эти варианты имеют разные опкоды. Причем зачастую к этим опкодам применяются разные правила.
Начнём с кодировки инструкции
mov eax,edx. Базовый опкод инструкции
mov reg1,reg2 – 8Bh, но здесь уже два операнда, а не один.
Для составления требуемого кода используется дополнительный байт, который называется ModR/M, он располагается сразу после опкода. Сам ModR/M делится на три поля следующим образом:
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|
| Mod | Reg/Opcode | R/M |
Биты 3-5 (поле Reg/Opcode) могут представлять либо уточняющий опкод (в случае, если один из операндов представлен непосредственным значением), либо регистр. Поля
Reg/Opcode (пока назовем это поле просто
Reg) и
R/M служат для указания операндов.
Составим код команды – в
Reg помещаем номер регистра
reg1, в
R/M – номер
reg2. Поле
Mod равно 3. Вот и сам код:
| Assembler |
1
| db 8Bh,0С2h;mov eax,edx |
|
| | опкод | d | w | Mod | Reg | R/M |
|---|
| bin | 100010 | 1 | 1 | 11 | 000=EAX | 010=EDX |
| hex | 8Bh | 0С2h |
Формулы этих инструкций, когда операндами являются два регистра такова:
- mov reg1,reg2
1000 100w:11 reg1 reg2
- mov reg1,reg2
1000 101w:11 reg2 reg1
Используя эти формулы легко вычислить, например, что у команды mov eax,edx есть две кодировки:
1000100:w:11 reg1 reg2

1000:100:1:11:010:000b=89D0h и
1000101:w:11 reg2 reg1

1000:101:1:11:000:010b=8BC2h
| Assembler |
1
| db 8Bh,0C2; mov eax,edx |
|
| | опкод | d | w | Mod | Reg | R/M |
|---|
| bin | 100010 | 0 | 1 | 11 | 000=EAX | 010=EDX |
| hex | 89h | 0С2h |
| Assembler |
1
| db 89h,0D0h;mov eax,edx |
|
| | опкод | d | w | Mod | Reg | R/M |
|---|
| bin | 100010 | 1 | 1 | 11 | 010=EDX | 000=EAX |
| hex | 8Bh | 0D2h |
Бит
d называется битом направления. Бит
d показывает, чем является регистр в поле
Reg: операндом-источником (
d=0) или операндом-приемником (
d=1)
Кодировку инструкции можно заменить на более универсальную:
mov reg1,reg2

100010:d:w:11:reg1:reg2
Режимы адресацииСпособ определения местонахождения операнда называется режимом адресации. Операнд машинной команды может находится в регистре процессора, указываться непосредственно в инструкции или находится в памяти данных. Процессоры семейства x86 поддерживают следующие режимы адресации:
- Регистровая адресация: mov eax,edx
В регистр EAX скопировать значение, которое находится в регистре EDX.
- Непосредственная (прямая) адресация: mov eax,[401000h]
В регистр EAX скопировать значение, которое находится в ячейке памяти с адресом 401000h. Адрес ячейки явно указан в самой инструкции.
Адрес данных может быть задан косвенно ― в команде указывается местоположение элемента памяти, где находится адрес операнда
- Косвенная базовая адресация: mov eax,[edx]
В регистр EAX скопировать значение ячейки памяти, адрес которой находится в регистре Base (=EDX).

Рис. 7.7.1. Формат команды в зависимости от поля MOD
- Косвенная базовая адресация со смещением: mov eax,[edx+1000h]
В регистр EAX скопировать значение ячейки памяти, адрес которой равен сумме 32-битного смещения (=1000h) и содержимого регистра Base (=EDX).
- Базово-индексная адресация: mov eax,[edx+ecx]
В регистр EAX копируется значение ячейки памяти, адрес которой равен сумме содержимого регистра Base (=EDX) и содержимого регистра Index (=ECX).
- Относительная базово-индексная адресация: mov eax,[edx+ecx+1000h]
В регистр EAX копируется содержимое ячейки памяти, адрес которой вычисляется следующим образом: содержимое регистра Index (=ECX) прибавляется к содержимому регистра Base (=EDX), а к полученному результату добавляется смещение (=1000h).
- Относительная базово-индексная адресация с масштабированием:mov eax,[edx+ecx*4+1000h]
В регистр EAX копируется содержимое ячейки памяти, адрес которой вычисляется следующим образом: содержимое регистра Index (=ECX) умножается на Scale (может быть 1, 2, 4 и 8, в данном случае Scale=4), прибавляется к содержимому регистра Base (=EDX), а к полученному результату добавляется смещение (=1000h).
Косвенная адресация может быть только в одном операнде, но не в двух. Поэтому инструкции, подобные следующей, недопустимы, а при попытке их трансляции будет выдана ошибка: mov [eax],[edx]
Поле Mod задаёт, что именно кодируется в Reg/Mem (рисунок 0). В сочетании с Mod, Reg/Mem может кодировать не только регистры, но и различные режимы косвенной адресации (таблица 7.1.2). Назначение поля Reg при этом не изменяется – в нём всегда кодируется номер регистра (либо расширение опкода).
Зависимость режима адресации от поля Mod
| Mod | Адрес |
|---|
| 0 | [Reg] |
| 1 | [Reg+8-битное смещение] |
| 2 | [Reg+32-битное смещение] |
| 3 | Reg |
Таблица 7.1.2Двухбитовое поле Mod вместе с трехбитовым полем Reg/Mem образует 2
5=32 возможных значения, 8 из которых обозначения регистров и 24 соответствуют режимам адресации.
Если Mod=3 в R/M находится номер регистра.
| R/M | Адрес |
|---|
| 000 | AL | AX | EAX | RAX | |
| 001 | CL | CX | ECX | RCX | |
| 010 | DL | DX | EDX | RDX | |
| 011 | BL | BX | EBX | RBX | |
| 100 | AH | SP | ESP | RSP | SPL |
| 101 | CH | BP | EBP | RBP | BPL |
| 110 | DH | SI | ESI | RSI | SIL |
| 111 | BH | DI | EDI | RDI | DIL |
Вот так интерпретируется значение поля ModR/M в режиме Mod=0.
| R/M | Адрес | SIB |
|---|
| 000 | DS: [EAX] | не представлен |
| 001 | DS:[ECX] | не представлен |
| 010 | DS: [EDX] | не представлен |
| 011 | DS:[EBX] | не представлен |
| 100 | за байтом ModR/M следует байт SIB |
| 101 | DS:[32-битное смещение] | не представлен |
| 110 | DS:[ESI] | не представлен |
| 111 | DS:[EDI] | не представлен |
Составим кодировку для инструкции:
mov eax,[edx]
8Bh это опкод операции
mov reg32, mem32:
| Assembler |
1
| db 8Bh,2 ;mov eax,[edx] |
|
| | опкод | d | w | Mod | Reg | R/M |
|---|
| bin | 1000 | 10 | 1 | 1 | 00 | 000=EAX |
| hex | 8B | 02 |
А теперь манипулируем битами
d и
w
| | опкод | d | w | Mod | Reg | R/M |
|---|
| bin | 1000 | 10 | 1 | 0 | 00 | 000=AL |
| hex | 8A | 02 |
| Assembler |
1
| db 89h,2 ;mov [edx],eax |
|
| | опкод | d | w | Mod | Reg | R/M |
|---|
| bin | 1000 | 10 | 0 | 1 | 00 | 000=EAX |
| hex | 89h | 02 |
| | опкод | d | w | Mod | Reg | R/M |
|---|
| bin | 1000 | 10 | 0 | 0 | 00 | 000=AL |
| hex | 88h | 02 |
Пусть у нас есть переменная var, содержимое которой нужно скопировать в ESI. Чтобы закодировать команду mov esi,var, помещаем в поле R/M значение 101b, а после байта ModR/M – адрес переменной:
| Assembler |
1
2
| db 8Bh,35h;mov esi,offset var
dd offset var |
|
| | опкод | d | w | Mod | Reg | R/M | imm32 |
|---|
| bin | 1000 | 10 | 1 | 1 | 00 | 110=ESI | 101 |
| hex | 8Bh | 35h | var |
Обратите внимание, в списке отсутствуют регистры EBP и ESP, хотя, инструкции типа mov eax,[ebp] и mov eax,[esp] поддерживаются. Для инструкций такого типа используют Mod=1. В этом режиме R/M интерпретируется следующим образом:
| R/M | Адрес | SIB |
|---|
| 000 | DS:[EAX+ смещение8] | не представлен |
| 001 | DS: [ECX+ смещение8] | не представлен |
| 010 | DS: [EDX+ смещение8] | не представлен |
| 011 | DS: [EBX+ смещение8] | не представлен |
| 100 | за ModR/M следует SIB, а далее 8-битное смещение |
| 101 | SS: [EBP+ смещение8] | не представлен |
| 110 | DS:[ESI+ смещение8] | не представлен |
| 111 | DS: [EDI+ смещение8] | не представлен |
Из этого списка можно получить ответ – как же закодировать инструкцию
MOV EDI,[EBP]. Фактически, у нас получается
MOV EDI,[EBP+0], а кодировка будет следующей:
| Assembler |
1
| db 8Bh,01111101b,0; MOV EDI,[EBP] |
|
| | опкод | d | w | Mod | reg | R/M | смещение8 |
|---|
| bin | 1000 | 10 | 1 | 1 | 01 | 111=EDI | 101=[EBP+смещение8] |
| hex | 8Bh | 35h | 0 |
Интерпретация значений поля ModR/M в режиме Mod=2
| R/M | Адрес | SIB |
|---|
| 000 | DS:[EAX + смещение32] | не представлен |
| 001 | DS: [ECX+ смещение32] | не представлен |
| 010 | DS: [EDX+ смещение32] | не представлен |
| 011 | DS: [EBX+ смещение32] | не представлен |
| 100 | за ModR/M следует SIB, а далее 32-битное смещение |
| 101 | SS:[EBP+ смещение32] | не представлен |
| 110 | DS: [ESI+ смещение32] | не представлен |
| 111 | DS: [EDI+ смещение32] | не представлен |
Кодируем инструкцию
mov ecx,[ebx+100000h]
:
| Assembler |
1
2
| db 8Bh,8Bh
dd 100000h ;32-битное смещение |
|
| опкод | d | w | Mod | Reg | R/M | 32-битное смещение |
|---|
| bin | 100010 | 1 | 1 | 10 | 001=ECX | 011=[EBX+ смещ32] |
| hex | 8Bh | 8Bh |
Итак, теперь вы умеете кодировать инструкции, в которых есть косвенная адресация вида [регистр+смещение] или просто адрес переменной. Чтобы окончательно понять инструкции ассемблера и овладеть искусством кодирования во всех режимах адресации, потребуется разобраться со следующим полем, которое применяется для этих целей – SIB (ScaleIndex Base). С помощью байта SIB можно задавать выражения вида:
[Base + Index * Scale +смещение].
Регистр Base – это один из 8 регистров общего назначения. Регистр Index – тоже один из регистров общего назначения, за исключением ESP. Множителем Scale может быть число 1, 2, 4 или 8. Смещение может быть 8-, 16- или 32-битным (последние два – в зависимости от того, в каком режиме работает программа, в 16- или 32-битном). С помощью префикса 67h можно в 16-разрядном режиме использовать 32-разрядные режимы адресации, и наоборот.
Закодируем инструкцию mov ecx,[edi+esi*4]. Разберем ModR/M:
Mod =0, Reg=001b (ECX), R/M=100b (потому что далее следует SIB). Теперь разберем SIB: Scale=10 (*4), регистр Index (который должен быть умножен на 4) =110b (ESI), регистр Base=111b (EDI).
В итоге получается код:
| Assembler |
1
| db 8Bh,0Ch,0B7h;mov ecx,[edi+esi*4] |
|
| | опкод | d | w | Mod | Reg | R/M | Scale | Index | Base |
| bin | 100010 | 1 | 1 | 00 | 001=ECX | 100 | 10=4 | 110=ESI | 111=EDI |
| hex | 8Bh | 0Ch | 0B7h |
Рис. 7.1.2. Структура байта SIBКодируем инструкцию
mov eax,[esp]. Mod=0, Reg=000b (EAX), R/M=100b (понадобится поле SIB). Байт SIB: множитель Scale=0 (*1), Index=100b (не используется), Base=100b (ESP):
| Assembler |
1
| db 8Bh,4,24h;mov eax,[esp] |
|
| | опкод | d | w | Mod | Reg | R/M | Scale | Index | Base |
|---|
| bin | 100010 | 1 | 1 | 00 | 000=EAX | 100 | 00=1 | 100 | 100=ESP |
| hex | 8Bh | 4 | 24h |
Изменяя значение полей Mode и SIB, получаем всевозможные варианты одной и той же инструкции mov eax,[esi]:
| Код | Команда |
|---|
| 8B 06 | mov eax,[esi] |
| 8B 04 35 00 00 00 00 | mov eax,[Index=esi´1+смещ32=0] |
| 8B 04 26 | mov eax,[Base=esi] |
| 8B 46 00 | mov eax,[esi+смещ8=0] |
| 8B 44 26 00 | mov eax,[Base=esi+смещ8=0] |
| 8B 86 00 00 00 00 | mov eax,[esi+смещ32=0] |
| 8B 84 26 00 00 00 00 | mov eax,[Base=esi+смещ32=0] |
А теперь итоговая таблица
| R/M | Mod | SIB |
| | 0 | 1 | 2 | 3 | Index | Base |
| 000 | [EAX] | [EAX+смещ8] | [EAX+32-битное смещение] | EAX | [EAX*N] | EAX |
| 001 | [ECX] | [ECX+смещ8] | [ECX+32-битное смещение] | ECX | [ECX*N] | ECX |
| 010 | [EDX] | [EDX+смещ8] | [EDX+32-битное смещение] | EDX | [EDX*N] | EDX |
| 011 | [EBX] | [EBX+смещ8] | [EBX+32-битное смещение] | EBX | [EBX*N] | EBX |
| 100 | за байтом ModR/M следует байт SIB | за ModR/M следует SIB, а далее 8-битное смещение | за ModR/M следует SIB, а далее 32-битное смещение | ESP | нет | ESP |
| 101 | за ModR/M следует 32-битное смещение (адрес памяти) | [EBP+смещ8] | [EBP+смещ32] | EBP | [EBP*N] | зависит от значения Mod |
| 110 | [ESI] | [ESI+смещ8] | [ESI+смещ32] | ESI | [ESI*N] | ESI |
| 111 | [EDI] | [EDI+смещ8] | [EDI+смещ32] | EDI | [EDI*N] | EDI |
Если в качестве базового регистра будет выбран EBP, то полученный режим адресации будет зависеть от значения Mod
| Mod | Действие |
|---|
| 0 | [Index+Смещение32] – регистр EBP в адресации не участвует |
| 1 | [EBP+Index+8-битное смещение] |
| 2 | [EBP+Index+32-битное смещение] |
| 3 | EBP |
Обратите внимание на следующее:
во-первых, регистр Index не может быть ESP, ведь значение 100b, которое должно было символизировать ESP, указывает, что Index не используется. Соответственно, закодировать инструкцию mov eax,[edx+esp*4] нельзя, а инструкции mov eax,[esp+edx] или mov eax,[esp+4*edx] можно;
во-вторых, если Mod=00b, то Reg1= 101b означает, что далее следует 8-битное смещение.
Поэтому, чтобы закодировать mov eax,[ebp+edx], нужен описанный выше трюк: в Mod помещается 01b, а после SIB идет нулевое 8-битное смещение.
db 8Bh,44h,15h,0;mov eax,[ebp+edx+0]
| | опкод | d | w | Mod | Reg | R/M | Scale | Index | Base | offset8 |
|---|
| bin | 100010 | 1 | 1 | 01 | 000=EAX | 100 | 00=1 | 010=EDX | 101=EBP | 00000000 |
| hex | 8Bh | 44h | 15h | 0 |
Уменьшение размера кодировки MOV
Там, где это возможно, меняйте адресацию с прямой на косвенную, но только в случае, когда смещение лежит в диапазоне от 80h(-128) до 7Fh(+127).
Пример: (Пусть EDI=5)
8B0F05 MOV EBX,[EDI+5]
код короче на 2 байта, чем
BB0A000000 MOV EBX,0Ah
Косвенная адресация с заранее известным значением в одном из регистров дает возможность использовать знаковое расширение непосредственного значения в команде MOV.