在 8086 上将两个 32 位数字相乘得到一个 64 位数字(32x32 => 64 位与 16 位相乘)

Dea*_*ger -1 assembly multiplication bigint x86-16

我如何将汇编中的两个 32 位数字或一个 32 位与另一个 16 位相乘,有人知道算法吗?

data1 dw 32bit
data2 dw 32bit    
mov ax,data2
Mul data1
Run Code Online (Sandbox Code Playgroud)

Bre*_*dan 5

首先,dw用于创建一个 16 位(“字”)值。它不会保存 32 位值。您需要使用dd来存储 32 位“dword”,或使用一对 16 位值。

将一对 32 位值相乘,结果可以是 64 位(例如 0xFFFFFFFF * 0xFFFFFFFF = 0xFFFFFFFE00000001)。对于 8086(不仅仅是 80386 或更高版本的实模式代码),有一个 MUL 指令,但它仅限于乘以 2 个 16 位值(并获得 32 位结果)。这意味着您希望将每个 32 位值视为一对 16 位值。

如果A被拆分为A_low(第一个32位数字的最低16位)和A_high(第一个32位数字的最高16位),B按照同样的方式拆分为B_low和B_high;然后:

  A * B = A_low * B_low
          + ( A_high * B_low ) << 16
          + ( A_low * B_high ) << 16
          + ( A_high * B_high ) << 32
Run Code Online (Sandbox Code Playgroud)

代码可能如下所示(NASM 语法):

         section .data
first:   dw 0x5678, 0x1234  ;0x12345678
second:  dw 0xDEF0, 0x9ABC  ;0x9ABCDEF0
result:  dw 0, 0, 0, 0      ;0x0000000000000000
         section .text

    mov ax,[first]          ;ax = A_low
    mul word [second]       ;dx:ax = A_low * B_low
    mov [result],ax
    mov [result+2],dx       ;Result = A_low * B_low

    mov ax,[first+2]        ;ax = A_high
    mul word [second]       ;dx:ax = A_high * B_low
    add [result+2],ax
    adc [result+4],dx       ;Result = A_low * B_low
                                     ; + (A_high * B_low) << 16

    mov ax,[first]          ;ax = A_low
    mul word [second+2]     ;dx:ax = A_low * B_high
    add [result+2],ax
    adc [result+4],dx       ;Result = A_low * B_low
                                     ; + (A_high * B_low) << 16
                                     ; + (A_low * B_high) << 16
    adc word [result+6], 0   ; carry could propagate into the top chunk

    mov ax,[first+2]        ;ax = A_high
    mul word [second+2]     ;dx:ax = A_high * B_high
    add [result+4],ax
    adc [result+6],dx       ;Result = A_low * B_low
                                     ; + (A_high * B_low) << 16
                                     ; + (A_low * B_high) << 16
                                     ; + (A_high * B_high) << 32
Run Code Online (Sandbox Code Playgroud)

我们并不需要adc word [result+6], 0第二个步骤(后[first+2] * [second]),因为它的高半最多0xfffe[result+4]那时已经为零(因为此代码只能工作一次),因此adc [result+4],dx无法包装并产生进位。它最多可以生产0xffff

(它可以作为adc dx, 0/mov [result+4], dx来避免依赖于result已经归零的那部分。同样,adc进入归零寄存器可用于第一次写入[result+6],以使此代码无需先归零即可使用result。)


如果您实际上使用的是 80386 或更高版本,那么它会简单得多:

         section .data
first:   dd 0x12345678
second:  dd 0x9ABCDEF0
result:  dd 0, 0            ;0x0000000000000000
         section .text

    mov eax,[first]          ;eax = A
    mul dword [second]       ;edx:eax = A * B
    mov [result],eax
    mov [result+4],edx       ;Result = A_low * B_low
Run Code Online (Sandbox Code Playgroud)