簡體   English   中英

理解匯編語言的mul和imul指令的問題

[英]problem in understanding mul & imul instructions of Assembly language

我正在從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 這如何仍然有效?

問題4:我對表中的其余條目有問題。 兩個 n 位數字(n = 8/16/32 位)的最壞情況相乘結果為 2n 位。 它如何將兩個 16/32 位乘法結果的結果存儲在相同大小的寄存器中?

Q1/Q2:x86 指令集保持其 16 位歷史。 進行 16 位乘法時,結果存儲在 DX:AX 中。 這就是它的方式,因為這就是它在 16 位土地上的方式。

Q3:如果您嘗試計算大於 2^16 的數字的平方,您展示的代碼有一個錯誤,因為該代碼忽略了存儲在edx中的結果的高 32 位。

Q4:我認為你可能誤讀了表格。 8 位乘法存儲在 16 位結果中; 16 位乘法存儲在 32 位結果中; 32 位乘法存儲在 64 位結果中。 你具體指的是哪條線?

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 這使得它更靈活,更容易使用。 在這個變體中,您可以自由選擇任意 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

Q1/Q2:為什么是 DX:AX? 為什么它不能存儲在 EAX / EDX 中?

就像其他人說的那樣,這只是為了向后兼容 原來(i)mul指令是從16位x86其來意的32位x86指令集出現過,所以他們不能把結果存儲到EAX / EDX因為沒有E-寄存器

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用於有符號和無符號乘法,因為

  • 這兩種情況的低位總是相同,並且在 C 中將兩個變量相乘會生成相同大小的結果( int x intint , long x longlong ...),這很好地適合imul的操作數。 強制編譯器發出單操作數mulimul的唯一方法是使用兩倍於寄存器大小的類型
  • 看到乘法結果比寄存器大小更寬的情況非常罕見,例如int64_t a; __int128_t p = (__int128_t)a * b; int64_t a; __int128_t p = (__int128_t)a * b; 所以很少需要單操作數(i)mul
  • 僅計算較低位將比獲得整個結果更快。
  • 由於各種形式的imul指令,使用起來更加靈活
    • 在 2-operand 形式中,您不需要保存/恢復 EDX 和 EAX
    • 3 操作數形式進一步允許您進行非破壞性乘法
  • 現代 CPU 通常針對imul的多操作數版本進行imul (因為現在現代編譯器幾乎只將多操作數imul用於有符號和無符號乘法),因此它們將比單操作數(i)mul更快

Q1/Q2:我認為原因是歷史性的。 在 32 位成為選項之前,沒有 eax 或 edx。 添加了 32 位功能以實現反向兼容。

Q3:低位將在 eax 中。 除非高位溢出,否則這些是您唯一關心的。

Q4:絕對是一張奇怪的桌子。 我想你明白了。

A1: mul最初出現在 8086/8088/80186/80286 處理器上,它們沒有 E**(E 表示擴展,即 32 位)寄存器。

A2:見 A1。

由於我作為匯編語言程序員的工作在那些 32 位英特爾變得司空見慣之前轉移到摩托羅拉 680x0 系列,我會就此打住:-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM