ГЛАВА 10
АРИФМЕТИЧЕСКИЕ КОМАНДЫ
(часть 4/7)
Команды умножения
Если сложение и вычитание для знаковых и беззнаковых чисел производятся по одним и тем же алгоритмам, то умножение и деление для знаковых и беззнаковых чисел производятся по разным алгоритмам.
Допустим, вы умножаете 0FFh на 0FFh, если вы перемножаете числа со знаком, тогда результатом будет 1 (

), а если беззнаковые – тогда 0FE01h (

). Например, чтобы умножить или разделить два числа с разными знаками вам придется сначала отрицательное число преобразовать командой NEG в положительное, затем перемножить или разделить эти числа, затем опять командой NEG преобразовать результат. Поэтому для знаковых и беззнаковых чисел имеются по две команды умножения и деления. В остальном эти команды действуют одинаково.
Сочетание операций сдвигов (глава «Команды сдвига») и сложения позволяют осуществлять умножение чисел без знака.
Команда MUL
(Умножение целых без учета знака = «Unsigned MULTIPLY»)
Синтаксис команды:
MUL <
SRC>
Возможные варианты команды:
mul reg/mem
Семантика команды: операция умножения двух целых чисел без учета знака.
Псевдокод команды:
IF OperandSize=8
THEN
AX

AL*SRC
IF AL=AX
THEN CF

0; OF

0
ELSE CF

1 OF

1 ENDIF
ELSE IF OperandSize =16
THEN DX:AX

AX*SRC
IF AX=DX:AX
THEN CF

0; OF

0
ELSE CF

1 OF

1 ENDIF
ELSE IF OperandSize =32
THEN EDX:EAX

EAX*SRC
IF EAX=EDX:EAX
THEN CF

0 OF

0
ELSE CF

1 OF

1 ENDIF
ELSE IF OperandSize =64
THEN RDX:RAX

RAX*SRC
IF RAX=RDX:RAX
THEN CF

0 OF

0
ELSE CF

1 OF

1 ENDIF
ENDIF
Алгоритм работы: команда выполняет умножение двух операндов без учета знаков. Алгоритм зависит от формата операнда команды и требует явного указания местоположения только одного сомножителя SRC, который может быть расположен в памяти или в регистре, но не может быть непосредственно числом. Местоположение второго сомножителя фиксировано и зависит от размера сомножителя SRC:
- если перемножить максимальные числа величиной в байт – результат займет 2 байта:
0FFh * 0FFh = 255 * 255 = 65025 =0FE01h;
- при перемножении максимальных чисел величиной в слово (2 байта)
– результат займет уже 4 байта:
0FFFFh * 0FF FFh = 65535 * 65535 = 4294836225 = 0FFFE0001h;
и так далее, двоичный код результата будет занимать в два раза больше места, чем максимальный двоичный код сомножителей;
- если операнд SRC, указанный в команде – байт, то второй сомножитель расположен в регистре AL;
- если операнд SRC, указанный в команде – слово, то сомножитель расположен в регистре AX;
- если операнд SRC, указанный в команде – двойное слово, то сомножитель расположен в регистре EAX.
Местоположение результата умножения заранее известно и поэтому в команде явно не указывается. Под результат отводят в два раза больше места, чем под сомножители. Место, в которое будет помещен результат, определяется размером сомножителей:
- при умножении байтов результат помещается в AX;
- при умножении слов результат помещается в пару DX:AX;
- при умножении двойных слов результат помещается в пару EDX:EAX;
- при умножении счетверенных слов результат помещается в пару RDX:RAX.
Для получения квадрата числа поместите первый сомножитель в регистр AL/AX/EAX/RAX.
Хотя команда умножения выдает результат в удвоенном формате, не всегда величина произведения столь велика, что ей нужен удвоенный формат. Очень часто результат имеет тот же формат, что и сомножители. Вопрос о том, уместится ли результат умножения в формат сомножителей или нет, решается с помощью анализа флагов CF и OF:
CF=OF=1 – если произведение занимает двойной формат;
CF=OF=0 – если произведению достаточен формат сомножителей.
| Assembler |
1
2
3
4
5
6
7
| .data
MN_1 DB 15
MN_2 DB 25
.code
MOV AH,0
MOV AL,MN_1 ;AX=15=0Fh
MUL MN_2 ;AX=15*25=375=177h |
|
Умножение с многократной точностью

Команда MUL предназначена в основном для операций умножения с многократной точностью. Для иллюстрации продемонстрируем умножение двух 64-битных неотрицательных чисел:

