Assembly 如何添加两个数字,每个数字有12个字节?
我想添加两个有12个字节的数字,并将结果存储在一个16字节的变量中。我如何才能做到这一点Assembly 如何添加两个数字,每个数字有12个字节?,assembly,x86,bigint,carryflag,extended-precision,Assembly,X86,Bigint,Carryflag,Extended Precision,我想添加两个有12个字节的数字,并将结果存储在一个16字节的变量中。我如何才能做到这一点 section .data big_num1 dd 0x11111111, 0x22222222, 0x33333333 big_num2 dd 0xffffffff, 0x22222222, 0x33333333 section .bss result_4word resd 4 我想我可以把数字1的前4个字节和数字2的前4个字节相加,以此类推。。但是我不知道如何在我的结果变
section .data
big_num1 dd 0x11111111, 0x22222222, 0x33333333
big_num2 dd 0xffffffff, 0x22222222, 0x33333333
section .bss
result_4word resd 4
我想我可以把数字1的前4个字节和数字2的前4个字节相加,以此类推。。但是我不知道如何在我的结果变量中连接结果。
如果需要的话,我该如何搬运?
这是正确的解决方案吗
xor eax, eax
xor ebx, ebx
mov ecx, 3
loop1:
mov eax, dword[big_num1+4*(ecx-1)]
mov ebx, dword[big_num2+4*(ecx-1)]
mov [result_4word+4*(ecx-1)], eax
adc [result_4word+4*(ecx-1)], ebx
loop loop1
这里定义了什么数字?
因为是一种小端结构,所以数字的最低部分存储在最低地址的内存中。对于big_num1,第一个定义的dword(值为0x11111111)位于最低地址,因此是数字的最低部分。在正常的数字表示法中,这是右手边的数字
big_num1 == 0x333333332222222211111111
big_num2 == 0x3333333322222222FFFFFFFF
添加大数字
从右到左添加相应的数字,就像每个人在学校学到的一样
在这些数字的十六进制表示中,有24个数字需要考虑。然而,由于体系结构是32位的,我们可以很好地制作3组8位数字
对于第一组,我们只需使用添加:
mov eax, [big_num1] ; 0x11111111
add eax, [big_num2] ; + 0xFFFFFFFF <-- This produces a carry
mov [result_4dword], eax ; 0x00000000
对于第三组,我们使用ADC
从上一次添加中提取可能的进位:
mov eax, [big_num1 + 4] ; 0x22222222
adc eax, [big_num2 + 4] ; + 0x22222222 + CF=1 <-- No new carry
mov [result_4dword + 4], eax ; 0x44444445
mov eax, [big_num1 + 8] ; 0x33333333
adc eax, [big_num2 + 8] ; + 0x33333333 + CF=0 <-- No new carry
mov [result_4dword + 8], eax ; 0x66666666
现在我们可以编写一个包含3次迭代的循环,但是我们必须小心不要无意中更改进位标志。这就是为什么我使用LEA
而不是ADD
来推进偏移量DEC
也是一条不会破坏进位标志的指令。我更喜欢DEC ECX的组合,因为它比循环更好
mov ecx, 3
xor ebx, ebx ; This additionally clears the carry flag
Again:
mov eax, [big_num1 + ebx]
adc eax, [big_num2 + ebx] ; Can produce a new carry flag
mov [result_4dword + ebx], eax
lea ebx, [ebx + 4] ; This does not clobber the carry flag
dec ecx ; This does not clobber the carry flag
jnz Again
如果在这3个加法之后仍然有一个进位,您必须在result_4word的第4个双字中写一个1,否则您必须在这里写一个0。因为result_4dword在.bss部分,所以您不应该指望任何预设值,比如零
setc cl
mov [result_4dword + ebx], ecx ; ECX=[0,1]
请注意,我已将result_4word更改为result_4dword。更有意义…小学:
1234
+ 5678
========
开始填写
1
1234
+ 5678
========
2
4+8=12,所以2代表1
在电脑里你会
加上a=4+8
adc b=3+7
adc c=2+6
adc d=1+5
然后dcba包含您的结果,它可以扩展到您想要的范围。d、 根据指令集的不同,c、b、a可以是8位、16位、32位或64位。大多数都有add和adc,如果它们有标志,那些没有标志,那么你可以用各种方式合成它们,一点也不困难。。。(使用32位寄存器/内存将操作数分解为16位量。现在32位加法第16位是您的执行,将其添加到下一个16位块中,需要一些移位和掩蔽,但所有操作都是一样的,因为您可能有adc,所以您不需要执行任何操作,只需执行简单的加法、adc、adc、adc…直到完成
如果在开始之前清除该标志,则可以在循环中使用adc
现在,如果你的变量没有和处理器中的加法器对齐,那么你必须以某种方式合成它
小学数学对于同样的问题,现在你必须分开做专栏
4
+ 8
====
12
您必须手动屏蔽并移动结果(12>>1)%9=1,以10为基数
1
3
+ 7
====
11
然后
1
2
+ 6
====
9
这个带零
0
1
+ 5
====
6
阅读指令集参考中的adc
条目。您无需验证任何内容。adc
将为您完成此操作。使用Add
添加最低的DWORD,其余部分使用adc
。完成。是的,但这是adc
的明确目的,因此您不必手动完成。它确实有效,但您只有最后加法的进位,不存储在任何地方。操作数和结果的大小仅受内存限制。如果要将100万位数字添加到另一个100万位数字中,非常简单。第一个字节/字/dword,无论您选择使用什么,都可以添加,其余的使用adc。是的,dec
/jnz
是在Sandybridge系列上,部分标志暂停不是问题。在早期的Intel上,如Nehalem和Core 2,您可能希望保存/恢复标志或可能使用LEA/JECXZ/JMP循环(但这有点糟糕,所以您需要展开以隐藏成本)除了最近的AMD之外,loop
在所有方面都很慢,这真是太糟糕了,因为它在其他方面非常适合ADC环路
1
2
+ 6
====
9
0
1
+ 5
====
6