这个问题与汇编语言几乎没有关系,或者至少是这个问题的最大份额。您应该用 C 或任何您喜欢的语言对其进行原型制作,然后简单地将其转换为另一种语言(在这种情况下为汇编)。
如果你记得在小学时如果我们想加 999 + 1
999
1 +
======
我们将进行很多“随身携带”
111
999
1 +
======
1000
现在小学里可能没有定义的是“carry in”和“carry out”。您将 1 带到下一列的东西就是执行。当您将结转的东西添加到当前列中时,对于该列,现在将相同的数字称为进位。
ab
c
d +
====
e
b 是进位,a 是进位,操作数是 c 和 d。e 是该列的结果。我们确实在小学学到了我们可以添加无限大的数字,因为我们只需要学习一次添加一列并无限重复。请注意,第一列进位上方的原始问题是空白的,它是隐含的零。零加 9 加 1 等于“0 进位 1”,0 是 9+1 的结果,1 是进位。
所以现在做 999 + 1 但人为限制我们一次只能添加两列,也许我们的纸只够两个数字宽,谁知道(处理器有宽度有固定限制的寄存器,在您的案例 16 位或 16 列宽)。
我们知道答案看起来像这样,有四列,
1110
0999
0001 +
======
1000
一次两列
11 10
09 99
00 + 01 +
==== ====
10 00
首先将最不重要的列用零作为进位。然后将该使用作为下两列的进位执行,我们可以对无限大的数字无限重复此操作。
在汇编语言中,你经常(但不总是)有一个 ADD 和某种风格的 ADC 指令,不带进位的加法和带进位的加法,正常的加法有一个隐含的零进位,理想情况下有一个进位到一些进位标志处理器状态寄存器某处。那么如果有一个带进位的加法指令,你可以用带进位的加法进行其余的高阶加法,带进位的加法使用进位标志作为进位,并将进位放在进位标志中,所以你可以级联添加的内容。
清如泥?
减法,在计算机中没有真正的减法,你在初学者编程课上学到的疯狂的二进制补码是有原因的。使用二进制补码对数字求反是“反转并加 1”。现在考虑我们的加法运算,加法将两个数字相加并带有进位。如果您要反转第二个操作数,然后将 1 放入进位中怎么办?那是“反转并加1”,对吗?如果我想做与 5 + (-1) 相同的数学 5 - 1 与 5 + invert(1) + 1 相同,对吗?完全符合 ADD 逻辑。
不同的指令集对减法运算的进位执行不同的操作,基本上是在进入加法器的过程中,减法意味着反转第二个操作数并反转进位,然后进行正常的加法,尽管一些处理器系列反转如果运算是减法,则执行位,有些则不是。您可能需要也可能不需要根据您在操作后对进位位所做的操作(条件分支,例如如果进位设置跳转,如果进位未设置跳转),在这种情况下,如另一张海报所示,一些指令集合有借位减法。就像 add then 一系列 addc 操作级联一个无限大的加法一样,您可以使用 sub 和 sbb 级联一个无限大的减法。
相乘...幸运的是 8086 使这更容易一些,但整体性能在任何地方都是相同的。如果您查看数字(二进制),如果您将两个 x 位宽的数字相乘,理想情况下结果需要是 2*x 宽,因此要正确地将任何两个 16 位数字相乘,您需要一个 32 位的结果。如果您的硬件只能执行 16bit * 16bit = 16bit 那么轻松将该乘数级联为零高 8 位并假装执行 8bit * 8bit = 16bit ...
小学
abcd
efgh *
======
我们最终得到了四个相加的数字,对吗?我用 hhhh 代表 abcd*h 和 gggg 代表 abcd*g 等。
abcd
efgh *
=======
hhhh
gggg
ffff
eeee +
==========
当我们甚至做了两列乘法时
cd
gh *
====
分为四个乘法步骤
h * d
h * (c*10)
(g*10) * d
(g*10) * (d*10)
使用数学属性的是
h * d = h*d
h * (c*10) = 10 * (h*c)
(g*10) * d = 10 * (g*d)
(g*10) * (d*10) = 100 * (g*d)
以 10 为底乘以 10 只是移动一列,乘以 100 是两列,所有这些项目都相加。因此,如果您将乘法操作数分解为可消化的部分并跟踪每个项目需要移动多少,这基本上将该项目放在特定列中,使用我们无限宽的加法,我们已经将内容分解为列或组列。是的,它变成了一个交互过程,如上所述,我们有四件事要添加,我们的级联加法只添加两件事,所以你必须做 3 次宽加法,1)前两个操作数,2)1)的结果加上第三个操作数 3 ) 2) 的结果加上最后一个操作数
假设这些字母中的每一个都是一个 16 位数字
abcd
efgh *
======
上面唯一一个没有与它相关的 16 的倍数移位的乘法对(认为乘以十进制的 10 或 100 或 1000)是 h*d。
所以结果的低 16 位是 h*d 的低 16 位。h*d 的高 16 位必须添加到其他内容中
下一层是 h*c<<16 和 g*d<<16。每一个的低 16 位加在一起,也加到 d*h 的高 16 位。用两个字母组合来表示它们的乘法结果
换句话说 abcd * efgh =
000000hd
00000hc0
0000hb00
000ha000
00000gd0
0000gc00
000gb000
00ga0000
0000fd00
000fc000
00fb0000
0fa00000
000ed000
00ec0000
0eb00000
ea000000 +
==========
h*d 的低 16 位(上面表示为 hd)被加到零并直接进入结果 h*d 的上半部分和 h*c 和 g*d 的下半部分相加成为下一个结果的 16 位,依此类推。
如果您想使用 16*16=32 位乘法运算和 16*16=16+进位加法运算将两个 64 位数字相乘,您可以根据上述进行硬编码。
如果您想将结果限制为 64*64=64bits 而不是 64*64=128bits,您可以将其减半
00hd
0hc0
hb00
a000 (h*a)
0gd0
gc00
b000 (g*b)
fd00
c000 (f*c)
d000 + (e*d)
======
我会离开部门让你弄清楚......
首先使用基本操作加减法,综合进位位在高级语言中实现这一点
做一个添加功能
unsigned int a,b,c,result,carry;
//addition
a = operand1&0xFFFF;
b = operand2&0xFFFF;
c = a+b;
result = c&0xFFFF;
carry = (c>>16)&1;
和一个带进位功能的加法
//add with carry
a=(operand1>>16)&0xFFFF;
b=(operand2>>16)&0xFFFF;
c = a+b+carry_in;
result = c&0xFFFF;
carry = (c>>16)+1;
或者如果你想像硬件一样只做带进位功能的加法,并且在加法步骤中输入 0 表示进位,否则输入进位。
清如泥?
例如,x86 可能会反转减法的执行,而 arm 可能不会。有些处理器根本没有标志(mips),您必须使用小于寄存器大小的数字来合成上述所有内容(使用 32 位寄存器进行 16 位加法,从而允许保存第 17 位、执行或可能做一次 31 位,无论如何)。就像某些处理器一样,您必须在输入上使用乘法大小的一半(上半部分为零)才能得到结果的未剪辑答案,并执行上述游戏以正确执行全宽度乘法。