IMUL(有符号数乘法)指令执行有符号整数乘法。与 MUL 指令不同,IMUL 会保留乘 积的符号,实现的方法是,将乘积低半部分的最高位符号扩展到高半部分。
x86 指令集支持三种格式的 IMUL 指令:单操作数、双操作数和三操作数。单操作数格式中,乘数和被乘数大小相同,而乘积的大小是它们的两倍。
单操作数格式将乘积存放在 AX、DX:AX 或 EDX:EAX 中:
和 MUL 指令一样,其乘积的存储大小使得溢出不会发生。同时,如果乘积的高半部分不是其低半部分的符号扩展,则进位标志位和溢出标志位置 1。利用这个特点可以决定是否忽略乘积的高半部分。
32 位模式中的双操作数 IMUL 指令把乘积存放在第一个操作数中,这个操作数必须是寄存器。第二个操作数(乘数)可以是寄存器、内存操作数和立 即数。16位格式如下所示:
32 位操作数类型如下所示,乘数可以是 32 位寄存器、32 位内存操作数或立即数(8 位 或 32 位):
双操作数格式会按照目的操作数的大小来截取乘积。如果被丢弃的是有效位,则溢出标志位和进位标志位置 1。因此,在执行了有两个操作数的 IMUL 操作后,必须检查这些标志位中的一个。
32 位模式下的三操作数格式将乘积保存在第一个操作数中。第二个操作数可以是 16 位寄存器或内存操作数,它与第三个操作数相乘,该操作数是一个8位或16 位立即数:
而 32 位寄存器或内存操作数可以与 8 位或 32 位立即数相乘:
IMUL 执行时,若乘积有效位被丢弃,则溢出标志位和进位标志位置 1。因此,在执行了有三个操作数的 IMUL 操作后,必须检查这些标志位中的一个。
在 64 位模式下,IMUL 指令可以使用 64 位操作数。在单操作数格式中,64 位寄存器或内存操作数与 RAX 相乘,产生一个 128 位且符号扩展的乘积存放到 RDX:RAX 寄存器中。在下面的例子中,RBX 与 RAX 相乘,产生 128 位的乘积 -16。
也就是说,十进制数 -16 在 RAX 中表示为十六进制 FFFF FFFF FFF0,而 RDX 只包含 TRAX 的高位扩展,即它的符号位。
三操作数格式也可以用于 64 位模式。如下例所示,被乘数 (-16) 乘以 4,生成 RAX 中的乘积 -64:
.data
multiplicand QWORD -16
.code
imul rax, multiplicand, 4 ; RAX = FFFFFFFFFFFFFFC0 (-64)
由于有符号数和无符号数乘积的低半部分是相同的,因此双操作数和三操作数的 IMUL 指令也可以用于无符号乘法。但是这种做法也有一点不便的地方:进位标志位和溢出标志位将无法表示乘积的高半部分是否为零。
下述指令执行 48 乘以 4,乘积 +192 保存在 AX 中。虽然乘积是正确的,但是 AH 不是 AL 的符号扩展,因此溢出标志位置 1:
下述指令执行 -4 乘以 4,乘积 -16 保存在 AX 中。AH 是 AL 的符号扩展,因此溢出标志位清零:
下述指令执行 48 乘以 4,乘积 +192 保存在 DX:AX 中。DX 是 AX 的符号扩展,因此溢出标志位清零:
下述指令执行 32 位有符号乘法 (4 823 424*-423),乘积 -2 040 308 352 保存在 EDX:EAX 中。溢出标志位清零,因为 EDX 是 EAX 的符号扩展:
下述指令展示了双操作数格式:
.data
word1 SWORD 4
dword1 SDWORD 4
.code
mov ax, -16 ; AX = -16
mov bx, 2 ; BX = 2
imul bx, ax ; BX = -32
imul bx, 2 ; BX = -64
imul bx, word1 ; BX = -256
mov eax, -16 ; EAX = -16
mov ebx, 2 ; EBX = 2
imul ebx, eax ; EBX = -32
imul ebx, 2 ; EBX = -64
imul ebx, dword1 ; EBX = -256
双操作数和三操作数 IMUL 指令的目的操作数大小与乘数大小相同。因此,有可能发生有符号溢出。执行这些类型的 IMUL 指令后,总要检查溢岀标志位。下面的双操作数指令展示了有符号溢出,因为 -64000 不适合 16 位目的操作数:
下面的指令展示的是三操作数格式,包括了有符号溢出的例子:
.data
word1 SWORD 4
dword1 SDWORD 4
.code
imul bx, word1, -16 ; BX = word1 * -16
imul ebx, dword1, -16 ; EBX = dword1 * -16
imul ebx, dword1, -2000000000 ; 有符号溢出!