【发布时间】:2012-11-07 05:55:11
【问题描述】:
如何在汇编中将两个 32 位数字相乘或一个 32 位另一个 16 位相乘,有人知道算法吗?
data1 dw 32bit
data2 dw 32bit
mov ax,data2
Mul data1
【问题讨论】:
-
你想要一个实现内置
mul指令的算法吗?
标签: assembly x86-16 multiplication bigint
如何在汇编中将两个 32 位数字相乘或一个 32 位另一个 16 位相乘,有人知道算法吗?
data1 dw 32bit
data2 dw 32bit
mov ax,data2
Mul data1
【问题讨论】:
mul指令的算法吗?
标签: assembly x86-16 multiplication bigint
首先,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
代码可能如下所示(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
我们不需要在第二步([first+2] * [second])之后需要adc word [result+6], 0,因为它的高半部分最多为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
【讨论】:
ADC [result+6],00h
add/adc 工作。使用 memory-destination add 和 adc 只对最清楚地显示数学有意义,而不是在实际代码中实际应该做什么。 (但作为这个问题的答案,+1)
0xffff ^ 2 = 0xfffe0001 所以高半部分最多是0xfffe + CF
pmuludq 看看是否这是值得的,并且有助于缓解注册压力。)