程序集 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)
另一个问题是 _getText
中 SYS_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
上的下一个写操作将销毁它。
我正在尝试编写一个按以下方式工作的程序:
获取用户输入的数字 ->
除以 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)
另一个问题是 _getText
中 SYS_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
上的下一个写操作将销毁它。