,
где

и

Получаем классический алгоритм с 3 сложениями и 4 умножениями.
Так как умножение выполняется несколько медленнее, чем сложение, мы можем воспользоваться алгоритмом Карацуба из 4 сложений, 2 вычитаний и 3 умножений:
- вычислим
– первое умножение
- вычислим
– второе умножение
- вычислим
– третье умножение

Или используем алгоритм Тоома-Кука из 5 сложений, 3 вычитаний и 3 умножений:
- вычислим
– первое умножение
- вычислим
– второе умножение
- вычислим
– третье умножение
Так как

и

поэтому
Можно также использовать следующее свойство:
и обойтись 2 умножениями (возведение в квадрат), 4 сложениями и 3 вычитаниями.
Пример оптимизации по размеру

Реализуем алгоритм в 3 сложения и 4 умножения. Пусть и сами числа, и место под результат располагаются в памяти
последовательно
| Адрес | Машинный код | Вид команды до трансляции |
|---|
| 004000C4 | A130014000 | | mov eax,b |
| 004000C9 | F72538014000 | | mul d |
| 004000CF | A340014000 | | mov dword ptr Z,eax |
| 004000D4 | 891544014000 | | mov dword ptr [Z+4],edx |
| 004000DA | A130014000 | | mov eax,b |
| 004000DF | F7253C014000 | | mul c |
| 004000E5 | 010544014000 | | add dword ptr [Z+4],eax |
| 004000EB | 111548014000 | | adc dword ptr [Z+8],edx |
| 004000F1 | 7306 | | jnc a1;если был перенос увеличим [Z+12] |
| 004000F3 | FF054C014000 | | inc dword ptr [Z+12] |
| 004000F9 | A134014000 | a1: | mov eax,a |
| 004000FE | F72538014000 | | mul d |
| 00400104 | 010544014000 | | add dword ptr [Z+4],eax |
| 0040010A | 111548014000 | | adc dword ptr [Z+8],edx |
| 00400110 | 7306 | | jnc a2;если был перенос увеличим [Z+12] |
| 00400112 | FF054C014000 | | inc dword ptr [Z+12] |
| 00400118 | A134014000 | a2: | mov eax,a |
| 0040011D | F7253C014000 | | mul c |
| 00400123 | 110548014000 | | add dword ptr [Z+8],eax |
| 00400129 | 11154C014000 | | adc dword ptr [Z+12],edx |
| 0040012F | C3 | | ret ;выход из программы |
| 00400130 | FFFFFFFF | b | dd 0FFFFFFFFh ;сомножители |
| 00400134 | FFFFFFFF | a | dd 0FFFFFFFFh ;X и Y |
| 00400138 | FFFFFFFF | d | dd 0FFFFFFFFh |
| 0040013C | FFFFFFFF | c | dd 0FFFFFFFFh |
| 00400140 | 0000000000000000 | Z | dq 2 dup (0) ;результат |
| 00400148 | 0000000000000000 | | |
Сама программа после компиляции занимает 150h-0C4h=8Ch=140 байт. Пытаемся уменьшить размер программы. Так как и сомножители и место под результат расположены в памяти последовательно, то можно уменьшить размер кода программы если мы используем не прямую, а косвенную адресацию. При каждом таком обращении к памяти будет сэкономлено до четырех байт. Адреса операндов будут передаваться через регистр EBX.
| Адрес | Машинный код | Вид команды до трансляции |
|---|
| 004000C4 | BB02014000 | | mov ebx,offset X |
| 004000C9 | 8B03 | | mov eax,[ebx] |
| 004000CB | F76308 | | mul dword ptr [ebx+8] |
| 004000CE | 894310 | | mov dword ptr [ebx+16],eax |
| 004000D1 | 895314 | | mov dword ptr [ebx+20],edx |
| 004000D4 | 8B03 | | mov eax,[ebx] |
| 004000D6 | F7630C | | mul dword ptr [ebx+12] |
| 004000D9 | 014314 | | add dword ptr [ebx+20],eax |
| 004000DC | 115318 | | adc dword ptr [ebx+24],edx |
| 004000DF | 7303 | | jnc a1;если был перенос увеличим [ebx+28] |
| 004000E1 | FF431C | | inc dword ptr [ebx+28] |
| 004000E4 | 8B4304 | a1: | mov eax,[ebx+4] |
| 004000E7 | F76308 | | mul dword ptr [ebx+8] |
| 004000EA | 014314 | | add dword ptr [ebx+20],eax |
| 004000ED | 115318 | | adc dword ptr [ebx+24],edx |
| 004000F0 | 7303 | | jnc a2;если был перенос увеличим [ebx+28] |
| 004000F2 | FF431C | | inc dword ptr [ebx+28] |
| 004000F5 | 8B4304 | a2: | mov eax,[ebx+4] |
| 004000F8 | F7630C | | mul dword ptr [ebx+12] |
| 004000FB | 114318 | | add dword ptr [ebx+24],eax |
| 004000FE | 11531C | | adc dword ptr [ebx+28],edx |
| 00400101 | C3 | | ret ;выход из программы |
| 00400102 | FFFFFFFFFFFFFFFF | X | dd 0FFFFFFFFFFFFFFFFh ;сомножители |
| 0040010A | FFFFFFFFFFFFFFFF | Y | dd 0FFFFFFFFFFFFFFFFh |
| 00400112 | 0000000000000000 | Z | dq 2 dup (0) ;место под результат |
| 0040011A | 0000000000000000 | | |
122h–C4h=5Eh=94 байта
В программе имеются повторяющиеся фрагменты кода, выделенные жирным шрифтом, которые можно оформить как процедуру, кроме того, две команды JNC метка и INC DWORD PTR [EBX+28] можно заменить на одну команду ADC DWORD PTR [EBX+28],0. Это сэкономит еще по одному байту кода.
| Адрес | Машинный код | Вид команды до трансляции |
|---|
| 004000C4 | BB02014000 | mov ebx,offset X |
| 004000C9 | 8B03 | mov eax,[ebx] |
| 004000CB | F76308 | mul dword ptr [ebx+8] |
| 004000CE | 894310 | mov dword ptr [ebx+16],eax |
| 004000D1 | 895314 | mov dword ptr [ebx+20],edx |
| 004000D4 | 8B03 | mov eax,[ebx] |
| 004000D6 | F7630C | mul dword ptr [ebx+12] |
| 004000D9 | E812000000 | call routine; вызов процедуры |
| 004000DE | F76308 | mul dword ptr [ebx+8] |
| 004000E1 | E80A000000 | call routine |
| 004000E6 | F7630C | mul dword ptr [ebx+12] |
| 004000E9 | 114318 | add dword ptr [ebx+24],eax |
| 004000EC | 11531C | adc dword ptr [ebx+28],edx |
| 004000EF | C3 | ret ;выход из программы |
| routine proc; процедура |
| 004000F0 | 014314 | add dword ptr [ebx+20],eax |
| 004000F3 | 115318 | adc dword ptr [ebx+24],edx |
| 004000F6 | 83531C00 | adc dword ptr [ebx+28],0 |
| 004000FA | 8B4304 | mov eax,[ebx+4] |
| 004000FD | C3 | ret;выход из процедуры |
| routine endp;сомножители |
| 004000FE | FFFFFFFFFFFFFFFF | X dd 0FFFFFFFFFFFFFFFFh |
| 0040010A | FFFFFFFFFFFFFFFF | Y dd 0FFFFFFFFFFFFFFFFh |
| 00400112 | 0000000000000000 | Z dq 2 dup (0) ; место под результат |
| 0040011A | 0000000000000000 | |
11Eh-C4h=5Ah=90 байт
Итак, нам удалось уменьшить программу со 140 до 90 байт, то есть на 50 байт. Попробуем написать еще более короткий код. Вернем в текст программы то, что мы вынесли в подпрограмму. В тексте программы есть три очень похожих фрагмента.
| 1) MOV [EBX+16], EAX | 2) ADD [EBX+20], EAX | 3) ADD [EBX+24],EAX |
| MOV [EBX+20], EDX | ADD [EBX+24], EDX | ADC [EBX+28],EDX |
| | ADC [EBX+28],0 | |
Так как в ячейках памяти, отведенных под результат, пока еще ничего нет и если в ячейки [EBX+16] и [EBX+20] заранее поместить 0, тогда результат команд MOV [EBX+16],EAX и ADD [EBX+16],EAX будет эквивалентен. Добавим также для большей схожести в первый фрагмент – команду ADC DWORD PTR [EBX+24],0, в третий фрагмент – команду ADC DWORD PTR [EBX+32],0. Использование этих команд результат не изменит.
| MOV EAX,[EBX] | MOV EAX,[EBX] | MOV EAX,[EBX] |
| | | MOV EDI,8 |
| MUL D/[EBX+8] | MUL D/[EBX+8] | MUL D/[EBX+EDI] |
| MOV [EBX+16],EAX | ADD [EBX+16],EAX | ADD [EBX+16],EAX |
| MOV [EBX+20],DX | ADC [EBX+20],EDX | ADC [EBX+20],EDX |
| | ADC [EBX+24],0 | ADC [EBX+24],0 |
| MOV EAX,[EBX] | MOV EAX,[EBX] | MOV EAX,[EBX] |
| | | ADD EBX,4 |
| MUL D/[EBX+12] | MUL D/[EBX+8+4] | MUL D/[EBX+EDI] |
| ADD [EBX+20],EAX | ADD [EBX+16+4],EAX | ADD [EBX+16],EAX |
| ADC [EBX+24],EDX | ADC [EBX+20+4],EDX | ADC [EBX+20],EDX |
| ADC [EBX+28],0 | ADC [EBX+24+4],0 | ADC [EBX+24],0 |
| MOV EAX,[EBX+4] | MOV EAX,[EBX+4] | MOV EAX,[EBX] |
| | | ADD EBX,4 |
| | | SUB EBX,4 |
| | | SUB EDI,4 |
| MUL D/[EBX+8] | MUL D/[EBX+4+4] | MUL D/[EBX+EDI] |
| ADD [EBX+20],EAX | ADD [EBX+16+4],EAX | ADD [EBX+16],EAX |
| ADC [EBX+24],EDX | ADC [EBX+20+4],EDX | ADC [EBX+20],EDX |
| ADC [EBX+28],0 | ADC [EBX+24+4],0 | ADC [EBX+24],0 |
| MOV EAX,[EBX+4] | MOV EAX,[EBX+4] | MOV EAX,[EBX] |
| | | ADD EBX,4 |
| MUL D/[EBX+12] | MUL D/[EBX+4+8] | MUL D/[EBX+EDI] |
| ADD [EBX+24],EAX | ADD [EBX+16+8],EAX | ADD [EBX+16],EAX |
| ADC [EBX+28],EDX | ADC [EBX+20+8],EDX | ADC [EBX+20],EDX |
| | ADC [EBX+24+8],0 | ADC [EBX+24],0 |
| | | MOV EAX,[EBX] |
| | | ADD EBX,4 |
Жирным шрифтом выделены повторяющиеся фрагменты кода. Можно было бы использовать вызов подпрограмм, но при использовании цикла код программы будет короче. Вот что у нас получилось в результате:
| Адрес | Машинный код | Вид команды до трансляции |
|---|
| 004000C4 | BBEF004000 | | mov ebx,offset X |
| 004000C9 | 8B03 | | mov eax,[ebx] |
| 004000CB | BF08000000 | | mov edi,8 |
| 004000D0 | B902000000 | a2: | mov ecx,2 |
| 004000D5 | F76308 | a1: | mul dword ptr [ebx+8] |
| 004000D8 | 014310 | | add [ebx+16],eax |
| 004000DB | 115314 | | adc [ebx+20],edx |
| 004000DE | 83531800 | | adc dword ptr [ebx+24],0 |
| 004000E2 | 83C304 | | add ebx,4 |
| 004000E5 | E2EF | | loop a1 |
| 004000E7 | 83EB04 | | sub ebx,4 |
| 004000EA | 83EF04 | | sub edi,4 |
| 004000ED | 75E2 | | jnz a2 |
| 004000EF | C3 | | ret ;выход из программы |
| 004000F0 | FFFFFFFFFFFFFFFF | X | dd 0FFFFFFFFFFFFFFFFh;сомножители |
| 004000F8 | FFFFFFFFFFFFFFFF | Y | dd 0FFFFFFFFFFFFFFFFh |
| 00400100 | 0000000000000000 | Z | dq 2 dup (0) ;место под результат |
| 00400108 | 0000000000000000 | | |
110h-0C4h=4Ch=76 байт.
Подведем итоги:

