10

我正在从Paul caurter 的 PC Assembly 中学习 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

Q3:之前说的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 这仍然有效吗?

Q4:我对表中所有条目的其余部分有疑问。两个 n 位数字(n = 8/16/32 位)的最坏情况相乘结果为 2n 位。它如何将两个 16/32 位乘法结果存储在相同大小的寄存器中?

4

5 回答 5

7

Q1/Q2:x86 指令集保持其 16 位历史。进行 16 位乘法时,答案存储在 DX:AX 中。就是这样,因为在 16 位土地上就是这样。

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

Q4:我认为您可能误读了表格。8 位乘法存储在 16 位结果中;16 位乘法存储在 32 位结果中;32 位乘法存储在 64 位结果中。你具体指的是哪条线?

于 2009-12-22T17:56:38.247 回答
7

imul 指令有很多不同的变体。

您偶然发现的变体是 16 位乘法。它将 AX 寄存器与您作为参数传递给 imul 的任何内容相乘,并将结果存储在 DX:AX 中。

一种 32 位变体的工作方式类似于 16 位乘法,但将寄存器写入 EDX:EAX。要使用此变体,您所要做的就是使用 32 位源操作数。

例如:

  ; 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在两个操作数形式中编写 an。这使它更加灵活和易于使用。在这个变体中,您可以自由选择任意 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 的这个变体是随 386 引入的,并且有 16 位和 32 位操作数大小。(以及 64 位模式下的 64 位操作数大小)。

在 32 位代码中,您始终可以假设 386 条指令imul reg, reg/mem可用,但如果您不关心较旧的 CPU,您可以在 16 位代码中使用它。

186 引入了一个 3 操作数立即数形式。

imul  cx, bx, 123        ; requires 186

imul  ecx, ebx, 123      ; requires 386
于 2009-12-22T18:03:13.413 回答
6

Q1/Q2:为什么选择 DX:AX?为什么不能存储在 EAX / EDX 中?

就像其他人说的那样,这只是为了向后兼容。原始(i)mul指令来自 16 位 x86,它早32 位 x86 指令集出现之前就已经出现,因此他们无法将结果存储到 eax/edx,因为没有 E-register

Q3:在上面的代码中我们没有考虑任何 EDX 我们只是指 EAX 这仍然有效吗?

您输入了不会导致结果溢出的小值,因此您看不到差异。如果您使用足够大的值(>= 16 位),您将看到 EDX != 0 并且打印结果将不正确。

Q4:它怎么把两个16/32位相乘的结果存储到本身大小相同的寄存器中?

并不是结果仍然与操作数相同。将两个 n 位值相乘始终会产生 2n 位值。但是在imul r16, r/m16[, imm8/16]和它们的 32/64 位对应物中,高 n 位结果被丢弃。当您只需要结果的低 16/32/64 位(即非扩展乘法),或者当您可以确保结果不会溢出时使用它们。

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

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

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

于 2013-11-05T07:06:01.540 回答
1

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

Q3:低位将在 eax 中。这些是您唯一关心的,除非溢出到高位。

Q4:绝对是一张奇怪的桌子。我想你明白了。

于 2009-12-22T17:59:43.863 回答
1

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

A2:见 A1。

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

于 2009-12-22T18:00:43.080 回答