繁体   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