Попробуйте сами написать еще короче, может быть получится…
Пример оптимизации по быстродействию
a,
b,
c и
d 16-разрядные числа. Найти

, используя только 16-разрядные арифметические операции.
1) Решение проблемы «в лоб»:
| Assembler |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| mov ax,a
mul c ;dx:ax=a*c
mov [z0],ax ;младший байт результата
mov [z1],dx
mov [z2],0 ;старший байт результата
mov ax,b
mul c ;dx:ax=b*c
add [z0],ax
adc [z1],dx
adc [z2],0
mov ax,a
mul d ;dx:ax=a*d
add [z0],ax
adc [z1],dx
adc [z2],0
mov ax,b
mul d ;dx:ax=b*d
add [z0],ax
adc [z1],dx
adc [z2],0 |
|
2) Так как умножение достаточно медленная операция – постараемся свести количество умножений к минимуму.

,
где операция

возвращает младшие 16 бит результата, а x,y={0,1} биты переноса, образовавшиеся в результате сложений.

Умножение на {0,1} можно заменить на логические побитовые операции:
x*y=-((-x)&(-y))
x*(c+d) mod 2
16 =(-x)&(c+d) mod 2
16
y*(a+b) mod 2
16 =(-y)&(a+b) mod 2
16
тогда
(a+b)*(c+d) = -((-x)&(-y))*2
32 + ((-x)&(c+d) mod 2
16 +
+(-y)&(a+b) mod 2
16)*2
16+((a+b) mod 2
16)*((c+d) mod 2
16)
| Assembler |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| mov ax,a
add ax,b ;ax=(a+b) mod 216
sbb cx,cx ;cx=-x
mov dx,cx ;dx=-x
mov bx,c
add bx,d ;bx=(c+d) mod 216
sbb si,si ;si=-y
and dx,si ;dx=(-x)&(-y)
neg dx ;dx=-((-x)&(-y))
mov [z2],dx
and cx,bx ;cx=(-x)&(c+d) mod 216
and si,ax ;si=(-y)&(a+b) mod 216
add cx,si
mov [z1],cx
adc [z2],0
mul bx ;dx:ax=((a+b) mod 216)?((c+d) mod 216)
mov [z0],ax ;младший байт результата
add [z1],dx
adc [z2],0 ;старший байт результата |
|
Команда IMUL(Умножение целых чисел со знаком =” SIGNED MULTIPLY”)
Синтаксис команды Количество операндов
IMUL <SRC> (1)
IMUL <DEST>,<SRC> (2а)
IMUL <DEST>,<Imm> (2б)
IMUL<DEST>,<SRC>,<Imm> (3)
Возможные варианты команды:
imul reg/mem
imul reg,reg/mem
imul reg,imm
imul reg,reg/mem,imm
Семантика команды: операция умножения двух целочисленных двоичных значений со знаком.
Алгоритм работы: алгоритм работы команды зависит от используемой формы команды. Форма команды с одним операндом (1) требует явного указания местоположения только сомножителя SRC, который может быть расположен в ячейке памяти или регистре. Местоположение второго сомножителя фиксировано и зависит от размера сомножителя SRC:
- если операнд SRC, указанный в команде, – байт, то второй сомножитель расположен в регистре AL;
- если операнд SRC, указанный в команде, – слово, то второй сомножитель расположен в регистре AX;
- если операнд SRC , указанный в команде, – двойное слово, то второй сомножитель расположен в регистре EAX.
Пример:
| Assembler |
1
2
| Alfa db –200
IMUL Alfa ; AL*(–200)-> AX |
|
Результат умножения для команды с одним операндом также помещается в строго определенное место, определяемое размером сомножителей:
- при умножении байтов результат помещается в AX;
- при умножении слов результат помещается в пару DX:AX;
- при умножении двойных слов результат помещается в пару EDX:EAX;
- при умножении счетверенных слов результат помещается в пару RDX:RAX.
Команды с двумя и тремя операндами однозначно определяют расположение результата и сомножителей следующим образом:
- в команде с двумя операндами (2а) сомножители DEST и SRC. Результат помещается в DEST;
Псевдокод команды:

