我正在从PC Assembly by paul caurter
学习80386 mul source
- 如果操作数是字节大小,则乘以AL中的字节 注册并存储结果 AX的16位。
细
- 如果源是16位,则乘以AX中的字和 32位结果存储在DX:AX。
中
Q1:为什么DX:AX?为什么它不能存储在EAX / EDX中?
imul
实在令人困惑
imul dest, source1
imul dest, source1, source2
我在理解表格时遇到了问题。
Q2:在表格的第二个条目中。再次,为什么DX:AX。为什么不是EAX或EDX?
现在考虑以下代码段:
imul eax ; edx:eax = eax * eax
mov ebx, eax ; save answer in ebx
mov eax, square_msg ; square_msg db "Square of input is ", 0
call print_string ; prints the string eax
mov eax, ebx
call print_int ; prints the int stored in eax
call print_nl ; prints new line
问题3:它预先说The notation EDX:EAX means to think of the EDX and EAX registers as one 64 bit register with the upper
32 bits in EDX and the lower bits in EAX.
所以答案也存储在edx中,对吧?在上面的代码中,我们没有考虑任何EDX,我们只是指EAX
这怎么还能用呢?
问题4:我对表中所有条目的其余部分都有疑问。两个n位数(n = 8/16/32位)的最坏情况乘法结果是2n位。为什么它存储两个16/32位乘法的结果本身会产生相同大小的寄存器?
答案 0 :(得分:7)
Q1 / Q2:x86指令集保持其16位历史记录。进行16位乘法时,答案存储在DX:AX中。这就是它的方式,因为它就是16位的原因。
问题3:如果您尝试计算大于2 ^ 16的数字的平方,则显示的代码存在错误,因为代码忽略了edx
中存储的结果的高32位。
答案 1 :(得分:6)
imul指令有很多不同的变体.2位代码。
你偶然发现的变种是16位乘法。它将AX寄存器与您传递给imul的参数相乘,并将结果存储在DX:AX。
中一个32位变体的工作方式与16位乘法类似,但将寄存器写入EDX:EAX。要使用此变体,您所要做的就是使用32位参数。
E.g:
; a 16 bit multiplication:
mov ax, [factor1]
mov bx, [factor2]
imul bx ; 32-bit result in DX:AX
; or imul word [factor2]
; a 32 bit multiplication:
mov eax, [factor1]
mov ebx, [factor2]
imul ebx ; 64-bit result in EDX:EAX
在386或更高版本中,您还可以在两个操作数形式中编写imul
。这使得它更灵活,更易于使用。在此变体中,您可以自由选择任意2个寄存器作为源和目标,并且CPU不会浪费时间在任何地方写入高半结果。并且不会破坏EDX。
mov ecx, [factor1]
imul ecx, [factor2] ; result in ecx, no other registers affected
imul ecx, ecx ; and square the result
或者对于签名的16位输入,以匹配您的imul
。 (将movzx用于无符号输入)
movsx ecx, word [factor1]
movsx eax, word [factor2] ; sign-extend inputs to 32-bit
imul eax, ecx ; 32-bit multiply, result in EAX
imul eax, eax ; and square the result
imul的这个变体是introduced with 386,并且有16位和32位操作数大小。 (64位模式下的64位操作数大小)。
在32位代码中,您始终可以假设386条指令(如imul reg, reg/mem
)可用,但如果您不关心较旧的CPU,则可以在16位代码中使用它。
imul cx, bx, 123 ; requires 286
imul ecx, ebx, 123 ; requires 386
答案 2 :(得分:3)
Q1 / Q2:为什么选择DX:AX?为什么它不能存储在EAX / EDX中?
像其他人所说,这只是为了向后兼容性。原始的(i)mul
指令来自16位x86,在32位x86指令集出现之前已经长,因此它们无法将结果存储到eax / edx中,因为< strong>没有电子注册。
Q3:在上面的代码中我们没有考虑任何EDX,我们只是指EAX如何仍然有效?
您输入的小值不会导致结果溢出,因此您没有看到差异。如果使用足够大的值(> = 16位),您将看到EDX!= 0并且打印结果将不正确。
Q4:为什么将两个16/32位乘法的结果存储在相同大小的寄存器本身?
结果不是与操作数的大小相同。 Multiplying two n-bit values always produces a 2n-bit value。但是在imul r16, r/m16[, imm8/16]
及其32/64位对应物中,高n位结果被丢弃。当你只需要结果的低16/32/64位(即non-widening multiplication),或者你可以确保结果不会溢出时,就会使用它们。
- 双操作数形式 - 使用此形式,目标操作数(第一个操作数)乘以源操作数(第二个操作数)。目标操作数是通用寄存器,源操作数是立即数,通用寄存器或存储单元。 中间产品(输入操作数大小的两倍)被截断并存储在目标操作数位置。
- [...对于三操作数形式相同]
现代编译器现在几乎只使用多操作数imul
进行有符号和无符号乘法,因为
int
x int
→int
,long
x long
→long
...)很适合imul
的操作数。强制编译器发出单操作数mul
或imul
的唯一方法是using a type twice the register size int64_t a; __int128_t p = (__int128_t)a * b;
这样的寄存器大小更宽,这是非常罕见的,因此很少需要单操作数(i)mul
imul
指令,使用的灵活性更高
imul
(因为现代编译器现在几乎只使用多操作数imul
进行有符号和无符号乘法)所以they'll be faster than single-operand (i)mul
答案 3 :(得分:1)
Q3:低阶位将在eax中。除非高位溢出,否则这些是你唯一关心的。
第四季:肯定是一张奇怪的桌子。我想你明白了。答案 4 :(得分:1)
A1: mul
最初出现在8086/8088/80186/80286处理器上,它没有E **(扩展为E,即32位)寄存器。
A2:参见A1。
当我作为汇编语言程序员的工作转移到摩托罗拉680x0家族之前,那些32位的英特尔变得司空见惯,我会停在那里: - )