[英]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 位兩個數字,我知道我需要更新,例如:
AX
更改為00000002h
並將BX
更改為00000008h
。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
我的問題是:
提前致謝。
給一個人一條魚,等等等等……
很好,你有一個代碼示例。 但是你懂算法嗎?
好的,讓我們通過一個簡化的例子一步一步地完成它:將AL
和AH
兩個 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*low
、 low*high
和high*low
產品。 (如果你想要完整的 64 位結果,另一個high*high
產品)。
8086 缺少高效的imul reg, reg
形式,不需要 DX:AX 作為隱式輸出,並且不會浪費時間將高半部分放在任何地方。 所以不幸的是,與編譯器在 32 位模式下的 64x64 => 64 乘法相比,我們需要更多的寄存器改組,但除此之外,這是完全相同的問題。 (見https://godbolt.org/z/ozSkt_ )
x_lo
、 x_hi
、 y_lo
和y_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 之前添加它們。)
adc
如何傳播結果 16 * 32 => 32 位乘法會更容易,只需兩個mul
和一個add
(加上一堆mov
以將數據放入正確的位置)。 例如,請參閱執行此操作的階乘循環: 在匯編語言程序中連續兩次相乘(該答案還顯示了擴展精度乘法數學的工作原理,與您為紙筆算法添加項以對多個數字進行乘法的方式相同十進制數字。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.