如何在RISC-V汇编中实现C的puts函数?现有putchar循环调用异常
问题分析与修复
你的代码核心问题出在putchar函数的实现上,导致调用puts时输出未知字符。
1. putchar函数的逻辑错误
你提到测试putchar时能正确输出a0中的字符,但实际上那是因为测试时先调用getchar把字符读到栈上,putchar刚好读取栈上的正确数据。但当puts调用putchar时,a0是从字符串中加载的字符,而putchar却依然去读取栈上的垃圾数据,同时错误地把a2设为字符的ASCII值(而非输出字节数),这就导致了输出空白方块。
错误代码:
putchar: # Write the just read character to the terminal addi a2, a0, 0 li a7, __NR_WRITE li a0, STDOUT mv a1, sp ecall ret
修复后的putchar:
putchar: # 临时将a0中的字符存入栈中 addi sp, sp, -4 sb a0, 0(sp) # 调用WRITE系统调用:a0=STDOUT, a1=缓冲区地址, a2=字节数, a7=系统调用号 li a7, __NR_WRITE li a0, STDOUT mv a1, sp li a2, 1 # 输出1个字符 ecall # 恢复栈指针 addi sp, sp, 4 ret
2. puts循环的可选优化
当前puts用地址比较beq a1,a3,puts_loop_end来判断字符串结束,这在你用标签标记字符串末尾的场景下是可行的。如果要让puts支持C风格的以\0结尾的字符串,可以把循环条件修改为检查字符是否为0:
puts: puts_loop: lb a0, 0(a1) beqz a0, puts_loop_end # 字符为0时结束循环 jal ra, putchar addi a1, a1, 1 j puts_loop puts_loop_end: ret
同时需要把.data段的字符串改为.asciz(自动添加终止符):
.data prompt: .asciz "Enter a message: "
修复后的完整代码
.globl main .equ STDOUT, 1 .equ STDIN, 0 .equ __NR_READ, 63 .equ __NR_WRITE, 64 .text main: #prolog addi sp, sp, -104 sw ra, 100(sp) # Load prompt and call puts la a1, prompt la a3, prompt_end jal ra,puts #epilogue lw ra, 100(sp) addi sp, sp, 104 ret getchar: # Read up to 1 character from the terminal (stdin) li a7, __NR_READ li a0, STDIN mv a1, sp addi a2, zero, 1 ecall ret putchar: # 临时将a0中的字符存入栈中 addi sp, sp, -4 sb a0, 0(sp) # 调用WRITE系统调用 li a7, __NR_WRITE li a0, STDOUT mv a1, sp li a2, 1 ecall # 恢复栈指针 addi sp, sp, 4 ret # puts function puts: puts_loop: # load current char into a0 lb a0,0(a1) # check for end of string beq a1,a3,puts_loop_end # print using putchar jal ra,putchar # add 1 to a1 to move to next char addi a1,a1,1 j puts_loop puts_loop_end: ret .data prompt: .ascii "Enter a message: " prompt_end:
内容的提问来源于stack exchange,提问作者scou1218
相关产品推荐
相关产品推荐

