【问题标题】:How to add 2 numbers together that are of different lengths in x86 linux nasm assembly如何在x86 linux nasm程序集中将两个不同长度的数字相加
【发布时间】:2021-12-26 15:37:17
【问题描述】:

我对组装非常陌生,并且在使用不同长度数字进行基本计算时遇到困难。

这是我的添加代码,适用于长度为 3 个或更少字符的数字。只要两者的长度相同。例如 123 + 123 工作正常并输出 246。但 12 + 123 不起作用,它输出 253 作为答案。 我如何才能使用不同的长度数字来实现它?

    sys_exit equ 1
    sys_read equ 3
    sys_write equ 4
    stdin equ 0
    stdout equ 1

    section .data
        newLine db 10
        cquestion db 'Enter a number: ', 0xa
        cqLen equ $ - cquestion

        answer db 'Your answer is: '
        aLen equ $ - answer


    section .bss

        number1 resb 4
        number2 resb 4
        number1Len resd 1
        number2Len resd 1

        answ resb 8

    %macro write_string 2
        mov eax, 4
        mov ebx, 1
        mov ecx, %1
        mov edx, %2
        int 0x80
    %endmacro

    section .text
    global _start
    _start:
    write_string cquestion, cqLen

    mov eax, sys_read
    mov ebx, stdin
    mov ecx, number1
    mov edx, 4
    int 0x80
    mov [number1Len], eax

    write_string cquestion, cqLen

    mov eax, sys_read
    mov ebx, stdin
    mov ecx, number2
    mov edx, 4
    int 0x80
    mov [number2Len], eax

    write_string answer, aLen

    clc
    mov ecx, [number2Len]           ;number of digits
    dec ecx                         ;need to decrease one for some reason?
    mov esi, ecx                
    dec esi                         ;pointing to the rightmost digit.
    .add_loop:

        mov al, [number1 + esi]
        adc al, [number2 + esi]
        aaa
        pushf               ; also no idea what this is here for
        or  al, 30h         ; or this
        popf                ; and this...

        mov [answ + esi], al
        dec esi
        loop addition.add_loop

        mov eax, sys_write
        mov ebx, stdout
        mov ecx, answ
        mov edx, 8
        int 0x80
        
        mov eax, sys_write
        mov ebx, stdout
        mov ecx, newLine
        mov edx, 1
        int 0x80

    mov [answ], DWORD 0

【问题讨论】:

  • "出于某种原因需要减少一个?" - 最后摆脱换行符。 pushf/popf 是为下一次迭代保留进位标志的值。 or al, 30h 正在通过添加 0 的 ascii 代码转换为文本。要处理不同的长度,只需假装较短的用零填充。
  • 一次做 1 个数字是非常低效的。特别是使用pushf/or/popf 而不是lea eax, [eax + 0x30],如果0x30 位始终未设置开始。

标签: linux assembly x86 nasm bigint


【解决方案1】:
  • 您的循环永远不会超过 3 次迭代。如果有最终进位,您需要额外写入目的地。
  • 如果代码需要处理不同长度的输入,则不能使用相同的偏移量ESI 来寻址两个数字中的对应数字。
  • 您也不能使用相同的ESI 来存储输出,因为您可能需要在左侧多出一个位置。
  • 关于answ resb 8,将几个3位数相加最多可以产生一个4位数的和。

以下是该问题的众多解决方案之一

                           ECX=1
                           v
num1:  31 32 0A 00      31 32 30 30
num2:  31 32 33 0A      31 32 33 30
                              ^
                              EDX=2

answ:                   00 00 00 00     --> 30 31 33 35
                                 ^
                                 EDI=3
    mov   ecx, [number1Len]           ; Number of bytes (eg. 3)
    sub   ecx, 2                      ; Offset to 'ones' digit
    lea   eax, [ecx + 1]              ; Offset to the newline
  .more1:
    mov   byte [number1 + eax], 30h
    inc   eax
    test  eax, 3
    jnz   .more1

    mov   edx, [number2Len]           ; Number of bytes (eg. 4)
    sub   edx, 2                      ; Offset to 'ones' digit
    lea   eax, [edx + 1]              ; Offset to the newline
  .more2:
    mov   byte [number2 + eax], 30h
    inc   eax
    test  eax, 3
    jnz   .more2

    mov   edi, 3                      ; 4 iterations
    clc
  .add_loop:
    movzx eax, byte [number1 + ecx]
    adc   al, [number2 + edx]
    aaa
    lahf                              ; Preserves CF gotten from `aaa`
    or    al, 30h                     ; Convert into character "0" to "9"
    mov   [answ + edi], al
    dec   ecx
    and   ecx, 3                      ; Wraparound in number1
    dec   edx
    and   edx, 3                      ; Wraparound in number2
    sahf                              ; Restores CF
    dec   edi
    jns   .add_loop

answ 的结果中,我们最多可以删除 3 个前导零。如果真实结果为 0,则必须保留 answ 中的第 4 个字符。

    mov   edx, 4
    mov   ecx, answ
  .del:
    cmp   byte [ecx], 30h
    jne   .ok
    dec   edx
    inc   ecx
    cmp   edx, 1
    ja    .del
  .ok:
    mov   ebx, stdout
    mov   eax, sys_write
    int   80h

【讨论】:

  • 请注意,使用 sahf / lahf 可以更便宜地保存/恢复 CF。甚至setc al / add al, 255。此外,如果 BigInt 像往常一样使用 little-endian 顺序(最低地址处的最低有效块),您可以在每个静态数组中使用相同的偏移量。当你到达一个结束时,那么你只是通过其他的其余部分进行进位传播(movzx/adc al, 0/aaa 或将 AF 传播到 CF 的东西,因为这是未打包的 BCD?),并且可以选择回退到仅在进位未传播时进行复制。
  • @SepRoland 你确定这是正确的吗?它不适合我。我注意到的是,就在 .more2 之前:你执行 'mov eax, ecx',当我们实际上想将 number2Len 的偏移量移动到 eax 中时,它会将 number1Len 的偏移量移动到 eax 中,而 eax 就在 edx 中我能够了解。但即使在更改代码后也无法正常工作。我也同意答案中的前置零不是最漂亮的东西,所以我们是否可以在代码中添加一个“硬编码”零,我们将添加到更长的数字中而不是前置零?
  • @Lauri Me 使用ECX 需要EDX 是复制/粘贴出错的完美示例!尽管如此,我在这个答案中犯了太多错误。准备循环运行的时间太长,我不承认这是关于解压 BCD 的事实。我相信我今天的编辑这次是正确的(手指交叉)。我添加了代码来删除多余的前导零。
  • @SepRoland 是的,现在它运行良好。仅供参考,您的错误让我仔细查看了您编写的代码,虽然我花了很多时间才开始看到您列出的相同错误,但我需要很长时间才能像您一样工作。所以感谢您的回答和学习机会!
  • @Lauri 感谢您的美言,祝您学习愉快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-14
  • 2015-09-20
  • 2014-02-21
  • 2011-12-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多