在我的 MIPS 程序中,地址并不总是增加 4
下面的 MIPS 代码应该编写一个函数 swapbigsmall
,它使用嵌套函数 findloc
来查找该数组中最大和最小数字的位置,并交换它们。但是当我运行该程序时,findloc 错误地增加了地址,导致 v0 可能等于不是 4 的倍数的数字,从而产生错误地址异常。如何确保地址始终增加 4?
A: .word 90, 2, 93, 66, 8, 120, 121, 11, 33, 9
.text
.globl main
main: la $a0, A
li $a1, 10
jal swapbigsmall
done: li $v0, 10
syscall
swapbigsmall:
#Creates 3 spaces in stack,
#then stores registers
addi $sp, $sp, -12
sw $ra, 8($sp)
sw $ra, 4($sp)
sw $ra, 0($sp)
jal findloc #Find smallest
#Stores first result in s1
#This is where exception occurs
lw $s1, 0($v0)
#Finds and stores largest
li $a2, 1
jal findloc
lw $s2, 0($v0)
#Swap code
lw $t3, 0($s1)
lw $t4, 0($s2)
sw $t3, 0($s2)
sw $t4, 0($s1)
#Function conclusion
lw $ra, 8($sp)
lw $s2, 4($sp)
lw $s1, 0($sp)
add $sp, $sp, 12
jr $ra
findloc:
li $t0, 0
#t9 = A[0]
lw $t9, 0($a0)
blt $a2, 1, LOOPSM
#t1 = A[i]
LOOPL: sll $t1, $t0, 2
add $t1, $t1, $a0
lw $t1, 0($t1)
sgt $t2, $t9, $t1
bne $t2, $0, LOOP_NEXTL
addi $t9, $t1, 0
sll $v0, $t0, 2
LOOP_NEXTL: addi $t0, $t0, 1
bne $t0, $a1, LOOPL
j Exit
LOOPSM: sll $t1, $t0, 2
add $t1, $t1, $a0
lw $t1, 0($t1)
slt $t2, $t9, $t1
bne $t2, $0, LOOP_NEXTSM
addi $t9, $t1, 0
sll $v0, $t0, 2
LOOP_NEXTSM: addi $t0, $t0, 1
bne $t0, $a1, LOOPL
EXIT:
jr $ra
The MIPS code below is supposed to write a function swapbigsmall
that uses nested function findloc
to find the location of the biggest and smallest numbers in this array, and swap them. But when I run the program, findloc increments the address incorrectly, causing v0 to potentially equal a number that isn't a multiple of 4, creating a bad address exception. How can I make sure that the address goes up by 4 at all times?
A: .word 90, 2, 93, 66, 8, 120, 121, 11, 33, 9
.text
.globl main
main: la $a0, A
li $a1, 10
jal swapbigsmall
done: li $v0, 10
syscall
swapbigsmall:
#Creates 3 spaces in stack,
#then stores registers
addi $sp, $sp, -12
sw $ra, 8($sp)
sw $ra, 4($sp)
sw $ra, 0($sp)
jal findloc #Find smallest
#Stores first result in s1
#This is where exception occurs
lw $s1, 0($v0)
#Finds and stores largest
li $a2, 1
jal findloc
lw $s2, 0($v0)
#Swap code
lw $t3, 0($s1)
lw $t4, 0($s2)
sw $t3, 0($s2)
sw $t4, 0($s1)
#Function conclusion
lw $ra, 8($sp)
lw $s2, 4($sp)
lw $s1, 0($sp)
add $sp, $sp, 12
jr $ra
findloc:
li $t0, 0
#t9 = A[0]
lw $t9, 0($a0)
blt $a2, 1, LOOPSM
#t1 = A[i]
LOOPL: sll $t1, $t0, 2
add $t1, $t1, $a0
lw $t1, 0($t1)
sgt $t2, $t9, $t1
bne $t2, $0, LOOP_NEXTL
addi $t9, $t1, 0
sll $v0, $t0, 2
LOOP_NEXTL: addi $t0, $t0, 1
bne $t0, $a1, LOOPL
j Exit
LOOPSM: sll $t1, $t0, 2
add $t1, $t1, $a0
lw $t1, 0($t1)
slt $t2, $t9, $t1
bne $t2, $0, LOOP_NEXTSM
addi $t9, $t1, 0
sll $v0, $t0, 2
LOOP_NEXTSM: addi $t0, $t0, 1
bne $t0, $a1, LOOPL
EXIT:
jr $ra
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当您在汇编语言中遇到错误时,您不应该假设您知道重点在哪里或出了什么问题,因为任何小打字错误都可能对执行产生戏剧性或意外的影响。
所以,这是您学习调试的机会。
模拟器具有单步功能,您应该使用它来验证每条指令是否执行其应执行的操作:您期望它执行的操作。
如果任何一条指令错误,程序将无法运行,也不会挂起。
您需要特别小心系统调用和
jal
调用。如果被调用函数(被调用者)扰乱了调用者的期望,那么当被调用者返回到调用者时,调用者将无法工作。当您拨打电话时,请验证每个参数。 当调用返回时,验证堆栈指针是否已返回到其原始位置,并且所有寄存器都具有预期值。寄存器在调用者和被调用者之间共享,因此从抽象意义上讲,在单步执行函数调用时需要验证更多内容。 (不过,要明确的是,被调用者中破坏调用者期望的错误可以在被调用者中找到;但有时,只需跳过调用就可以让您快速了解正在破坏的内容。)
就像 C 或其他语言一样调试时,我们可以单步调试“向前”,也可以使用“除法”“向后”调试。征服以找到代码中出现问题的点。
我更喜欢转发新代码,因为错误的影响不会复合。所有新代码都应该至少向前运行一次。
然而,使用divide & 逆向工作征服有时可以很好地快速找到故障原因 - 其想法是从故障/异常返回代码并确定错误时的原因。通常这是一个迭代过程,因为逆向工作发现的问题不一定是错误,并且您必须继续逆向工作才能找到真正的错误。
When you have bugs in assembly language, you should not assume you know where to focus or what's going wrong, as any little typo can have a dramatic or unexpected effect on the execution.
So, this is your opportunity to learn debugging.
The simulators have a single step feature, and you are supposed to use it to verify that each and every instruction does what it is supposed to do: what you expect it to do.
If any one instruction is wrong, the program won't work, it won't hang together.
You need to be especially careful of syscalls and
jal
calls. If the called function, the callee, messes up the expectations of the caller, then the caller just won't work when the callee returns to the caller.When you're making a call, verify every parameter. When the call comes back, verify that the stack pointer has returned to its original position, and that all the registers have the expected values. The registers are shared between caller and callee, so in the abstract sense, there's more to verify when stepping over a function call. (Bugs in the callee that stomp on the caller's expectations can be found in the callee, though, to be clear; but sometimes just stepping over the call may give you quick insight as to what's being clobbered.)
Just like with C or other language debugging, we can debug "forwards" with single step or debug "backwards" using divide & conquer to find the point in the code where something went wrong.
I prefer forwards for new code, since the effect of mistakes won't compound. All new code should be run forwards at least once.
However, working backwards with divide & conquer can sometimes work well to quickly find the cause of a fault — the idea being to go back in the code from a fault/exception and determine what when wrong. Usually that is an iterative processes because working backwards the issues you find are not necessarily the bugs, and you have to keep working backwards to find the real bugs.