如何使用乘法的符号扩展?

问题描述 投票:0回答:2

我现在正在学习汇编,我完全不知道如何使用符号扩展。据我所知,如果您在 AX 中有一个值并执行 MUL 值,它将将该值乘以 AX,结果在 DX:AX 中,但如何访问和使用上面的部分?这对我来说毫无意义。

assembly x86 masm irvine32
2个回答
2
投票

例如,在没有 32 位寄存器的 8086 上,您可以使用

add ax, si
/
adc dx, di
来执行
DX:AX += DI:SI
进行 32 位加法。这就是处理大于最宽寄存器的整数的方法。

但通常,当单个寄存器足够宽时,您首先不希望您的号码在多个寄存器之间分开。 由于您正在编写 32 位代码,因此首先不要通过不使用

mul r/m16
来创建此 DX:AX 拆分问题。

相反,只需使用 32 位操作数大小作为

imul r, r/m
或 3 操作数立即数形式,就像编译器那样。 (零扩展或符号扩展窄输入到所需的目标宽度)。

那么你甚至不会被迫使用 EDX:EAX,并且当你只想要 32x32 => 32 位乘法而不是 32x32 => 64 位时,你不会无缘无故地破坏 EDX。 (只有全乘的高半部分取决于符号,这就是为什么只有 imul,而不是其他形式的无符号 mul)。

例如:

  movsx   edx, word ptr [esp+4]    ; sign-extend a signed 16-bit stack arg
  movzx   ecx, word ptr [esp+8]    ; zero-extend an unsigned 16-bit arg to 32-bit
  imul    edx, ecx                 ; edx = i16 * (int)u16
  imul    edx, [esp+12]            ; and multiply by a 32-bit stack arg, edx *= i32

  mov  eax, 123
  imul ecx,  eax, 456            ; ecx = eax*456, leaving EAX unmodified.

  add   edx, ecx

  lea   eax, [edx + edx*4 + 789]   ; eax = edx * 5 + 789 ; more math using the power of 32-bit addressing modes.

顺便说一句,正如 @Michael 在评论中指出的那样,您可以执行类似的操作将 DX:AX 合并到 EDX 中的单个 32 位 int 中。

   shl edx, 16
   mov dx, ax

如果您知道 EAX 的高 16 位为零,则可以使用

or edx, eax
而不是部分寄存器恶作剧。 为什么 GCC 不使用部分寄存器?


1
投票

16 位寄存器保存 0000h..FFFFh 范围内的无符号数字。 这些数字的的结果可能会溢出此范围一位,该位由CarryFlag介导。
例如 FFFFh + FFFFh = CF + FFFEh。

情况与乘法不同:它可能会溢出十六位,
例如 FFFFh * FFFFh = FFFE0001h。
这就是为什么使用另一个寄存器(硬连线为 DX)来保存结果的高 16 位。

即使是旧的16位处理器8086也可以只使用传统的16位寄存器来加|减32位数字,但每个操作都需要两条机器指令,并且必须考虑进位标志。 32 位数字的下半部分用

ADD|SUB
计算,上半部分用
ADC|SBB
计算。

我们来计算一下

(a*b)+(c*d)
,其中
a,b,c,d
分别是16位数字000Ah、000Bh、000Ch、000Dh。

MOV AX,000Ah ;
MOV CX,000Bh ; Let CX=000Bh
MUL CX       ; Let DX:AX= a * b = 0000:006Eh
MOV SI,AX  ; Save temporary result DX:AX
MOV DI,DX  ;   into another pair DI:SI.
MOV AX,000Ch
MOV CX,000Dh
MUL CX     ; Let DX:AX = c * d = 0000:009Ch
; Add the temporary result DI:SI to DX:AX.
ADD AX,SI  ; Let AX = 009Ch + 006Eh = 010Ah. CF = 0.
ADC DX,DI  ; Let DX = 0000h + 0000h + CF = 0. CF = 0.

(a*b)+(c*d)
的结果,即0000:010Ah,现在位于DX:AX,CF=0。

我们也可以将 16 位值视为 -32768..+32767 范围内的signed整数,即 8000h..7FFFh。 Intel CPU 8086 无法进行有符号数相乘,指令 IMUL 是在 80186 及更高版本中引入的。在没有

IMUL
的情况下,我们必须将每个数字转换为其绝对值,执行无符号乘法,如果乘数恰好有一个为负,则将无符号乘积转换为其负值。
负值与其绝对(正)值之间的转换由指令NEG提供。要对两个连接的 16 位寄存器中的 32 位数字求反,必须再次考虑进位标志:

; Negate DX:AX
NEG DX
NEG AX
SBB DX,0

或者,替代方案,效果较差

; Negate DX:AX
NOT AX
NOT DX
ADD AX,1
ADC DX,0

但是,过去三十年制造的所有 Intel/AMD 处理器都可以直接使用

IMUL
计算有符号乘法,因此您最好使用它,正如 Peter Cordes 在他的评论中建议的那样。

© www.soinside.com 2019 - 2024. All rights reserved.