程序集 x86_64:从用户获取输入整数并打印出来

Assembly x86_64: Getting input integer from user and print it

我正在尝试编写一个按以下方式工作的程序:

获取用户输入的数字 -> 除以 2 -> 打印结果(商)。

divide-by-the-number-2 部分似乎并没有太大的难度,所以我初步编写了一个程序,获取用户输入的整数并打印该整数。

这意味着我尝试编写一个程序,将用户的字符串整数转换为真正的整数,然后将其转换回字符串并打印出来。

但是在编译之后我陷入了一个无限循环(点击 Enter 之后没有任何反应)。

我使用以下命令编译:

nasm -f elf64 ascii.asm -o ascii.o

ld ascii.o -o ascii

./ascii

在下面的代码中,子程序 _getInteger 用于从字符串到整数的转换,子程序 _appendEOL_loopDigit 用于从整数到字符串的整个转换。

section .bss
        ascii resb 16           ; holds user input
        intMemory resb 100      ; will hold the endline feed 
        intAddress resb 8       ; hold offset address from the intMemory

section .data
        text db "It's not an integer", 10
        len equ $-text

section .text

        global _start

_start:

        call _getText
        call _toInteger
        call _appendEOL

mov rax, 60
        mov rdi, 0
        syscall

_getText:
        mov rax, 0
        mov rdi, 0
        mov rsi, ascii
        mov rdx, 16
        syscall
        ret

_toInteger:
        mov rbx,10      ; for decimal scaling
        xor rax, rax    ; initializing result
        mov rcx, ascii  ; preparing for working with input
        movzx rdx, byte [rcx]   ; getting first byte (digit)
        inc rcx         ; for the next digit

        cmp rdx, '0'    ; if it's less than '0' is not a digit
        jb _invalid

        cmp rdx, '9'    ; if it's greater than '9' is not a digit
        ja _invalid

        sub rdx, '0'    ; getting decimal value
        mul rbx         ; rax = rax*10
        add rax, rdx    ; rax = rax + rdx
        jmp _toInteger  ; repeat
        ret

_invalid:
        mov rax, 1
        mov rdi, 1
        mov rsi, text
        mov rdx, len
        syscall
        ret

_appendEOL:
        ; getting EOL
        mov rcx, intMemory
        mov rbx, 10 ; EOL
        mov [rcx], rbx
        inc rcx
        mov [intAddress], rcx

_loopDigit:
        xor rdx, rdx
        mov rbx, 10
        div rbx
        push rax
        add rdx, '0'
        mov rcx, [intAddress]
        mov [rcx], dl
        inc rcx
        mov [intAddress], rcx
        pop rax
        cmp rax, 0
        jne _loopDigit

_printDigit:
        mov rcx, [intAddress]

        mov rax, 1
        mov rdi, 1
        mov rsi, rcx
        mov rdx, 1
        syscall
        mov rcx, [intAddress]
        dec rcx
        mov [intAddress], rcx
        cmp rcx, intMemory
        jge _printDigit

        ret

您的 "infinite loop" 在您的 _toInteger 函数中。

RDX 将为 0 或您的第一个元素或 ASCII 输入的值,因为您正在通过跳回标签 _toInteger 将指针重置为第一个元素。因此,您永远不能离开或跳出循环。

我们再怎么强调也不为过;您应该始终使用调试器。

 mov rcx, ascii  ; preparing for working with input

但即使您解决了该问题,_toInteger 函数似乎还有其他问题。

$ ./jazz_001
12
It's not an integer
20

_toInteger 是一个无限循环,永远检查第一个数字。您需要更好的循环入口和中断条件。

下一期是mul rbx。此指令也更改了 EDX,需要将其添加到下面的 RAX 行中。如果不想用IMUL rax,rax,10可以用LEA:

的算术能力
add rax, rax                ; RAX = RAX * 2
lea rax, [rax + rax * 4]    ; RAX = (former RAX * 2) + (former RAX * 8)

另一个问题是 _getTextSYS_READ 系统调用的棘手行为。您不会得到带有空终止符的 C 风格字符串。 SYS_READ 在末尾用 \n 填充缓冲区 - 如果根据 RDX 有足够的空间。有时 \n,有时不是 - 这不是 _toInteger 的有用中断条件。解决方法是使 SYS_READ 的最后一个字节无效,无论它是 \n 还是数字。这会将可用缓冲区缩短 1.

_getText:
    mov rax, 0
    mov rdi, 0
    mov rsi, ascii
    mov rdx, 16
    syscall
    mov byte [ascii-1+rax], 0
    ret

准备好迎接 SYS_READ 给您的更多惊喜。中断条件现在为空。让我们开始吧:

_toInteger:
    mov rbx,10      ; for decimal scaling
    xor rax, rax    ; initializing result
    mov rcx, ascii  ; preparing for working with input

    .LL1:           ; loops the bytes
    movzx rdx, byte [rcx]   ; getting current byte (digit)

    test rdx, rdx   ; RDX == 0?
    jz .done        ; Yes: break

    inc rcx         ; for the next digit

    cmp rdx, '0'    ; if it's less than '0' is not a digit
    jb _invalid

    cmp rdx, '9'    ; if it's greater than '9' is not a digit
    ja _invalid

    sub rdx, '0'    ; getting decimal value

    ; mul rbx         ; rax = rax*10
    add rax, rax
    lea rax, [rax + rax * 4]

    add rax, rdx    ; rax = rax + rdx

    ;jmp _toInteger  ; repeat
    jmp .LL1  ; repeat

    .done:
    ret

请注意:_toInteger returns 与 RAX 中的整数,但您不保存此值。 EAX 上的下一个写操作将销毁它。