I'm currently having trouble writing this recursive factorial assembly code in MIPS. I do not want to change the format of this code, as it is for a project, but I want to understand how to store or call $ra so that it will return to "branch:" in order to output the calculated factorial.
##
##
.data
prompt1: .asciiz "\nPlease enter an integer: \n"
negativePrompt: .asciiz "\nYour input must be greater than or equal to 0 in order to function.\n"
factorialIs: .asciiz "\nThe factorial of your input number is: \n"
.globl main
.text
main:
subu $sp, $sp, 32 # push memory for 8 values
sw $ra, 20($sp) # save return address
sw $fp, 16($sp) # save old frame pointer
addiu $fp, $sp, 28 # set up frame pointer
branch:
li $v0, 4 # load macro for print_str
la $a0, prompt1 # pass argument to string
syscall # print string prompt1
li $v0, 5 # load macro for read_int
syscall # get N value from user
move $a0, $v0 # store N as arg
blt $a0, 0, negBranch # if N is negative, handles case
jal factorial # calls factorial function
li $v0, 4 # macro for print string
la $a0, factorialIs # print string
syscall
li $v0, 1 # macro for print int
move $a0, $v0 # print output int in v0
syscall
lw $ra, 20($sp) # restore return address of caller
lw $fp, 16($sp) # restore frame pointer
addiu $sp, $sp, 32 # pop stack
jr $ra # return to caller
#exit:
# "would you like to calculate again?"
# li $v0, 10 # load macro to exit
# syscall # execute exit
negBranch:
li $v0, 4 # load macro for print_str
la $a0, negativePrompt # pass argument to string
syscall # print string prompt1
li $t0, 0 # initialize input value to 0
j branch # ask user to input new number
factorial:
subu $sp, $sp, 32 # pushes memory for variables
sw $ra, 12($sp) # stores return address of recursion
sw $fp, 8($sp) # save frame pointer
addiu $fp, $sp, 28 # set up frame pointer
sw $a0, 0($fp) # stores n in frame pointer
lw $v0, 0($fp)
bgtz $v0, zeroBranch # if N is greater than 0, recurse
li $v0, 1 # return 1
jr end
zeroBranch:
lw $v1, 0($fp) # load n value
subu $v0, $v1, 1 # v0 = n - 1
move $a0, $v0 # a0 = v0 = n - 1
jal factorial # recursive function call
lw $v1, 0($fp) # load original n value to v1
mul $v0, $v0, $v1 # v0 = n * fact(n-1)
end:
lw $ra, 12($sp) # loads return address of main call
## this ^ line does not access the address from "jal factorial"
lw $fp, 16($sp) # loads initial n value
addiu $sp, $sp, 32 # collapses stack frame
jr $ra # return to main then exit
When I enter factorial(1), it functions recursively as expected; however, when I get to my "end:" branch, the return address is not returning back to my "branch:" branch, which would output the result of the factorial input.
Why do you think that the function should return to label branch:
— that is essentially incorrect and so you need to rethink this. The function should return to the instruction immediately after jal
.
This code has assembly time errors, so I don't know how you're able to run it.
jr end
is not a valid instruction — I think you want j end
there.That code is messing up the $fp
register in epilogue of factorial
— the old $fp
is saved at 8($sp)
but restored from 16($sp)
, so it doesn't come back to where the caller needs it to be. Change the restore to use 8($sp)
.
The syscall sequence used to print the factorial result in main
is:
li $v0, 1 # macro for print int
move $a0, $v0 # print output int in v0
Can you see how these two lines have clobbered the value in $v0
that you want to print, so will always print 1
?
But it's worse than that, since you also print a prompt (syscall #4, a few lines prior), that moves a 4 (syscall code for print string) into $v0
wiping out the function return value.
So, what to do is: immediately after the jal
instruction in main
, copy the return value $v0
, in a different location — suggest $t0
as it will survive a syscall
(if it was a function call then would suggest memory or $s0
instead).
Then, at the syscall #1 for print integer copy $t0
into $a0
for the parameter.
You can see all of this by using single step in the debugger. Debugging is a critical skill and anyone attempting assembly language programming should have it or acquire it asap. Single step and watch each line, verify the program state (registers and memory) in between each line.