我正在学习汇编。我正在编写一个简单的程序,它应该做以下事情:
- 从STDIN读取2位数
- 递减读取数
- 将递减结果发送到STDOUT
这里的程序,我到目前为止:
section .data
max_number_size: equ 2 ; max length of read input
section .bss
input_buffer: resb max_number_size ; used to store input number
section .text
global _start
_start:
mov eax, 3 ; sys call read
mov ebx, 0 ; from FD 0
mov ecx, input_buffer ; indicate the adress of the memory buffer where the bytes will be stored
mov edx, max_number_size ; read this quantity of character
int 80H ; store max_number_size to input_buffer from STDIN
atoi:
mov eax, 0 ; Set initial total to 0
mov ebx, 0 ; keep track of nbr of char processed
atoi_convert:
mov esi, [ecx] ; Get the current character
test ebx, max_number_size ; break the loop
je _stdout
cmp esi, 48 ; Anything less than char 0 is invalid (check ASCII table)
jl _exit_1
cmp esi, 57 ; Anything greater than char 9 is invalid (check ASCII table)
jg _exit_1
sub esi, 48 ; Convert from ASCII to decimal (0 starts at 48d in ASCII)
imul eax, 10 ; Multiply total by 10d
add eax, esi ; Add current digit to total
inc ecx ; Get the address of the next character
inc ebx ; keep track of nbr of char processed
jmp atoi_convert
_stdout:
mov ecx, eax
mov eax, 4
mov ebx, 1
mov edx, 32
int 80h
_exit_0:
mov eax, 1
mov ebx, 0
int 80H
_exit_1:
mov eax, 1
mov ebx, 1
int 80H
注意,在上面的代码中,还没有递减。
我很难理解的是在这里的_stdout
标签中实际发送到STDOUT的是什么。
我所理解的是,使用atoi
,从stdin读取的ASCII字符被转换成实际的十进制值(如果input是12,则eax中atoi之后的值将是0000 1100(二进制))。
因此,当到达_stdout
时,eax
中有12d(0000 1100b)。为了将其发送到STDOUT,我将该值移动到ecx
中,为系统调用配置eax
、ebx
、edx
,然后,boom,系统调用。
但是,根本没有输出。
例如,参见:
/app # make
nasm -f elf -g -F stabs main.asm
ld -o main.exe main.o -m elf_i386
/app # ./main.exe > stdout
49
/app #
/app # ls -l | grep stdout
-rwxr-xr-x 1 root root 0 Feb 7 12:05 stdout
stdout
文件是空的。甚至没有一个字节。
我在这个程序中做错了什么,因此,我没有正确理解什么?
1条答案
按热度按时间qvtsj1bj1#
更新:如@Peter Cordes所言,第一段代码缺少输出ASCII数字的功能当前代码在@Peter Cordes给出的reference link的基础上增加了ASCII数字打印功能,谢谢!
使用像GDB这样的调试器来逐步执行并查看寄存器如何变化呢?
如果使用GDB在
_stdout
的int 0x80
之前断点,您会注意到ecx
为NULL。我们来看看为什么,
_stdout
的调用者是atoi_convert
,我们把atoi_convert
断点,一步一步的运行(使用si
命令),你没注意到它总是在je _stdout
中跳转_stdout
吗?test ebx, max_number_size
实际上只是在执行and
指令。它通常用于检测零。在这种情况下,cmp ebx, max_number_size
是合适的。在
atoi_convert
中,mov esi, [ecx]
将ecx
的4字节内容复制到esi
。如您所见,当我输入
49
时,$esi
是0x3934
。但是您假设esi
是1字节(来自cmp esi, 48
等)。正如@Jester所说,您应该使用movzx esi, byte [ecx]
以确保内容是1字节,并且您应该使用read
的返回值而不是max_number_size
。而且,如果用户在终端上只输入一个字符,也会增加一个换行符,如果有无法识别的字符,我觉得调用
_stdout
比调用_exit_1
更好。让我们再次在
int 0x80
之前设置断点。第二个参数应该是一个指针,但它是一个值,我需要把这个值转换成一个指向ASCII字符的指针,看看这个:How do I print an integer in Assembly Level Programming without printf from the c library?
不管怎样,下面的代码是可以工作的。我还添加了一些我没有提到的修正,比如对
read
的错误检查。另外,我还添加了一个基于reference link的ASCII数字打印函数。