汇编语言IMUL指令:有符号数乘法


IMUL(有符号数乘法)指令执行有符号整数乘法。与 MUL 指令不同,IMUL 会保留乘 积的符号,实现的方法是,将乘积低半部分的最高位符号扩展到高半部分。

x86 指令集支持三种格式的 IMUL 指令:单操作数、双操作数和三操作数。单操作数格式中,乘数和被乘数大小相同,而乘积的大小是它们的两倍。

单操作数格式

单操作数格式将乘积存放在 AX、DX:AX 或 EDX:EAX 中:

IMUL reg/mem8    ; AX = AL * reg/mem8
IMUL reg/meml6    ; DX:AX = AX * reg/meml6
IMUL reg/mem32    ; EDX:EAX = EAX * reg/mem32

和 MUL 指令一样,其乘积的存储大小使得溢出不会发生。同时,如果乘积的高半部分不是其低半部分的符号扩展,则进位标志位和溢出标志位置 1。利用这个特点可以决定是否忽略乘积的高半部分。

双操作数格式(32位模式)

32 位模式中的双操作数 IMUL 指令把乘积存放在第一个操作数中,这个操作数必须是寄存器。第二个操作数(乘数)可以是寄存器、内存操作数和立 即数。16位格式如下所示:

IMUL regl6, reg/meml6
IMUL regl6, imm8
IMUL regl6, imml6

32 位操作数类型如下所示,乘数可以是 32 位寄存器、32 位内存操作数或立即数(8 位 或 32 位):

IMUL reg32, reg/mem32
IMUL reg32, inun8
IMUL reg32, imm32

双操作数格式会按照目的操作数的大小来截取乘积。如果被丢弃的是有效位,则溢出标志位和进位标志位置 1。因此,在执行了有两个操作数的 IMUL 操作后,必须检查这些标志位中的一个。

三操作数格式

32 位模式下的三操作数格式将乘积保存在第一个操作数中。第二个操作数可以是 16 位寄存器或内存操作数,它与第三个操作数相乘,该操作数是一个8位或16 位立即数:

IMUL regl6, reg/meml6,imm8
IMUL regl6, reg/meml6, iirrnl6

而 32 位寄存器或内存操作数可以与 8 位或 32 位立即数相乘:

IMUL reg32, reg/mem32, imm8
IMUL reg32, reg/mem32, imm32

IMUL 执行时,若乘积有效位被丢弃,则溢出标志位和进位标志位置 1。因此,在执行了有三个操作数的 IMUL 操作后,必须检查这些标志位中的一个。

在 64 位模式下执行 IMUL

在 64 位模式下,IMUL 指令可以使用 64 位操作数。在单操作数格式中,64 位寄存器或内存操作数与 RAX 相乘,产生一个 128 位且符号扩展的乘积存放到 RDX:RAX 寄存器中。在下面的例子中,RBX 与 RAX 相乘,产生 128 位的乘积 -16。

mov rax, -4
mov rbx, 4
imul rbx          ; RDX = 0FFFFFFFFFFFFFFFFh, RAX = -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 指令也可以用于无符号乘法。但是这种做法也有一点不便的地方:进位标志位和溢出标志位将无法表示乘积的高半部分是否为零。

IMUL 示例

下述指令执行 48 乘以 4,乘积 +192 保存在 AX 中。虽然乘积是正确的,但是 AH 不是 AL 的符号扩展,因此溢出标志位置 1:

mov al,48
mov bl, 4
imul bl             ; AX = 00C0h, OF = 1

下述指令执行 -4 乘以 4,乘积 -16 保存在 AX 中。AH 是 AL 的符号扩展,因此溢出标志位清零:

mov al, -4
mov bl, 4
imul bl             ; AX = FFF0h, OF = 0

下述指令执行 48 乘以 4,乘积 +192 保存在 DX:AX 中。DX 是 AX 的符号扩展,因此溢出标志位清零:

mov ax, 48
mov bx, 4
imul bx             ; DX:AX = 000000C0h, OF = 0

下述指令执行 32 位有符号乘法 (4 823 424*-423),乘积 -2 040 308 352 保存在 EDX:EAX 中。溢出标志位清零,因为 EDX 是 EAX 的符号扩展:

mov eax, +4823424
mov ebx, -423
imul ebx         ; EDX:EAX = FFFFFFFF86635D80h, OF = 0

下述指令展示了双操作数格式:
.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 位目的操作数:

mov ax, -32000
imul ax, 2            ; OF = 1

下面的指令展示的是三操作数格式,包括了有符号溢出的例子:
.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   ; 有符号溢出!