assembly 我如何将两个12字节的数字相加?

9wbgstp7  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(195)

我想把两个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个字节相加,以此类推。但是我不知道如何在我的result变量中连接结果。如果需要进位,我应该怎么做?这个解决方案是正确的吗?

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
czfnxgou

czfnxgou1#

big_num1 dd 0x11111111, 0x22222222, 0x33333333
big_num2 dd 0xffffffff, 0x22222222, 0x33333333

此处定义了哪些数字?

因为x86是小端结构,所以数字的最低部分存储在内存中的最低地址。对于 big_num1,第一个定义的双字(值为0x 1111111)位于最低地址,因此是数字的最低部分。在正常的数字表示中,这是位于右手边的部分。

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

对于第三组,我们使用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 ECXJNZ ...,因为它比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

如果在这三次加法之后仍然有一个进位,你必须在 result_4dword 的第四个dword中写一个1,否则你必须在这里写一个0。因为 result_4dword 在.bss部分中,你不应该指望任何预设值,比如零!

setc    cl
    mov     [result_4dword + ebx], ecx  ; ECX=[0,1]

请注意,我已经将 result_4word 更改为 result_4dword。这样更有意义...

j1dl9f46

j1dl9f462#

小学:

1234
+  5678
========

开始填写

1
   1234
+  5678
========
      2

4+8 = 12,所以2携带1。
在计算机中,您可以:
加a = 4 + 8
模数转换器B = 3 + 7
模数转换器c = 2 + 6
模数转换器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

相关问题