Assembly 程序集x86_64:从用户获取输入整数并打印它
我正在尝试编写一个程序,其工作方式如下: 获取用户输入的数字Assembly 程序集x86_64:从用户获取输入整数并打印它,assembly,nasm,x86-64,Assembly,Nasm,X86 64,我正在尝试编写一个程序,其工作方式如下: 获取用户输入的数字->除以,例如2->打印结果(商) divide-by-The-number-2部分似乎没有太大的难度,所以我初步编写了一个程序,由用户输入一个整数并打印该整数 这意味着我试图编写一个程序,将用户的字符串整数转换为实整数,然后将其转换回字符串并打印出来 但是在编译之后,我最终进入了一个无限循环(在点击Enter之后,什么也没有发生) 我使用以下命令进行编译: nasm -f elf64 ascii.asm -o ascii.o ld
->
除以,例如2->
打印结果(商)
divide-by-The-number-2
部分似乎没有太大的难度,所以我初步编写了一个程序,由用户输入一个整数并打印该整数
这意味着我试图编写一个程序,将用户的字符串整数转换为实整数,然后将其转换回字符串并打印出来
但是在编译之后,我最终进入了一个无限循环(在点击Enter
之后,什么也没有发生)
我使用以下命令进行编译:
nasm -f elf64 ascii.asm -o ascii.o
ld ascii.o -o ascii
./ascii
在下面的代码中,子程序\u getInteger
用于从字符串到整数的转换,子程序\u appendEOL
和\u 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
你的“无限循环”在你的_-toInteger函数中
$ ./jazz_001
12
It's not an integer
20
RDX将为0或第一个元素或ASCII输入的值,因为您要通过跳回标签_-toInteger来重置指向第一个元素的指针。因此,您永远不能离开或跳出循环
我们怎么强调都不过分;您应该始终使用调试器
mov rcx, ascii ; preparing for working with input
但是,即使解决了这个问题,_-toInteger函数似乎也存在其他问题
$ ./jazz_001
12
It's not an integer
20
\u 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\u READ
syscall在\u getText
中的棘手行为。您不会得到带有空终止符的C样式字符串SYS\u READ
在缓冲区末尾填充\n
-如果根据RDX
有足够的位置。有时\n
,有时不-这不是\u-toInteger
的有用中断条件。woirkaround是将系统读取的最后一个字节置零,无论它是\n
还是数字。这会将可用缓冲区缩短1
_getText:
mov rax, 0
mov rdi, 0
mov rsi, ascii
mov rdx, 16
syscall
mov byte [ascii-1+rax], 0
ret
为SYS\u 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
请注意:\u toInteger
返回RAX
中的整数,但不保存此值。EAX
上的下一个写操作将破坏它。听起来似乎是时候了解GDB(或其他调试器)@tommyle2k这意味着什么(:?我在上面的代码中有什么错误吗?我花了两周的时间来模拟汇编,所以我对汇编几乎一无所知。我没有完全理解你的意思):。这意味着,如果您试图证明代码的正确性,那么源代码是否编译几乎是无关紧要的(除非任何编译时失败都会立即排除)。实际上,即使运行可执行文件并收到预期结果,也离证明正确性还有很长的路要走。为了至少更接近这个难以捉摸的乌托邦目标,您应该使用调试器逐条执行代码指令,根据您的期望/设计测试每个执行指令后的当前机器状态,并测试不同的输入,如null/empty/large/…很抱歉回复太晚,但是我对GDB有点熟悉了(真的是一件很不错的事情)。我有4个问题,所以我很感谢您的指导:1)
在这个预定的程序中,除了由于乘法后的溢出之外,mul-rbx
可以通过什么方式更改edx
寄存器?(也许在这个问题中我没有考虑字节的某些方面)2)
我知道lea-rax,[rax+rax*4]
在每个循环之后,将rax 2
位的值向左移动,以便为下一个数字留出一些空间。正确吗?3)
您指出的SYS\u READ
问题是因为我任意设置了用户输入的字符串长度,对吗4)
我读到imul
清除溢出(如果我没有错的话,这意味着edx
不会被更改),但是为什么要使用imul
的三个操作数形式,而不仅仅是一个操作数(或两个操作数)。@Jazz:这是一个世界性的网站。即使是24小时的延迟也不是不礼貌的。1) mul-rbx
在EDX:EAX
中生成64位结果,而不仅仅是溢出。2) 该LEA
行与前一行一起只是执行EAX=EAX*10的一种方法。它不会改变EDX,也不会改变标志。3) SYS_READ的问题是SYS_READ的问题。你完全是无辜的。关于SYS_READ,我问了三个问题。单击标记,然后单击“活动”,您将看到这些问题直接位于您的问题下方(由我修改)…@Jazz:4)IMUL
的一个操作数形式也会发生变化EDX
。当然,也可以使用双操作数形式。你是在集会的世界里。这里不存在一个标准、意识形态或宗教——如果它起作用,那就好了。你需要一份详细的参考资料,哇。有很多很酷的东西要学。谢谢你回答这些问题。我正在仔细阅读关于SYS_READ的文章。我希望在这几天内解决这些问题。如果没有,我将创建另一个问题xD(但希望有更好的基础)。再次感谢您抽出时间。