簡體   English   中英

在 8086 微處理器上將 32 位兩個數相乘

[英]Multiplying 32 bit two numbers on 8086 microprocessor

我有代碼示例,用於在 8086 上將兩個 16 位數字相乘並嘗試將其更新為兩個 32 位數字相乘。

start:
 MOV AX,0002h ; 16 bit multiplicand
 MOV BX,0008h ; 16 bit multiplier
 MOV DX,0000h ; high 16 bits of multiplication
 MOV CX,0000h ; low 16 bits of multiplication
 MOV SI,10h ; loop for 16 times

LOOP:
 MOV DI,AX
 AND DI,01h
 XOR DI,01h
 JZ ADD
CONT:
 RCR DX,1
 RCR CX,1
 SHR AX,1
 DEC SI
 CMP SI,0
 JNZ LOOP
 JMP END ; ignore here, it's not about multiplication. 
ADD:
 ADD DX,BX
 JMP CONT

上面的代碼語句將兩個 16 位數字相乘。

要將其更新為 32 位兩個數字,我知道我需要更新,例如:

  1. AX更改為00000002h並將BX更改為00000008h
  2. 使用另外兩個寄存器(我不知道我應該使用哪個寄存器)來保存第二和第三個 16 位乘法(因為乘法將是 64 位。4 次 16 位。我目前有 DX 和 CX。)
  3. 將循環編號更新為20h (在這種情況下為SI )(對於 32 位編號,這是 32 次)

8086 是 16 位微處理器,所以它的寄存器也是。 我無法為寄存器分配 32 位長的數字。

8086的寄存器:

REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.
SREG: DS, ES, SS, and only as second operand: CS.

資料來源: http : //www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html

我的問題是:

  1. 如何為一個 32 位數字處理兩個不同的寄存器。 (寄存器是 16 位的,所以我必須把數字分成兩個寄存器)
  2. 我可以為此使用哪些寄存器? 我可以隨意使用任何寄存器嗎?

提前致謝。

給一個人一條魚,等等等等……

很好,你有一個代碼示例。 但是你懂算法嗎?

好的,讓我們通過一個簡化的例子一步一步地完成它:將ALAH兩個 8 位寄存器相乘,並將結果存儲在DX

順便說一句,你可以使用任何你喜歡的寄存器,除非這個或那個指令需要任何特定的寄存器。 例如, SHL reg, CL

但在我們真正開始之前,您提供的算法有一些優化。 你知道,組裝就是優化。 無論是速度還是尺寸。 否則你會在 C# 或 smth 中做膨脹軟件。 別的。

MOV DI,AX
AND DI,01h
XOR DI,01h
JZ ADD

這部分所做的只是檢查AX的第一位(位 #0)是否已設置。 你可以簡單地做

TEST AX, 1
JNZ ADD

但是您只需要測試一位,因此TEST AL, 1而不是TEST AX, 1可以為您節省一個字節。

下一個,

RCR DX,1

不需要輪換,所以它可以簡單地為SHR DX, 1 但是這兩條指令的執行時間相同,並且都是兩個字節長,因此在本例中無關緊要。

下一個,

DEC SI
CMP SI,0
JNZ LOOP

永遠不要在DEC之后與零進行比較。 是動靜! 簡單地做

DEC SI
JNZ LOOP

接下來,不必要的循環拆分

JZ ADD
CONT:
. . .
JMP END
ADD:
ADD DX, BX
JMP CONT
END:
. . .

應該

JNZ CONT
ADD DX, BX
CONT:
. . .
END:
. . .

在這里,我們使用了一些優化的例程:

LOOP:
 TEST AL, 1
 JZ SHORT CONT
 ADD DX, BX
CONT:
 RCR DX, 1
 RCR CX, 1
 SHR AX, 1
 DEC SI
 JNZ LOOP
END:

就是這樣。 現在回到(或向前?)這段代碼實際上做了什么。 以下代碼示例完全模仿您的示例,但適用於 8 位寄存器。

 MOV AL,12h   ; 8 bit multiplicand
 MOV AH,34h   ; 8 bit multiplier
 XOR DX, DX   ; result
 MOV CX, 8    ; loop for 8 times

LOOP:
 TEST AL, 1
 JZ SHORT CONT
 ADD DH, AH
CONT:
 SHR DX, 1
 SHR AL, 1
 DEC CX
 JNZ LOOP
END:

這是一個長乘法算法

 12h = 00010010
               x
 34h = 01110100
       --------
       00000000
      01110100
     00000000
    00000000
   01110100
  00000000
 00000000
00000000

將 shift 34h 添加兩次:

0000000011101000
+
0000011101000000
----------------
0000011110101000 = 03A8

就是這樣! 現在要使用更多數字,您可以使用相同的方法。 下面是 fasm 語法的實現。 結果存儲在DX:CX:BX:AX

Num1    dd 0x12345678
Num2    dd 0x9abcdef0

 mov si, word [Num1]
 mov di, word [Num1 + 2]
 xor ax, ax
 xor bx, bx
 xor cx, cx
 xor dx, dx
 mov bp, 32

_loop:
 test si, 1
 jz short _cont
 add cx, word [Num2]
 adc dx, word [Num2 + 2]
_cont:
 rcr dx, 1
 rcr cx, 1
 rcr bx, 1
 rcr ax, 1
 rcr di, 1
 rcr si, 1
 dec bp
 jnz short _loop

干杯;)

