big_num1 dd 0x11111111, 0x22222222, 0x33333333
big_num2 dd 0xffffffff, 0x22222222, 0x33333333
这里定义了哪些数字?
因为x86是 little-endian 架构,所以数字的最低部分存储在内存中的最低地址。对于big_num1,第一个定义的 dword(值为 0x11111111)位于最低地址,因此是数字的最低部分。在正常的数字表示中,这就是右侧的内容。
big_num1 == 0x333333332222222211111111
big_num2 == 0x3333333322222222FFFFFFFF
添加大数字
您从右到左添加相应的数字,就像每个人在学校都学过的一样。
在这些数字的十六进制表示中,需要考虑 24 位数字。然而,由于架构是 32 位的,我们可以很好地制作 3 组 8 位数字。
对于第一组,我们只需使用ADD
:
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
对于第 3 组,我们用来ADC
从之前的添加中选择一个可能的进位:
mov eax, [big_num1 + 8] ; 0x33333333
adc eax, [big_num2 + 8] ; + 0x33333333 + CF=0 <-- No new carry
mov [result_4dword + 8], eax ; 0x66666666
把它变成一个循环
这里的关键是,如果我们事先明确清除进位标志,我们也可以ADC
用于第一组:
clc
mov eax, [big_num1] ; 0x11111111
adc eax, [big_num2] ; + 0xFFFFFFFF + CF=0 <-- This produces a carry
mov [result_4dword], eax ; 0x00000000
现在我们可以编写一个包含 3 次迭代的循环,但我们必须小心不要无意中更改进位标志。这就是为什么我使用LEA
而不是ADD
为了推进偏移量。DEC
也是不破坏进位标志的指令。我更喜欢这个组合DEC ECX
JNZ ...
,因为它比LOOP ...
:
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_4dword的第 4 个 dword 中写入1,否则您必须在此处写入0。因为result_4dword在 .bss 部分,所以你不应该指望任何像零这样的预设值!
setc cl
mov [result_4dword + ebx], ecx ; ECX=[0,1]
请注意,我已将result_4word更改为result_4 d word。更有意义...