- в команде с двумя операндами (2б) сомножители DEST и непосредственно указанное число Imm. Результат помещается в DEST;
Псевдокод команды:

- в команде с тремя операндами (3) сомножители SRC и непосредственно указанное число Imm размером в байт, слово или двойное слово. Результат помещается в DEST.
Псевдокод команды:
DEST

SRC

Imm
По команде с с двумя операндами (2а или 2б) генерируется команда
умножения с тремя операндами (3), где операнд SRC=DEST .
Псевдокод команды:
IF (NumberOfOperands=1)
THEN IF (OperandSize=8)
THEN
AX

AL*SRC(Знаковое расширение)
IF AL=AX
THEN CF

0; OF

0
ELSE CF

1 OF

1 ENDIF
ELSE IF OperandSize=16
THEN
DX:AX

AX*SRC(Знаковое расширение)
IF sign_extend_to_32 (AX)=DX:AX
THEN CF

0 OF

0
ELSE CF

1 OF

1 ENDIF
ELSE IF OperandSize=32
THEN
EDX:EAX

EAX*SRC(Знаковое расширение)
IF EAX=EDX:EAX
THEN CF

0 OF

0
ELSE CF

1 OF

1 ENDIF
ENDIF
ENDIF
ELSE IF (NumberOfOperands=2)
THEN IF (OperandSize=8)
THEN
temp

