MIPS汇编测验程序定时器问题:计时时无法输入答案
问题原因分析
你的程序当前逻辑是先启动阻塞式的定时器循环,完全占用CPU执行计时,直到倒计时结束后才会执行用户输入代码。这导致计时的10秒内,程序一直卡在timer_loop和delay_loop中,根本没有机会处理用户输入操作,自然无法作答。
解决方案:将计时与输入检查合并
要实现"计时同时允许输入"的效果,需要把计时逻辑和输入检查逻辑合并到同一个循环中:每经过一小段时间就检查一次是否有用户输入,有输入则立即处理并退出计时;无输入则继续倒计时,直到时间耗尽。
针对MARS模拟器,我们可以使用syscall 3非阻塞检查键盘是否有输入(返回值存于$v0,1表示有输入,0表示无),同时调整计时粒度,避免长时间阻塞。
修改后的完整代码
.data welcomeMsg: .asciiz "Welcome to the Quiz Game!\nPlease choose a subject to play:\n1. Math\n2. English\n3. History\n4. Soccer Sport\n" chooseMsg: .asciiz "Enter your choice (1 for Math, 2 for English, 3 for History, 4 for Soccer Sport): " invalidChoiceMsg: .asciiz "Invalid choice. Please enter a number between 1 and 4.\n" mathWelcomeMsg: .asciiz "You chose Math! Let's solve some equations.\n" englishWelcomeMsg: .asciiz "You chose English! Let's work on some exercises.\n" historyWelcomeMsg: .asciiz "You chose History! Let's dive into the past.\n" soccerWelcomeMsg: .asciiz "You chose Soccer Sport! Let's test your knowledge of football.\n" questionMath: .asciiz "What is 5 + 3?\n" answerMath: .asciiz "8\n" questionEnglish: .asciiz "Correct the following sentence: 'He go to school every day.'\n" answerEnglish: .asciiz "He goes to school every day." questionHistory: .asciiz "In which year did the American Civil War end?\n" answerHistory: .word 1865 questionSoccer: .asciiz "Who is known as the 'King of Football'?\n" answerSoccer: .asciiz "Pele\n" userInputBuffer: .space 100 positiveFeedback: .asciiz "Correct answer! You earned 20 points.\n" incorrectFeedback: .asciiz "Incorrect answer. The correct answer is " incorrectFeedbackEnd: .asciiz ".\n" timeUpMsg: .asciiz "Time's up! Please try again.\n" .text .globl main main: li $v0, 4 la $a0, welcomeMsg syscall li $v0, 4 la $a0, chooseMsg syscall li $v0, 5 syscall move $t0, $v0 li $t1, 1 li $t2, 2 li $t3, 3 li $t4, 4 beq $t0, $t1, math_quiz beq $t0, $t2, english_quiz beq $t0, $t3, history_quiz beq $t0, $t4, soccer_quiz li $v0, 4 la $a0, invalidChoiceMsg syscall j main math_quiz: li $v0, 4 la $a0, mathWelcomeMsg syscall li $v0, 4 la $a0, questionMath syscall # 启动带输入检查的定时器(10秒),处理字符串输入 li $a0, 10 la $a1, userInputBuffer li $a2, 100 jal timer_with_str_input # 比较答案 la $a0, userInputBuffer la $a1, answerMath jal strcmp beq $v0, $zero, correct_answer_math j incorrect_answer_math correct_answer_math: li $v0, 4 la $a0, positiveFeedback syscall j finish incorrect_answer_math: li $v0, 4 la $a0, incorrectFeedback syscall li $v0, 4 la $a0, answerMath syscall li $v0, 4 la $a0, incorrectFeedbackEnd syscall j finish english_quiz: li $v0, 4 la $a0, englishWelcomeMsg syscall li $v0, 4 la $a0, questionEnglish syscall # 启动带输入检查的定时器(10秒),处理字符串输入 li $a0, 10 la $a1, userInputBuffer li $a2, 100 jal timer_with_str_input # 比较答案 la $a0, userInputBuffer la $a1, answerEnglish jal strcmp beq $v0, $zero, correct_answer_english j incorrect_answer_english correct_answer_english: li $v0, 4 la $a0, positiveFeedback syscall j finish incorrect_answer_english: li $v0, 4 la $a0, incorrectFeedback syscall li $v0, 4 la $a0, answerEnglish syscall li $v0, 4 la $a0, incorrectFeedbackEnd syscall j finish history_quiz: li $v0, 4 la $a0, historyWelcomeMsg syscall li $v0, 4 la $a0, questionHistory syscall # 启动带输入检查的定时器(10秒),处理整数输入 li $a0, 10 jal timer_with_int_input move $t5, $v0 # 存储用户输入的整数 # 加载正确答案 lw $t6, answerHistory # 比较答案 bne $t5, $t6, incorrect_answer_history j correct_answer_history correct_answer_history: li $v0, 4 la $a0, positiveFeedback syscall j finish incorrect_answer_history: li $v0, 4 la $a0, incorrectFeedback syscall li $v0, 1 lw $a0, answerHistory syscall li $v0, 4 la $a0, incorrectFeedbackEnd syscall j finish soccer_quiz: li $v0, 4 la $a0, soccerWelcomeMsg syscall li $v0, 4 la $a0, questionSoccer syscall # 启动带输入检查的定时器(10秒),处理字符串输入 li $a0, 10 la $a1, userInputBuffer li $a2, 100 jal timer_with_str_input # 比较答案 la $a0, userInputBuffer la $a1, answerSoccer jal strcmp beq $v0, $zero, correct_answer_soccer j incorrect_answer_soccer correct_answer_soccer: li $v0, 4 la $a0, positiveFeedback syscall j finish incorrect_answer_soccer: li $v0, 4 la $a0, incorrectFeedback syscall li $v0, 4 la $a0, answerSoccer syscall li $v0, 4 la $a0, incorrectFeedbackEnd syscall j finish strcmp: # 比较两个空终止字符串,相等返回0,否则返回1 li $t1, 0 compare_loop: lb $t2, 0($a0) lb $t3, 0($a1) beq $t2, $zero, compare_end bne $t2, $t3, compare_fail addi $a0, $a0, 1 addi $a1, $a1, 1 j compare_loop compare_fail: li $v0, 1 jr $ra compare_end: li $v0, 0 jr $ra # 带字符串输入检查的定时器:$a0=倒计时秒数,$a1=输入缓冲区地址,$a2=缓冲区大小 timer_with_str_input: move $t1, $a0 # 存储总秒数 li $t2, 1000 # 每1ms检查一次输入,调整这个值适配你的环境 timer_str_loop: blez $t1, timer_end # 时间到,结束 # 检查是否有键盘输入 li $v0, 3 syscall beq $v0, 1, read_str_input # 有输入则读取 # 1ms延迟 li $t0, 0 delay_str: addi $t0, $t0, 1 bne $t0, $t2, delay_str # 每1000次1ms延迟,总时间减1秒 subi $t1, $t1, 1 j timer_str_loop read_str_input: # 读取用户字符串输入 li $v0, 8 move $a0, $a1 move $a1, $a2 syscall jr $ra # 带整数输入检查的定时器:$a0=倒计时秒数,返回用户输入的整数到$v0 timer_with_int_input: move $t1, $a0 # 存储总秒数 li $t2, 1000 # 每1ms检查一次输入,调整这个值适配你的环境 timer_int_loop: blez $t1, timer_end # 时间到,结束 # 检查是否有键盘输入 li $v0, 3 syscall beq $v0, 1, read_int_input # 有输入则读取 # 1ms延迟 li $t0, 0 delay_int: addi $t0, $t0, 1 bne $t0, $t2, delay_int # 每1000次1ms延迟,总时间减1秒 subi $t1, $t1, 1 j timer_int_loop read_int_input: # 读取用户整数输入 li $v0, 5 syscall jr $ra timer_end: li $v0, 4 la $a0, timeUpMsg syscall j finish # 跳转到程序结束 finish: li $v0, 10 # 退出程序 syscall
关键修改说明
- 替换阻塞式定时器为带输入检查的循环:
- 新增
timer_with_str_input和timer_with_int_input两个函数,分别处理字符串输入和整数输入的计时场景。 - 使用
syscall 3非阻塞检查键盘输入,每1ms检查一次,避免长时间占用CPU。
- 新增
- 调整计时逻辑:将原来的1秒大延迟拆分为1ms的小延迟,累计1000次小延迟后减少1秒倒计时,同时在每次小延迟后检查输入。
- 输入触发逻辑:一旦检测到有键盘输入,立即执行对应的输入读取操作(字符串或整数),并返回继续处理答案判断。
内容的提问来源于stack exchange,提问作者Diego Manríquez
相关产品推荐
相关产品推荐

