我现在正在学习汇编,我完全不知道如何使用符号扩展。据我所知,如果您在 AX 中有一个值并执行 MUL 值,它将将该值乘以 AX,结果在 DX:AX 中,但如何访问和使用上面的部分?这对我来说毫无意义。
例如,在没有 32 位寄存器的 8086 上,您可以使用
add ax, si
/ adc dx, di
来执行 DX:AX += DI:SI
进行 32 位加法。这就是处理大于最宽寄存器的整数的方法。
但通常,当单个寄存器足够宽时,您首先不希望您的号码在多个寄存器之间分开。 由于您正在编写 32 位代码,因此首先不要通过不使用
mul r/m16
来创建此 DX:AX 拆分问题。
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 不使用部分寄存器?)
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
的情况下,我们必须将每个数字转换为其绝对值,执行无符号乘法,如果乘数恰好有一个为负,则将无符号乘积转换为其负值。; 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 在他的评论中建议的那样。