DEST*SRC
(Знаковое расширение DEST*SRC, у temp удвоенный размер DEST)
DEST

DEST*SRC(Знаковое расширение)
IF temp?DEST
THEN CF

1; OF

1
ELSE CF

0 OF

0 ENDIF
ELSE (NumberOfOperands=3)
DEST

SRC1*SRC2(Знаковое расширение)
temp

SRC1*SRC2
(Знаковое расширение SRC1*SRC2; у temp удвоенный размер SRC1)
IF temp?DEST
THEN CF

1; OF

1
ELSE CF

0 OF

0 ENDIF
ENDIF
ENDIF
Команда IMUL устанавливает в ноль флаги OF и CF, если размер результата соответствует регистру назначения. Если эти флаги отличны от нуля, то это означает, что результат слишком велик для отведенных ему регистром назначения рамок и необходимо указать больший по размеру регистр для успешного завершения данной операции умножения. Конкретными условиями сброса флагов OF и CF в ноль являются следующие условия:
- для однооперандной формы команды IMUL регистры AX/DX/EDX являются знаковыми расширениями регистров AL/AX/EAX;
- несмотря на использование регистров EAX и EDX по умолчанию, для двух- и трехоперандной формы команды IMUL можно указать другие источники и приёмники. Первый операнд – место, куда необходимо поместить результат и которое всегда должно быть регистром, второй и третий операнды – два значения, которые нужно перемножить. Для размещения результата умножения достаточно размерности указанных регистров назначения r16/32.
Применение: команда выполняет целочисленное умножение операндов с учетом их знаковых разрядов. Для выполнения этой операции необходимо наличие двух сомножителей. Размещение и задание их местоположения в команде зависит от формы применяемой команды умножения, которая, в свою очередь, определяется моделью микропроцессора. Так, для микропроцессоров до i80386 возможна только однооперандная форма команды, для последующих моделей микропроцессоров дополнительно можно использовать одно-, двух- и трехоперандные формы этой команды.
| Assembler |
1
2
3
4
5
| MOV BX,186
IMUL AX,BX,7 ; AX:=BX*7
;если результату не хватило размерности операнда1,
;то перейдем на метку M1, где и скорректируем ситуацию
JC M1 |
|
Следующий пример:
| Assembler |
1
| IMUL EBP,DWORD PTR [ESI+74],0FF800002h; EBP:=[ESI+74] * 0FF800002h |
|
Пусть в ячейке памяти [esi+74] находится число 0C7000000h (–956301312). Это значение будет умножено на 0FF800002h (–8388606), а результат данной операции будет сохранён в EBP:
–956301312?(–8388606)=8022034923651072=1 C7FFF8E000000h
Поскольку 32-разрядный регистр EBP не может вместить это 64-разрядное число полностью, то в нем остается содержимое младших 32 разрядов, равное 8E000000h, а содержимое старших 32 разрядов будет потеряно.
В следующем примере два операнда, которые перемножаются друг на друга, а результат сохраняется в первом операнде.
| Assembler |
1
| IMUL EDX, DWORD PTR [EBP-18]; EDX:=EDX * [EBP-18] |
|
Основной способ для умножения больших чисел – использовать IMUL только с одним операндом, так как в этом случае результат сохраняется в паре регистров EDX:EAX, то есть он может быть двойного размера, чего нельзя добиться при использовании двух или трех операндов, поэтому применяйте двух- и трехоперандные команды imul для умножения небольших чисел.
Пусть нам потребовалось закодировать команду imul esi,5. Из всех опкодов нам подходит следующий: 6B /r ib (IMUL r32, r/m32, imm8). Согласно описанию, поле Reg задает регистр назначения, R/M – регистр или адрес ячейки памяти с умножаемым значением, последним идет байт множителя.
| | опкод | d | w | Mod | Reg | R/M | imm8 |
|---|
| bin | 011010 | 1 | 1 | 11 | 110=ESI | 110=ESI | 0101 |
| hex | 6Bh | 0F6h | 5 |
Продолжение