解決方案 n. 如果產品大於 32 位,則 2 似乎不起作用。 此外,移位指令是錯誤的。 此解決方案正常工作:

Procedure _PosLongIMul2; Assembler;

{INPUT:

 DX:AX-> First factor (destroyed).
 BX:CX-> Second factor (destroyed).

 OUTPUT:

 BX:CX:DX:AX-> Multiplication result.

 TEMP:

 BP, Di, Si}

Asm

     Jmp   @Go

 @VR:DD    0      {COPY of RESULT     (LOW)}
     DD    0      {COPY of RESULT    (HIGH)}

 @Go:Push  BP

     Mov   BP,20H {32 Bit Op.}

     XOr   DI,DI  {COPY of first op.  (LOW)}
     XOr   SI,SI  {COPY of first op. (HIGH)}

     Mov   [CS:OffSet @VR  ],Word(0)
     Mov   [CS:OffSet @VR+2],Word(0)
     Mov   [CS:OffSet @VR+4],Word(0)
     Mov   [CS:OffSet @VR+6],Word(0)

 @01:ShR   BX,1
     RCR   CX,1

     JAE   @00

     Add   [CS:OffSet @VR  ],AX
     AdC   [CS:OffSet @VR+2],DX
     AdC   [CS:OffSet @VR+4],DI
     AdC   [CS:OffSet @VR+6],SI

 @00:ShL   AX,1
     RCL   DX,1
     RCL   DI,1
     RCL   SI,1

     Dec   BP
     JNE   @01

     Mov   AX,[CS:OffSet @VR]
     Mov   DX,[CS:OffSet @VR+2]
     Mov   CX,[CS:OffSet @VR+4]
     Mov   BX,[CS:OffSet @VR+6]

     Pop   BP

End;

這適用於兩個無符號整數。

如果要將 32 位無符號整數乘以 16 位無符號整數,可以使用 Mul 指令如下:

Function Mul32Bit(M1:LongInt;M2:Word):LongInt; Assembler;

Asm
 LEA   SI,M1
 Mov   AX,[SS:SI]
 Mov   CX,[SS:SI+2]
{CX:AX contains number to multiply by}
 Mov   BX,M2
{BX contains number that multiply}
 Mul   BX
 XChG  AX,CX
 Mov   SI,DX
 Mul   BX
 Add   AX,SI
 AdC   DX,0
{DX:AX:CX contains the result of multiplication}
 Mov   DX,AX
 Mov   AX,CX
{DX:AX contains the partial result of m. and is the function's result}
End;

作為記錄,8086 有一個mul指令,使這更容易(並且在具有快速mul后來的 CPU 上更有效)。 在最初的 8086 上它真的很慢,但是在所有 CPU 上運行 32 次 RCL 多精度移位循環會很糟糕! 這個版本有更少的靜態代碼大小,這很好。

您只需要三個mul指令即可獲得low*lowlow*highhigh*low產品。 (如果你想要完整的 64 位結果,另一個high*high產品)。

8086 缺少高效的imul reg, reg形式,不需要 DX:AX 作為隱式輸出,並且不會浪費時間將高半部分放在任何地方。 所以不幸的是,與編譯器在 32 位模式下的 64x64 => 64 乘法相比,我們需要更多的寄存器改組,但除此之外,這是完全相同的問題。 (見https://godbolt.org/z/ozSkt_

x_lox_hiy_loy_hi可以是相對於bp作為y_hi或函數參數或標簽的內存。 或者其中一些可能位於此函數不使用的寄存器中,如果您更改語法使它們不是尋址模式。

;; untested
;; inputs: uint32_t x, y in memory
;; clobbers: CX, SI, DI

    mov     ax, [y_lo]
    mov     cx, ax
    mul     word ptr [x_hi]
    mov     si, ax            ; save  y_lo * x_hi

    mov     ax, [x_lo]
    mov     di, ax
    mul     word ptr [y_hi]
    add     si, ax            ; sum of the cross products

    mov     ax, di
    mul     cx                ; DX:AX = y_lo * x_lo
    add     dx, si            ; add the cross products into the high half
;; Result: uint32_t DX:AX = X * Y

要使用更少的 tmp 寄存器,您只需從內存中重新加載 x_lo 和 y_lo 兩次,而不是將它們保存在 DI 和 CX 中。

(相關: 64x64 => 32 位模式下的 64 位乘法,這與不同操作數大小的問題相同,從寄存器中的所有 4 個值開始,並使用xchg來管理事物。)

請注意,我們不保存lo * hi乘積的上半部分 DX 結果,因為我們只想要 32 位結果,而不是完整的 32x32 => 64 位結果。 這些產品的低 16 位添加到我們最終的 32 位產品的上半部分。 (而且我們不需要從它們進位到 64 位結果的最高 16 位字,所以我們可以在最后一個 mul 之前添加它們。)

16 * 32 => 32 位乘法會更容易,只需兩個mul和一個add (加上一堆mov以將數據放入正確的位置)。 例如,請參閱執行此操作的階乘循環: 在匯編語言程序中連續兩次相乘(該答案還顯示了擴展精度乘法數學的工作原理,與您為紙筆算法添加項以對多個數字進行乘法的方式相同十進制數字。)

暫無
暫無

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

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