x86 nasm将字符串的字符串转换为整数



这是一个简单的问题,但它使我的头旋转。我需要将一串字符字符串(输入为负十进制数字(转换为一个无符号整数。RDI寄存器包含要转换的字符串。RAX寄存器将保持结果。

    xor rsi, rsi
    xor rax, rax
    xor dl, dl
    xor rdx, rdx
convert:
    mov dl, [rdi+rsi]    ;+rsi causes segmentation fault
    cmp dl, "-"
    jz  increment
    cmp dl, "."
    jz  dtoi_end
    sub dl, "0"
    mov rdx, 10
    mul rdx
    add rax, dl          ;invalid combination
    inc rsi
    jmp convert
increment:
    inc rsi
    jmp convert
convert_end:
    ret
  1. 我需要迭代每个字符,并且我正在尝试使用RSI寄存器来使用它。但是每次尝试此操作时,我都会得到一个细分错误。

  2. 无效的组合误差。我知道这是因为寄存器的大小不同,但是我迷失了如何继续将转换后的ASCII值添加回RAX。

这里有一个类似的问题可以帮助我更好地理解这一过程,但是我已经撞到了一堵墙:将字符串转换为int。x86 32位汇编器使用nasm

我需要迭代每个字符,并且我试图通过使用RSI寄存器来使用它。但是每次尝试此操作时,我都会得到一个细分错误。

基于您显示的代码以及RDI的语句保留字符串开始的地址,我可以看到一些不同的原因,为什么您会在该负载中获得分割错误。

也许问题是RDI包含一个8个字符ASCII字符串(通过值通过(,而不是包含字符串的内存位置的地址(通过参考通过(?

另一个可能性的可能性是它在循环的前几个迭代中正常工作,但是随后您开始尝试读取字符串末端,因为您无法正确终止循环。您显示的代码中没有dtoi_end标签,也没有您实际跳到convert_end标签的位置。这些应该是同一标签吗?如果我通过字符串" -2"会怎样?您的循环何时终止?在我看来,它不会!

您需要某种方法来指示整个字符串已经处理过。有几种常见方法。一个人在字符串末端使用哨兵终结器字符,就像C与ASCIINUL字符一样。在循环的内部,您会检查要处理的角色是否为0(NUL(,如果是的,请跳出循环。另一个选项是将字符串的长度作为附加参数传递给该函数,就像帕斯卡(Pascal(使用计数长度字符串一样。然后,您将在循环的内部进行测试,检查是否已经处理过足够的字符,如果是的话,请跳出循环。

我会尽量不要对此过于讲道,但是您应该能够通过使用调试器自己发现这个问题。逐步执行逐条执行,观察变量/寄存器的值,并确保您了解正在发生的事情。这基本上是我在分析您的代码时所做的,除了我将头作为调试器,"在我自己的脑海中执行"代码。不过,让计算机这样做要容易得多(且容易出错(,这就是为什么发明辩论者的原因。如果您的代码不起作用,并且还没有在调试器中逐条逐步浏览它,那么您还没有努力工作以自己解决问题。实际上,通过单步步您编写的每个功能都是一个很好的习惯,因为(a(它将确保您了解所写的内容的逻辑,并且(b('将帮助您找到错误。

无效的组合错误。我知道这是因为寄存器的大小不同,但是我迷失了如何继续将转换后的ASCII值添加到RAX中。

您必须使大小匹配。您可以执行add al, dl,但是您将结果将结果限制为8位字节。那可能不是你想要的。因此,您需要将dl制成64位QWOWS,例如rax。这样做的明显方法是使用MOVZX指令,该指令零扩展。换句话说,它将值扩展到更大的尺寸,并用0填充上部。这就是您想要的未签名值。对于签名的值,您需要进行签名感知扩展名(即,考虑到符号位(,并且要执行此操作,您将使用MOVSX指令。

代码:

movzx  rdx, dl
add    rax, rdx

请注意,正如一位评论者指出的那样,DL只是RDX寄存器的最低8位:

| 63 - 32 | 31 - 16 | 15 - 8 | 7 - 0 |
--------------------------------------
                    |   DH   |   DL  |
--------------------------------------
          |           EDX            |
--------------------------------------
|                 RDX                |

因此,它是xor dl, dlxor rdx, rdx的冗余。后者实现了前者。另外,每次修改dl时,实际上您都在修改rdx的最低8位,这将导致结果不正确。提示,提示:这是您通过一次调试器单步步捕获的其他东西(尽管您可能不明白为什么!(。

此外,根本不需要进行xor rdx, rdx!您可以通过执行xor edx, edx

更有效地完成相同的任务

只是为了娱乐,这是代码的可能实现:

; Parameters: RDI == address of start of character string
;             RCX == number of characters in string
; Clobbers:   RDX, RSI
; Returns:    result is in RAX
    xor   esi, esi
convert:
    ; See if we've done enough characters by checking the length of the string
    ; against our current index.
    cmp   rsi, rcx
    jge   convert_end
    ; Get the next character from the string.
    mov   dl, BYTE [rdi + rsi]
    cmp   dl, "-"
    je    increment
    cmp   dl, "."
    je    convert_end
    ; Efficient way to multiply by 10.
    ; (Faster and less difficult to write than the MUL instruction.)
    add   rax, rax
    lea   rax, [4 * rax + rax]
    sub   dl, "0"
    movzx rdx, dl
    add   rax, rdx
    ; (fall through to increment---no reason for redundant instructions!)
increment:
    inc   rsi            ; increment index/counter
    jmp   convert        ; keep looping
convert_end:
    ret

(警告:未经测试的逻辑!我只是以更优化的方式重写了您的现有代码,而没有错误。(

最新更新