在8086微处理器上乘以32位两位数

时间:2015-03-25 03:20:34

标签: assembly multiplication x86-16

我有代码示例,用于在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位.16位是4次。我目前有DX和CX。)
  3. 将循环次数更新为20h(在这种情况下为SI)(32位数为32次)
  4. 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. 我可以将哪些寄存器用于此目的?我可以随意使用任何注册吗?
    3. 提前致谢。

3 个答案:

答案 0 :(得分:3)

给一个男人一条鱼,等等等等......

很好,你有一个代码示例。但是你了解算法吗?

好的,让我们一步一步地看一个简单的例子:在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:

这是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_lox_hiy_loy_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(该答案还显示了扩展精度乘法数学的工作方式,就像您为纸张和铅笔算法添加项以对多个十进制数字进行乘法一样。 )