MIPS汇编time2string子程序地址越界及寄存器保存问题求助
MIPS汇编time2string子程序错误排查与修正
核心错误分析
1. 非法内存访问(地址越界)
主函数已将mytime的数值(如0x5957)加载到$a1作为参数传入,但你在time2string第一行写了lw $a1, 0($a1)——这会把$a1的值当作内存地址去加载数据,而0x5957不是合法内存地址,直接触发地址越界错误。
2. 数据存储方式错误
- 作业要求用
sb(存储字节)保存ASCII字符,但你全程用sw(存储字,4字节),会一次性覆盖4个字节的内存,导致内存破坏。 - 地址偏移错误:字符串每个字符占1字节,存储后应执行
addi $a0, $a0, 1,而非addi $a0, $a0, 4。
3. 寄存器使用错误
- 错误引用
$t0:time2string中直接用$t0提取时间字段,但$t0是调用者保存寄存器,主函数已用它加载mytime地址,子程序未保存就修改会破坏主函数上下文;且应使用传入的参数$a1(时间值),而非$t0。 - 移位指令写错:
srl $t3, $3, 8中的$3是无效寄存器,正确写法为srl $t3, $t3, 8。
4. 逻辑错误
- 冒号处理错误:直接将ASCII冒号(0x3A)加载到
$v0后调用hexasc,但hexasc是将低4位转换为ASCII,0x3A的低4位是0xA,会被转成'A',完全不符合需求——直接用sb存储冒号即可,无需调用hexasc。 - 缺少空终止符:生成的字符串末尾未添加
\0(0x00),不符合C风格字符串要求。
修正后的time2string子程序
time2string: PUSH($ra) # 保存返回地址,因要调用hexasc PUSH($s0) # 保存被调用者保存寄存器$s0,存储原始输出地址 PUSH($s1) # 保存$s1,存储传入的时间值 move $s0, $a0 # 把原始输出地址存到$s0(被调用者保存寄存器,不会被破坏) move $s1, $a1 # 把时间值存到$s1 # 提取分钟高位(NBCD的第15-12位) andi $a0, $s1, 0xF000 srl $a0, $a0, 12 jal hexasc sb $v0, 0($s0) # 存储分钟高位ASCII addi $s0, $s0, 1 # 提取分钟低位(NBCD的第11-8位) andi $a0, $s1, 0x0F00 srl $a0, $a0, 8 jal hexasc sb $v0, 0($s0) addi $s0, $s0, 1 # 存储冒号':' li $t0, 0x3A sb $t0, 0($s0) addi $s0, $s0, 1 # 提取秒高位(NBCD的第7-4位) andi $a0, $s1, 0x00F0 srl $a0, $a0, 4 jal hexasc sb $v0, 0($s0) addi $s0, $s0, 1 # 提取秒低位(NBCD的第3-0位) andi $a0, $s1, 0x000F jal hexasc sb $v0, 0($s0) addi $s0, $s0, 1 # 添加空终止符 li $t0, 0x00 sb $t0, 0($s0) POP($s1) # 恢复寄存器 POP($s0) POP($ra) jr $ra nop
寄存器保存的正确用法(MIPS规范)
MIPS寄存器分为两类,需严格遵守调用约定:
- 调用者保存寄存器($t0-$t9, $a0-$a3, $v0-$v1):
- 子程序可直接修改,无需保存;若主函数需保留这些值,由主函数负责保存。
- 被调用者保存寄存器($s0-$s7, $ra, $fp):
- 子程序若要修改这些寄存器,必须先用
PUSH保存到栈中,执行完逻辑后用POP恢复,否则会破坏调用者上下文。
- 子程序若要修改这些寄存器,必须先用
在你的场景中:
- 必须保存
$ra:因为time2string要调用hexasc,jal会覆盖$ra的值,所以必须先保存。 - 使用
$s0-$s1存储输出地址和时间值:这两个值需要在多次调用hexasc后保持不变,而hexasc可能修改$a0等调用者保存寄存器,用被调用者保存寄存器更安全,且需要保存/恢复。
内容的提问来源于stack exchange,提问作者Hazof
相关产品推荐
相关产品推荐

