Reputation: 119
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
Upvotes: 0
Views: 430
Reputation: 26646
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.
Upvotes: 1