x86汇编Linux系统调用计算器无法显示计算结果问题排查
解决x86汇编计算器无法显示结果的问题
嘿,我帮你揪出了几个导致结果无法正常显示的核心问题,咱们一步步来修复:
问题1:输入读取的文件描述符错误
Linux系统调用中,sys_read(eax=3)的文件描述符参数(ebx),标准输入是0,而你写成了2(这是标准错误stderr)。虽然某些环境下可能凑巧能读到输入,但这是不规范的,还可能引发异常。把所有读取输入的mov ebx, 2改成mov ebx, 0。
问题2:缺少字符串转整数的处理
你直接把first和second的内容加到eax里,但用户输入的是ASCII字符串(比如输入"10",内存里是字符'1'(ASCII 49)和'0'(ASCII 48)),不是直接可用的整数。必须先把这些字符串转换成十进制整数才能计算。
我给你加个字符串转整数的函数str_to_int,它会把输入的数字字符串转成整数存在eax里:
; 字符串转整数:输入ecx=字符串地址,输出eax=转换后的整数 str_to_int: xor eax, eax ; 初始化结果为0 mov esi, ecx ; esi指向字符串起始 .next_digit: movzx edx, byte [esi] ; 取当前字符 cmp dl, 0xA ; 判断是否是换行符(输入结束) je .done cmp dl, 0xD ; 处理回车符 je .done sub dl, '0' ; ASCII转数字 imul eax, 10 ; 结果*10 add eax, edx ; 加上当前数字 inc esi ; 指针后移 jmp .next_digit .done: ret
然后在读取完first和second后调用这个函数,把结果存到对应的内存里:
; 读取第一个数字后转成整数 mov ecx, first call str_to_int mov [first_num], eax ; 读取第二个数字后转成整数 mov ecx, second call str_to_int mov [second_num], eax
记得在.bss段加上存储整数的变量:
first_num resd 1 second_num resd 1
问题3:缺少整数转字符串的处理
计算后的结果是整数(比如15),但sys_write需要输出ASCII字符串。你直接把[result]作为输出地址,其实是把整数的二进制值当成内存地址,指向的内容不是可显示的字符,所以看不到正确结果。
再加个整数转字符串的函数int_to_str,把整数转成ASCII字符串:
; 整数转字符串:输入eax=整数,ecx=输出字符串地址,输出edx=字符串长度,ecx=字符串起始地址 int_to_str: mov esi, ecx add esi, 9 ; 从字符串末尾开始(最多10位数字) mov byte [esi], 0 ; 字符串结束符 mov ebx, 10 ; 除数10 .next_digit: xor edx, edx div ebx ; eax=商,edx=余数 add dl, '0' ; 余数转ASCII dec esi mov [esi], dl test eax, eax jnz .next_digit mov edx, ecx add edx, 10 sub edx, esi ; 计算字符串长度 mov ecx, esi ; 字符串起始地址 ret
然后在计算完结果后调用这个函数,再输出:
equalsSignSum: ;sum system xor eax, eax add eax, [first_num] ; 用转换后的整数计算 add eax, [second_num] mov ecx, result call int_to_str ;display result section mov eax, 4 mov ebx, 1 mov ecx, resultMsg mov edx, lenResultMsg int 80h ;display the result number mov eax, 4 mov ebx, 1 ; int_to_str已经把ecx设为字符串起始,edx设为长度 int 80h jmp exit
修改后的完整代码
section .data ;Data segment firstNumberMsg db 'Please give the first number: ' ;Ask the user for first number to calculate lenFirstNumberMsg equ $-firstNumberMsg ;The length of the message secondNumberMsg db 'Please give the second number: ' ;Ask the user for second number to calculate lenSecondNumberMsg equ $-secondNumberMsg ;The length of the message signMsg db 'Please give the calculation sign: ' ;Ask the user for calculation type lenSignMsg equ $-signMsg ;The length of the message resultMsg db 'Result: ' lenResultMsg equ $-resultMsg section .bss first resb 5 second resb 5 result resb 10 sign resb 1 first_num resd 1 second_num resd 1 section .text global _start ; 字符串转整数:输入ecx=字符串地址,输出eax=转换后的整数 str_to_int: xor eax, eax mov esi, ecx .next_digit: movzx edx, byte [esi] cmp dl, 0xA je .done cmp dl, 0xD je .done sub dl, '0' imul eax, 10 add eax, edx inc esi jmp .next_digit .done: ret ; 整数转字符串:输入eax=整数,ecx=输出字符串地址,输出edx=字符串长度,ecx=字符串起始地址 int_to_str: mov esi, ecx add esi, 9 mov byte [esi], 0 mov ebx, 10 .next_digit: xor edx, edx div ebx add dl, '0' dec esi mov [esi], dl test eax, eax jnz .next_digit mov edx, ecx add edx, 10 sub edx, esi mov ecx, esi ret exit: ;Exit from program mov eax, 1 mov ebx, 0 int 80h equalsSignSum: ;sum system xor eax, eax add eax, [first_num] add eax, [second_num] mov ecx, result call int_to_str ;display result section mov eax, 4 mov ebx, 1 mov ecx, resultMsg mov edx, lenResultMsg int 80h ;display the result number mov eax, 4 mov ebx, 1 int 80h jmp exit _start: ;Display the first prompt for entering a number mov eax, 4 mov ebx, 1 mov ecx, firstNumberMsg mov edx, lenFirstNumberMsg int 80h ;Read stdin from user (as a first number) mov eax, 3 mov ebx, 0 mov ecx, first mov edx, 5 ;5 bytes (numeric, 1 for sign) of that information int 80h ; 转成整数 mov ecx, first call str_to_int mov [first_num], eax ;Display the second prompt for entering a number mov eax, 4 mov ebx, 1 mov ecx, secondNumberMsg mov edx, lenSecondNumberMsg int 80h ;Read stdin from user (as a second number) mov eax, 3 mov ebx, 0 mov ecx, second mov edx, 5 ;5 bytes (numeric, 1 for sign) of that information int 80h ; 转成整数 mov ecx, second call str_to_int mov [second_num], eax ;Display the sign prompt mov eax, 4 mov ebx, 1 mov ecx, signMsg mov edx, lenSignMsg int 80h ;Read stdin from user (as a sign) mov eax, 3 mov ebx, 0 mov ecx, sign mov edx, 2 ; 多读1字节,因为输入+后会按回车,避免后续判断出错 int 80h ;Calculation sign section mov ebx, 43 ;43 is '+' in ascii cmp [sign], ebx je equalsSignSum ;jump to this label if user entered '+' as sign ;else ; Exit code mov eax, 1 mov ebx, 0 int 80h
编译运行
还是用你原来的编译命令:
nasm -f elf calculator.asm && ld -m elf_i386 -s -o calculator calculator.o && ./calculator
现在输入10、5、+,就能正常显示Result: 15啦~
内容的提问来源于stack exchange,提问作者p1oXYZ
相关产品推荐
相关产品推荐

