我正在尝试编写一个以下列方式工作的程序:
获取用户->
输入的数字,例如,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
答案 0 :(得分:2)
_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)
另一个问题是SYS_READ
中_getText
系统调用的棘手行为。您将不会获得带有null终止符的C样式字符串。如果根据SYS_READ
有足够的位置,则\n
会在RDX
的末尾填充缓冲区。有时\n
,有时不会 - 这对_toInteger
来说不是一个有用的中断条件。 woirkaround是取消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
返回RAX
中的整数,但您不保存此值。 EAX
上的下一个写操作会破坏它。
答案 1 :(得分:1)
你的"无限循环"在你的_toInteger函数中。
RDX将为0或第一个元素或ASCII输入的值,因为您通过跳回标签_toInteger重置指向第一个元素的指针。因此,您永远不能离开或跳出循环。
我们不能强调这一点;你应该总是使用调试器。
mov rcx, ascii ; preparing for working with input
但即使你解决了这个问题,_toInteger函数似乎还有其他问题。
$ ./jazz_001
12
It's not an integer
20