我有代码示例,用于在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的登记册:
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
我的问题是:
提前致谢。
答案 0 :(得分:3)
给一个男人一条鱼,等等等等......
很好,你有一个代码示例。但是你了解算法吗?
好的,让我们一步一步地看一个简单的例子:在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:
这是Long Multiplication algorithm
12h = 00010010
x
34h = 01110100
--------
00000000
01110100
00000000
00000000
01110100
00000000
00000000
00000000
两次加入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
干杯;)
答案 1 :(得分:1)
解决方案如果产品大于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;
这适用于两个无符号整数。
如果要为16位无符号整数乘以32位无符号整数,可以使用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;
答案 2 :(得分:1)
为了便于记录,8086的mul
instruction使其变得更容易(在具有快速mul
的较新CPU上更加高效)。在原始8086上,它确实很慢,但是在所有CPU上运行32次RCL高精度移位循环会非常麻烦!此版本的静态代码大小较小,很好。
您只需要三个mul
指令即可获得低*低,低*高和高*低乘积。 (如果要获得完整的64位结果,则另外一个代表高*高乘积。)
8086缺少了高效的imul reg, reg
格式,该格式不需要DX:AX作为隐式输出,并且不会将高半部分放在任何地方。因此,不幸的是,与32位模式下64x64 => 64乘法的编译器相比,我们需要更多的寄存器改组。但是,这是完全相同的问题。 (请参见https://godbolt.org/z/ozSkt_)
x_lo
,x_hi
,y_lo
和y_hi
相对于bp
可以是本地或函数args或标签。或者其中一些可能在该函数不使用的寄存器中,如果您更改了语法以使它们不寻址模式。
;; 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中。
请注意,我们不保存任何lo * hi产品的上半部分DX结果,因为我们只需要32位结果,而不是完整的32x32 => 64位结果。这些产品的低16位加到了我们最终32位产品的上半部分。 (而且我们不需要从它们进位到64位结果的最高16位字中,因此我们可以在最后一个mul之前添加它们。)
16 * 32 => 32位乘法将更加容易,只需两个mul
和一个add
(加上一堆mov
即可将数据放入正确的位置) 。例如,请参见执行此操作的阶乘循环:multiply two consecutive times in assembly language program(该答案还显示了扩展精度乘法数学的工作方式,就像您为纸张和铅笔算法添加项以对多个十进制数字进行乘法一样。 )