ND Arm9
主要问题是找到如何获得低 32 位和高 32 位。我真的很感激任何帮助或提示,这样我就可以继续做这个功课。
在这种情况下,您的问题与编程语言几乎没有关系,因此 asm、C、Pascal、Python 无关紧要(除非您只对快捷方式感兴趣,否则可能对高级语言感兴趣)。
所以,回到小学数学。两个数相乘:
12
* 34
====
8 (4*2)
40 (4*10)
60 (30*2)
+300 (30*10)
====
408
但是,如果我们的计算机对它可以操作的位数(位)有限制怎么办。例如,如果我们的硬件可以将两个 8 位数字相乘,在计算器上或在您的脑海中非常快,0xFF 是最大的无符号数,它乘以 0xFF * 0xFF = 0xFE01,只是为了检查十进制以防出现符号问题用计算器 255 * 255 = 65025 = 0xFE01。如果你没有看到模式,这里还有两个,一个两位乘法,最大的数字是 3, 3*3 = 9, 0b11 * 0b11 = 0b1001。两位输入,需要四位输出才能完全涵盖可能性,8 位操作数需要 16 位结果才能完全涵盖可能性。如果我错了,请纠正我,但如果您的处理器完全有乘法,并且结果不是输入大小的两倍,那么您必须做什么?可以说,您只能通过以下几种方式之一使用该硬件,如果存在溢出标志,请尝试乘法然后解决该问题(此答案的全部目的是如何解决该问题),否则您可以接受该结果。因此,如果您有一个 32 位 * 32 位 = 32 位,那么只有在用作 16 位 * 16 位 = 32 位时才可靠,方法是确保您的输入的高位为零,或者如果您对此更加思考并且意识到一个数字可以比 16 多几个有效位,只要另一个数字比 16 少几个。
所以使用我开始使用的十进制数字,但说明我们只能在乘法运算的输入上取一位数字,并得到两位数的结果,我们只能在我们的加法中取两位数并得到两位数的结果我们如何执行 12*34?
简单我们看中间的东西
8 (4*2) 40 (4*10) 60 (30*2) +300 (30*10)
和/或我们做一点基础数学
12*34 =
((1*10)+(2*1))*((3*10)+(4*1)) =
((1*10)*(3*10)) + ((1*10)*(4*1)) + ((2*1)*(3*10)) + ((2*1)*(4*1)) =
((1*3)*(10*10)) * ((1*4)*(10*1)) + ((2*3)*(1*10)) + ((2*4)*(1*1))
如果您还没有看到答案,请继续阅读。
如果每个数字都是变量 abcd,那么我们将把数字表示为 ab*cd 而不是十进制的 12*34
ab*cd =
((a*c)*(10*10)) + ((a*d)*(10*1)) + ((b*c)*(1*10)) + ((b*d)*(1*1))
还没看?我们现在可以使用一个数字输入,两个数字输出的规则来执行单个数字 a*c、a*d、b*c、b*d。乘以 100 或 10 或 1 只是移动。
((a*c)*(10*10)) = a*c * 100 = (a*c) << 2
((a*d)*(10*1)) = a*d * 10 = (a*d) << 1
((b*c)*(1*10)) = b*c * 10 = (b*c) << 1
((b*d)*(1*1)) = b*d * 1 = (b*d) << 0
现在,如果我们声明它不是一个十进制数字,而是十六进制数字,我们的乘法器可以做 4 位进 8 位输出。我们的加法器可以做 8 位进 8 位输出。
((a*c)*(0x10*0x10)) = a*c * 0x100 = (a*c) << 8
((a*d)*(0x10*0x1)) = a*d * 0x10 = (a*d) << 4
((b*c)*(0x1*0x10)) = b*c * 0x10 = (b*c) << 4
((b*d)*(0x1*0x1)) = b*d * 0x1 = (b*d) << 0
因此,如果我在这个系统中有两个 8 位数字,我需要将它们干净地相乘,并且指令集必须提供的最好的指令集是 4 位或 8 位输入乘法运算,结果是 8 位输出,加法是 8 位输入和 8 位输出。将我的两个 8 位数字 jk * mn 相乘,其中 j,k,m,n 代表单独的十六进制(四位)数字
(temp0,1,2,3... 是 8 位寄存器/变量)
temp0 = j*m temp1 = j*n temp2 = k*m temp3 = k*n
temp4 = ((temp1<<4)&0xFF) + ((temp2<<4)&0xFF) 如果进位位被设置则 temp0 = temp0+1; temp4 = temp4 + temp3 如果设置了进位位,则 temp0 = temp0 + 1 temp0 = temp0 + (temp1>>4) temp0 = temp9 + (temp2>>4)
答案的高 8 位在 temp0 中,低 8 位在 temp4 中。
这适用于任意数量的位,32 位输入,32 位输出。如果您想使用 8 位机器将 1024 位 * 1024 位 = 2048 位数字相乘,则 4 位 * 4 位 = 8 位乘法器和 8 位 + 8 位 = 8 位加法器。然后你就可以轻松做到了。
不必做所有书面数学,然后就这样做
jk
* mn
====
nk (n*k<<0)
nj (n*k<<1)
mk (m*k<<1)
+mj (m*k<<2)
====
如果有帮助,用零填充
00nk
0nj0
0mk0
+mj00
====
然后将它们分开到我们的加法器的宽度
00 nk
0n j0
0m k0
+mj +00
== ==
简化一点
nk
j0
+k0
===
lower half of answer
0n
0m
mj
+carry_out from lower half
====
upper half of answer
一个从小学就出来的三位数
abc
* def
========
fc (fc = f times c)
fb0 (fb = f times b)
fa00
ec0
eb00
ea000
dc00
db000
+da0000
========
然后将加法拆分为您的加法操作可以消化的列......这将永远扩大您希望通过任何大小乘法操作的任何大小数字并添加您的处理器支持。如果您没有乘法运算,那么它仍然以完全相同的方式工作,但是您的乘法器的一侧(您没有)的位宽为 1。
现在使用位:
abcd
*1011
=======
abcd (abcd * 0b0001)
abcd0 (abcd * 0b0010)
000000 (abcd * 0b0000)
+abcd000 (abcd * 0b1000)
========
a,b,c,d 现在是位,而 1011 是一个四位数字,在二进制中,每个数字只能是 1 或 0,所以 0 乘以任何东西都是零,1 乘以任何东西本身,所以当一个数字与另一个数字相乘时您没有乘法运算只是意味着对于一个操作数中的每个非零位,您将另一个操作数移动该数量并将其添加到您的累积结果中。速度不快,但在几乎任何处理器上都非常容易实现,而且它同样可以无限扩展。
现在将所有这些应用于您的 32 位 * 32 位 = 64 位乘法,您需要在您使用的任何编程语言中使用的任何处理器上保留结果的上半部分和下半部分。
一些 ARM 处理器(不确定 ARM 9)有一个 32x32 -> 64 位乘法指令,名为“UMULL”(无符号数)或“SMULL”(有符号数)。
结果存放在两个寄存器中;一个寄存器将包含低 32 位,另一个将包含高 32 位,因此您甚至不必将低位和高位分开!
如果您的处理器没有“UMULL”指令,则您必须执行“dwelch”在他的回答中写的内容。