理解mul& amp;汇编语言的imul指令

时间:2009-12-22 17:51:31

标签: assembly x86 nasm

我正在从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

alt text

我在理解表格时遇到了问题。

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位乘法的结果本身会产生相同大小的寄存器?

5 个答案:

答案 0 :(得分:7)

Q1 / Q2:x86指令集保持其16位历史记录。进行16位乘法时,答案存储在DX:AX中。这就是它的方式,因为它就是16位的原因。

问题3:如果您尝试计算大于2 ^ 16的数字的平方,则显示的代码存在错误,因为代码忽略了edx中存储的结果的高32位。

第四季:我想你可能误读了这张桌子。 8位乘法存储在16位结果中; 16位乘法存储在32位结果中; 32位乘法存储在64位结果中。你指的是哪一行?

答案 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位代码中使用它。

286引入了一个3操作数的直接形式。

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),或者你可以确保结果不会溢出时,就会使用它们。

  
      
  • 双操作数形式 - 使用此形式,目标操作数(第一个操作数)乘以源操作数(第二个操作数)。目标操作数是通用寄存器,源操作数是立即数,通用寄存器或存储单元。 中间产品(输入操作数大小的两倍)被截断并存储在目标操作数位置。
  •   
  • [...对于三操作数形式相同]
  •   
     

https://www.felixcloutier.com/x86/IMUL.html

现代编译器现在几乎只使用多操作数imul进行有符号和无符号乘法,因为

答案 3 :(得分:1)

Q1 / Q2:我认为原因是历史性的。在32位是一个选项之前,没有eax或edx。添加了32位功能以进行反向兼容。

Q3:低阶位将在eax中。除非高位溢出,否则这些是你唯一关心的。

第四季:肯定是一张奇怪的桌子。我想你明白了。

答案 4 :(得分:1)

A1: mul最初出现在8086/8088/80186/80286处理器上,它没有E **(扩展为E,即32位)寄存器。

A2:参见A1。

当我作为汇编语言程序员的工作转移到摩托罗拉680x0家族之前,那些32位的英特尔变得司空见惯,我会停在那里: - )