I have a mini bank program written that goes calls multiple functions inside the subrouting deposit, this is the subroutine
deposit:
addi $sp, $sp, -8 #save space on stack
addi $s3, $0, 1 #trigger s3
sw $s3, 0($sp)
sw $ra, 4($sp)
.....
jal AsciiConvert #convert ascii of deposited amount to integer
beq $v0, $0, Err_ACC #if no value to be deposited was inputed print error message
beq $t0, $0, deposit_checking #if check exists returns a 0
beq $t0, 1, deposit_saving #if check exists returns a 1
jal printarray
lw $s3, 0($sp)
lw $ra, 4($sp) # reload $ra so we can return to caller
addi $sp, $sp, 8 # restore $sp, freeing the allocated space
jr $ra
deposit_checking:
... arithmetic operations...
jr $ra
the ascii convert subroutine:
AsciiConvert:
...normal arithemtics...
j ConvertOP
ConvertOP:
lb $s0, 0($a1)
beq $s0, $0, endConvert #end at null terminating
beq $s0,32,endConvert #if found a space
addi $s0, $s0, -48 #convert to int
mul $s2, $s1, 10 #multiply sum by 10
add $s2, $s2, $s0 #sum = sum + previous number
add $s1, $s2, $0 #s1 holds previous value
addi $a1, $a1, 1 #increment adress
add $v0, $s2, $0 #store the number in the return adress
j ConvertOP
endConvert:
jr $ra
When I go into deposit, I jal AsciiConvert and then I go into the deposit_Checking subroutine, however the return address of that deposit_Checking returns me back to the line of jal AsciiConvert and not to the line where I called the deposit_Checking subroutine, leading to an infinite loop between Ascii convert subroutine and deposit_Checking subroutine...can someone please help me?
deposit_checking
looks like a subroutine, and you identify it as a subroutine in your post, but, we don't enter subroutines with beq
instruction, you're supposed to use jal
to call a subroutine.
In machine code, the return address, $ra
for MIPS, is effectively a parameter to the subroutine — it tells the subroutine where to resume execution in the caller, where to return to. There are several ways to set the $ra
register with a meaningful return address, though of course jal
is by far the most common way.
beq
transfers control of the processor to the target label (when eq is true) by changing the program counter (pc) though does not provide a (new) $ra
value.
By not setting $ra
to a new value, its old value is retained. Since the $ra
register was last set by the jal AsciiConvert
, the jr $ra
for the other function goes back there, none the wiser that this was not the right call — as it is the caller's job to set that parameter properly.
And while some instruction sets allow calling on a condition, we wouldn't necessarily want all beq
s to capture a return address, because then any function that used beq
would have to concern itself with preserving $ra
.
Let's also note that these behaviors are all visible during debugging. jr $ra
, for example will transfer control to whatever machine instruction is addressed by the value in the $ra
register. When you first enter a subroutine/function, there should be a proper return address parameter provided in $ra
register, and by the time you get to the end of the function with its final instruction jr $ra
, if the value in the $ra
register's value has changed from entry, then it won't go back to where it was called from — so we'd be looking for somewhere in between that changes $ra
, without restoring it back.