You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MIPS游戏开发:如何获取并修改字符地址实现WASD移动?

MIPS游戏开发:WASD控制字符移动问题

我正在用MIPS开发一款游戏,已经能成功打印字符,但不知道怎么让字符在屏幕上移动,想用WASD按键实现控制。

初始代码

controls:
    switchCase:
    
    li $v0, 12 # input for controls
    syscall
    move $a0, $v0
    
    la $t6, leftArrow
    lb $t6, ($t6)
    beq $a0, $t6, moveLeft
    
    la $t6, rightArrow
    lb $t6, ($t6)
    beq $a0, $t6, moveRight
    
    la $t6, upArrow
    lb $t6, ($t6)
    beq $a0, $t6, moveUp
    
    la $t6, downArrow  
    lb $t6, ($t6)
    beq $a0, $t6, moveDown
    
moveLeft:
    
    addi $t5, $t5, -4
    
    j switchCase
    #jr $ra

moveRight:
    
    addi $t5, $t5, 4
    
    j switchCase
    #jr $ra

moveUp:
    
    addi $t5, $t5, -16
    j switchCase
    #jr $ra
    
moveDown: 
    
    addi $t5, $t5, 16
    j switchCase
    #jr $ra

问题现象

编译运行后,程序只显示按下的按键,字符完全没移动(字符存在$t5寄存器里)。分步汇编时会卡在syscall 12指令处。我猜需要获取$t5的地址,同时不确定移动函数的代码是否正确。

更新后的代码

controls:

li $t8, 8
li $s5, 15

switchCase:

jal rowMajor
# addy in s5
input:
li $v0, 12 # input for controls
syscall
move $a0, $v0

la $t6, leftArrow
lb $t6, ($t6)
beq $a0, $t6, moveLeft

la $t6, rightArrow
lb $t6, ($t6)
beq $a0, $t6, moveRight

la $t6, upArrow
lb $t6, ($t6)
beq $a0, $t6, moveUp

la $t6, downArrow  
lb $t6, ($t6)
beq $a0, $t6, moveDown

moveLeft:
 

addi $s5, $s5, -4



j input
#jr $ra

moveRight:

addi $s5, $s5, 4

j input
#jr $ra

moveUp:

addi $s5, $s5, -16
j input
#jr $ra

moveDown: 

addi $s5, $s5, 16
j input
#jr $ra

核心问题与修复方案

  1. 阻塞输入导致的卡顿:syscall 12是阻塞式读字符且会回显输入,这会让程序停在输入步骤直到按键,还会干扰屏幕显示。改用syscall 11(无回显读字符,MARS/SPIM等模拟器支持非阻塞,未按键时返回0),或者结合中断实现实时输入。

  2. 缺少屏幕重绘逻辑:你只修改了位置寄存器的值,但没有更新屏幕——必须先清除原位置的字符(用空格覆盖),再在新位置绘制目标字符,否则旧字符会残留,看起来没移动。

  3. 位置偏移的合理性:

    • 从±16的偏移看,你假设屏幕每行16个字符,这个逻辑没问题;但左右移动用±4可能错误,正常字符在显存中按字节存储,左右移动应偏移±1,除非显存按4字节对齐存储每个字符,需根据实际布局调整。
    • 必须添加边界检测,防止字符移出屏幕范围(比如左移不能小于0,右移不能超过总字符数-1,上下移动不能超出行数限制)。
  4. 寄存器使用规范:$t系列是临时寄存器,可能被jal调用的函数(比如rowMajor)破坏,用$s系列寄存器保存位置(你更新后的$s5是正确选择),但要遵循MIPS调用约定,调用函数前将$s寄存器保存到栈中。

关键修复代码片段

# 假设显存起始地址为0x10008000(MARS默认),目标字符为'X',屏幕为8行×16列
la $t0, 0x10008000   # 显存起始地址
li $s5, 15           # 初始位置索引

main_loop:
    # 非阻塞读字符(未按键返回0)
    li $v0, 11
    syscall
    beq $v0, $zero, redraw  # 无按键直接重绘
    
    # 处理WASD按键(用ASCII值直接判断)
    move $t6, $v0
    li $t7, 'a'
    beq $t6, $t7, moveLeft
    li $t7, 'd'
    beq $t6, $t7, moveRight
    li $t7, 'w'
    beq $t6, $t7, moveUp
    li $t7, 's'
    beq $t6, $t7, moveDown
    j main_loop

moveLeft:
    bgtz $s5, left_valid  # 边界检测:不能小于0
    j main_loop
left_valid:
    addi $s5, $s5, -1
    j main_loop

moveRight:
    li $t7, 127          # 8×16-1=127,屏幕最大位置索引
    blt $s5, $t7, right_valid
    j main_loop
right_valid:
    addi $s5, $s5, 1
    j main_loop

moveUp:
    bge $s5, 16, up_valid  # 上移一行,至少在第二行
    j main_loop
up_valid:
    addi $s5, $s5, -16
    j main_loop

moveDown:
    li $t7, 111          # 最后一行起始索引为112,下移后不能超过127
    ble $s5, $t7, down_valid
    j main_loop
down_valid:
    addi $s5, $s5, 16
    j main_loop

redraw:
    # 清除原位置字符
    add $t1, $t0, $s5
    li $t2, ' '
    sb $t2, ($t1)
    # 在新位置绘制目标字符
    li $t2, 'X'
    sb $t2, ($t1)
    j main_loop

额外提示

  • 避免使用箭头键:特殊按键的ASCII值因模拟器而异,WASD的ASCII码固定,更易处理。
  • 显存地址确认:不同模拟器的显存起始地址可能不同,需提前确认。
  • 中断处理:如果需要更流畅的移动,可配置键盘中断,让程序在按键时触发移动逻辑,而非轮询输入。

内容的提问来源于stack exchange,提问作者dominic99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 15:40:49