Recursive procedure in MIPS - stack? - recursion

I have to create a procedure eval_expr in MIPS. Basically, it performs basic operations like a calculator, so if an argument in address $a0 is positive then it stores a number, if it is negative, the operation is performed and it can be either:
-80, then addition is performed
-60, then subtraction is performed
-40, then we shift right
-20, then we shift left
$a0 + 4 stores the address of left sub-expression
$a0 + 8 stores the address of right sub-expression
Using pseudo-code, we can write this procedure as:
int eval_expr (expr e) {
if (is_number(e)) {
return e.op;
}
else {
if (e == -80)
return eval_expr(e.left) + eval_expr(e. right);
if (e == -60)
return eval_expr(e.left) - eval_expr(e.right);
if (e == -40)
return eval_expr(e.left) >> eval_expr(e.right);
if (e == -20)
return eval_expr(e.left) << eval_expr(e.right);
}
}
Here's the code I got so far:
eval_expr:
#stack frame
addi $sp $sp -24
sw $ra 4($sp)
sw $fp 8($sp)
addiu $fp $sp 20
#if positive
lw $t1 ($a0)
slti $t0 $t1 0
bne $t0 $zero else
add $v0 $t1 $zero
j destack
#if negative
else:
addi $t3 $zero -20
addi $t4 $zero -40
addi $t5 $zero -60
addi $t6 $zero -80
beq $a0 $t3 sl
beq $a0 $t4 sr
beq $a0 $t5 subs
beq $a0 $t6 addit
sl:
sw $a0 ($sp)
lw $a0 4($a0)
jal eval_expr
lw $a0 ($sp)
sw $v0 12($sp)
lw $a0 8($a0)
jal eval_expr
lw $t0 12($sp)
sllv $v0 $t0 $v0
j destack
sr:
sw $a0 ($sp)
lw $a0 4($a0)
jal eval_expr
lw $a0 ($sp)
sw $v0 12($sp)
lw $a0 8($a0)
jal eval_expr
lw $t0 12($sp)
srlv $v0 $t0 $v0
j destack
subs:
sw $a0 ($sp)
lw $a0 4($a0)
jal eval_expr
lw $a0 ($sp)
sw $v0 12($sp)
lw $a0 8($a0)
jal eval_expr
lw $t0 12($sp)
sub $v0 $t0 $v0
j destack
addit:
sw $a0 ($sp)
lw $a0 4($a0)
jal eval_expr
lw $a0 ($sp)
sw $v0 12($sp)
lw $a0 8($a0)
jal eval_expr
lw $t0 12($sp)
add $v0 $t0 $v0
j destack
#destacking
destack:
lw $a0 ($sp)
lw $ra 4($sp)
lw $fp 8($sp)
addi $sp $sp 24
jr $ra
Unfortunately it only works if the operation performed is shifting left. I think this is because this part of the code is not jumping anywhere (which indicated some sort of problem with stack?), therefore always shifting left is performed.
beq $a0 $t3 sl
beq $a0 $t4 sr
beq $a0 $t5 subs
beq $a0 $t6 addit
Here are all the tests I ran:
Evaluate : (2<<2)
** Test passed ** Expected : 8 Received : 8
Evaluate : (1<<(2+(3-4)))
!! TEST FAILED !! Expected : 2 Received : 1
Evaluate : (((2<<2)+(2<<2))<<((2<<2)+(2<<2)))
!! TEST FAILED !! Expected : 1048576 Received : 2048
Evaluate : ((((1<<(2+(3-4)))<<3)-4)-(((2<<2)+(2<<2))<<((2<<2)+(2<<2))))
!! TEST FAILED !! Expected : -1048564 Received : 128
Evaluate : (((((1<<(2+(3-4)))<<3)-4)-(((2<<2)+(2<<2))<<((2<<2)+(2<<2))))>>5)
!! TEST FAILED !! Expected : -32768 Received : 4096
Evaluate : (((1+2)+((1<<(2+(3-4)))<<3))-(((((1<<(2+(3-4)))<<3)-4)-(((2<<2)+(2<<2))<<((2<<2)+(2<<2))))>>5))
!! TEST FAILED !! Expected : 32787 Received : 1024

All right, if anyone is interested in answer - I was loading only an address, not an actual value (from register $a0). Here's the corrected version of the code:
eval_expr:
#stack frame
addi $sp $sp -24
sw $ra 4($sp)
sw $fp 8($sp)
addiu $fp $sp 20
#if positive
lw $t1 ($a0)
slti $t0 $t1 0
bne $t0 $zero else
add $v0 $t1 $zero
j destack
#if negative
else:
sw $a0 16($sp)
lw $a0 4($a0)
jal eval_expr
lw $a0 16($sp)
sw $v0 20($sp)
lw $a0 8($a0)
jal eval_expr
lw $t0 20($sp)
lw $a0 16($sp)
add $t1 $a0 $zero
lw $a0 ($a0)
addi $t3 $zero -20
beq $a0 $t3 sl
addi $t3 $zero -40
beq $a0 $t3 sr
addi $t3 $zero -60
beq $a0 $t3 subs
addi $t3 $zero -80
beq $a0 $t3 addit
#operations
sl:
sllv $v0 $t0 $v0
j destack
sr:
srav $v0 $t0 $v0
j destack
addit:
add $v0 $t0 $v0
j destack
subs:
sub $v0 $t0 $v0
j destack
#destacking
destack:
lw $a0 ($sp)
lw $ra 4($sp)
lw $fp 8($sp)
addi $sp $sp 24
jr $ra

Related

Fibonacci recursion function in Mips

I am trying to pratice how recursion works in Mips. So I tried to write a fibonacci function fib.
At first I had addi $a0, $a0, n to write a general solution, but I thought that if I want to check my results in Qtspim , maybe i need to add a real number as an argument. I do not want a full answer , if the thought behind the code is wrong, but some help in order to run it in Qtspim, and find my mistakes (logic mistakes) on my own
This is my code:
.globl main
.text
main:
addi $a0, $a0, 4
fib:
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slti $t0, $a0, 1
beq $t0, 1, L2 #if n<1
beq $a0, 1, L2 # if n=1
beq $t0, 0, L1 # if n>1
#what to do when n<=1
L2:
addi $v0, $v0, 1
jr $ra
#what to do when n>1
L1:
addi $a0, $a0, -1
jal fib
lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
lw $t1, 0($v0)
add $v0, $t1, $v0
jr $ra
li $v0,10
syscall
I get an error message as such:
Bad address in data/stack read: 0x00000000
.text
main:
addi $a0, $a0, 4
#### you've place fib inline inside main,
#### you should have all of main here, and "call" fib using jal instruction
#### and the syscall for exit goes up here as well
fib:
addi $sp, $sp, -8 # you will want one more word of stack space
sw $ra, 4($sp)
sw $a0, 0($sp)
slti $t0, $a0, 1 # i would have use 2 here instead of 1
beq $t0, 1, L2 #if n<1 # this is ok, but better to use bne $t0, $0, L2
beq $a0, 1, L2 # if n=1 # and then this would not be needed
beq $t0, 0, L1 # if n>1 # this doesn't need to be conditional, if the program reaches here then L1 is the thing to do next.
#what to do when n<=1
L2:
addi $v0, $v0, 1 # here you want to return just 1 not v0+1
jr $ra
#what to do when n>1
L1:
addi $a0, $a0, -1
jal fib # fib(n-1), good
lw $a0, 0($sp) # this reloads $a0 the original n
lw $ra, 4($sp)
addi $sp, $sp, 8
lw $t1, 0($v0) # after a call to fib $v0 holds fib(n)
# an integer value but you're treating it like a pointer and dereferencing it
add $v0, $t1, $v0 # here doing fib(n-1) + n
# you want fib(n-1) + fib(n-2) instead
# so you're missing a fib(n-2)
jr $ra
li $v0,10 # this is part of main, so move it to where main is
syscall # realize that code located here is unreachable (aka dead)
# anything after an unconditional branch (here the jr $ra just above)
# and without a label is very suspicious as unreachable
update , some improved (i hope ) code
.globl main
.text
main:
addi $a0, $a0, 4
jal fib
li $v0, 10
syscall
fib:
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slti $t0, $a0, 2
beq $t0, 1, L2
#what to do when n<=1
L2:
addi $v0, $v0, 1
jr $ra
#what to do when n>1
L1:
addi $a0, $a0, -1 # a0 -> n-1
jal fib # fib(n-1)
lw $a0, 0($sp) #load word from memory adress 0($sp) to register $a0
lw $ra, 4($sp) #load word from memory adress 4($sp) to register $ra
addi $sp, $sp, 8
add $t1, $zero, $v1 # in register $t1 store current $v1
add $v0, $t2, $v0 # v0_ n+1 =v0 _n + v0_n-1
add $t2, $t1, $zero # in $t2 save the previous value of v1
.data
prompt: .ascii "Fibonacci Program\n"
.asciiz "Enter N value: "
results: .asciiz "\nFibonacci of N = "
n: .word 0
answer: .word 0
.text
.globl main
.ent main
main:
# Read n value from user
li $v0, 4 # print prompt string
la $a0, prompt
syscall
li $v0, 5 # read N (as integer)
syscall
sw $v0, n
# Call Fibonacci function.
lw $a0, n
jal fib
sw $v0, answer
# Display result
li $v0, 4 # print prompt string
la $a0, results
syscall
li $v0, 1 # print integer
lw $a0, answer
syscall
# Done, terminate program.
li $v0, 10 # terminate
syscall # system call
.end main
# Fibonacci function
# Recursive definition:
# = 0 if n = 0
# = 1 if n = 1
# = fib(n-1) + fib(n-2) if n > 2
# Arguments
# $a0 - n
# Returns
# $v0 set to fib(n)
.globl fib
.ent fib
fib:
subu $sp, $sp, 8
sw $ra, ($sp)
sw $s0, 4($sp)
move $v0, $a0 # check for base cases
ble $a0, 1, fibDone
move $s0, $a0 # get fib(n-1)
sub $a0, $a0, 1
jal fib
move $a0, $s0
sub $a0, $a0, 2 # set n-2
move $s0, $v0 # save fib(n-1)
jal fib # get fib(n-2)
add $v0, $s0, $v0 # fib(n-1)+fib(n-2)
fibDone:
lw $ra, ($sp)
lw $s0, 4($sp)
addu $sp, $sp, 8
jr $ra
.end fib

Double Recursion in Assembly works until secondary recursion becomes larger than the base case

I am trying to code a Assembly program that will ask for an integer and perform a recursive function: if n>5 f(n) = n*f(n-1)-f(n-3)+n-23 else n<=5 f(n)=15-2n I managed to get the first instance of recursion of the n*f(n-1)but when it goes for the second recursion of f(n-3) it performs the second portion improperly.
During the calculations, if I input 8 as the integer, the result should be 20 which I get but, when I input anything greater than 8 because then the second recursion hits an integer larger than 5 (the base case) I get an incorrect answer. Basically any input larger than 8 doesn't work
For example the correct answers are:
f(9) = 162
f(10) = 1602
f(11) = 17590
The answers I get
f(9)= 27
f(10)=22
f(11)=23
Here is my code:
```#data declarations: declare variable names used in program, storage allocated in RAM
.data
prompt1: .asciiz "\nEnter an Integer:\n" #Ending Index
message1: .asciiz "\nThe Solution is:\n"
answer: .word 0
#program code is contained below under .text
.text
.globl main #define a global function main
# the program begins execution at main()
main:
la $a0, prompt1 #load address of prompt1
li $v0, 4 #prepare print string
syscall
li $v0, 5 #prepare receive int
syscall
move $a0, $v0
addi $sp, $sp, -4
sw $ra, 0($sp)
jal Function
sw $v0, answer #move returned answer to a new memory
lw $ra, 0($sp)
addi $sp, $sp, 4
la $a0, message1 #load address of message1
li $v0, 4 #prepare print string
syscall
lw $a0, answer
li $v0, 1
syscall
jr $ra
############################################################################
# Procedure/Function Function1
# Description: recursive math function
# parameters: $a0 = value of n,
# return value: $v0 = answer
# registers to be used: $s3 and $s4 will be used.
############################################################################
Function:
addi $sp, $sp, -12 #adjust stack pointer
sw $ra, 8($sp) #save return address
sw $a0, 4($sp) #save n
sw $s0, 0($sp) #save immediate value (used for storing function(n-1))
li $t2, 15
slti $t0, $a0, 6
beq $t0, $zero, GreaterThanFive
LessThanFive:
add $t1, $a0, $zero
add $t1, $t1, $a0
sub $t2, $t2, $t1
move $v0, $t2
j Conclusion
GreaterThanFive:
addi $a0, $a0, -1
jal Function
move $s0, $v0
lw $a0, 4($sp)
addi $a0, $a0, -3
jal Function
lw $a0, 4($sp)
mul $t3, $a0, $s0
sub $t4, $t3, $v0
add $t5, $t4, $a0
addi $t6, $t5, -23
move $v0, $t6
Conclusion:
lw $s1,0($sp) # restore intermediate value
lw $a0,4($sp)
lw $ra,8($sp)
addi $sp,$sp,12 # restore stack
jr $ra #return to caller```

implementing recursive function in MIPS

I'm a bit confused on how to compile recursive functions in mips:
the following question was in a previous exam in my computer organization course:
the question asks us to compile:
int sum(int n){
if (n <= 1)
return 1;
else
return n + sum( sum(n-1) – sum(n-2) );
}
My attempt at tackling this problem:
sum: bgt $a0, 1, sum_recursive
add $v0, $0,1
jr $ra
sum_recursive: sub $sp, $sp, -12
sw $ra ,0($sp) # storing the return address
sw $a0, 4($sp) # storing the argument n
addi $a0, $a0, -1 # n-1
jal sum
sw $v0, 8($sp) # the return value of sum(n-1)
lw $a0, 4($sp) # a0=n
addi $a0, $a0, -2 # a0=n-2
jal sum
lw $t0, 8($sp) # the return value of sum (n-1)
sub $v0, $t0, $v0 # sum(n-1) - sum (n-2)
lw $a0, 4($sp)
add $a0, $v0, $0 # a0=sum (n-1)-sum (n-2)
jal sum
lw $a0, 4($sp)
add $v0, $v0, $a0 # $v0 = n+ sum (sum (n-1)- sum(n-2))
lw $ra, 0($sp)
lw $a0, 4 ($sp)
add $sp, $sp, 12
jr $ra
Thank you

Recursive assembly code stack pointer issues. (mips)

def count_partitions(n, m):
if n == 0:
return 1
elif n < 0:
return 0
elif m == 0:
return 0
else:
l = count_partitions(n, m-1) + count_partitions(n-m, m)
return l
Trying to convert this python code into MIPS assembly. My assembly code does its job but after it gets to the right answer, it continues and gets to absurd numbers.
I tried debugging with mars.jar and found out that my stack pointer didnt get updated at one point. Thanks a lot.
My code:
.data
prompt1: .asciiz "\Enter an integer to partition (n): "
prompt2: .asciiz "\Enter an integer for size of the partition (m): "
default: .asciiz "\nstuff "
.text
main:
la $a0, prompt1
li $v0,4
syscall
li $v0, 5
syscall
move $t0, $v0 # $t0 = n
la $a0, prompt2
li $v0,4
syscall
li $v0, 5
syscall
move $t1, $v0 # $t1 = m
add $a0, $t0, $zero
add $a1, $t1, $zero
addi $sp,$sp,-4
sw $ra,0($sp)
jal count_partitions
lw $ra,0($sp)
addi $sp,$sp,4
move $a0,$v0
li $v0, 1
syscall #prints the default
li $v0, 10
syscall
count_partitions:
addi $sp,$sp,-16
sw $s1,12($sp)
sw $a1,8($sp)
sw $a0,4($sp)
sw $ra,0($sp)
bne $a0,$zero,case1
addi $v0,$v0,1
j return
case1:
slti $t0,$a0,0
beq $t0,$zero,case2
j return
case2:
bne $a1,$zero,case3
j return
case3:
addi $a1,$a1,-1
jal count_partitions
addi $t7,$zero,1
mult $v0,$t7
mflo $s1
addi $a1,$a1,1
sub $a0,$a0,$a1
jal count_partitions
addi $t7,$zero,1
mult $v0,$t7
mflo $t2
add $v0,$s1,$t2
return:
lw $ra,0($sp)
lw $a0,4($sp)
lw $a1,8($sp)
lw $s1,12($sp)
addi $sp,$sp,16
jr $ra

Mips recursion that I am stuck at

Okay Im trying to do C(n, k) = C(n - 1, k - 1) + C(n - 1, k).
So far I have:
$a0 - n
$a1 - k
$v0 - C(n, k)
addi $sp, $sp, 8
sw $ra, 4($sp)
sw $s0, 0($sp)
addi $a0, $a0, -1
addi $a1, $a1, -1
lw $ra, 0($sp)
lw $s0, 4($sp)
Now Im lost on what to do next please help me out.
If I look right, it's about the binomial coefficient. Yes, the first steps into MIPS can be kinda confusing, but practice is the key to get better. I don't know if you are in any course or anything. I wrote up something for you, here with the extra feature of getting the input values for n and k. In the end, it prints out the result of C(n, k).
.TEXT
.GLOBL __MAIN
__MAIN:
LI $V0, 4 // print1 asking you for number (n).
LA $A0, PRINT1
SYSCALL
LI $V0,5 // do a syscall that reads an integer.
SYSCALL
MOVE $T0, $V0
LI $V0, 4 // print2 asking you for number (k).
LA $A0, PRINT2
SYSCALL
LI $V0,5 // do a syscall that reads an integer.
SYSCALL
MOVE $A0, $T0
MOVE $A1, $V0
JAL BINOMIALVAL
// this marks the end of procedure call. woo.
MOVE $T0, $V0
LI $V0, 4 // prints print3.
LA $A0, PRINT3
SYSCALL
MOVE $A0, $T0
LI $V0,1 // this prints your c(n,k)
SYSCALL
LI $V0,4 // this will print an eol. (end of line)
LA $A0, CR
SYSCALL
LI $V0,10 // exit here.
SYSCALL
// will display the result and exit.
BINOMIALVAL:
SLT $T0, $A0, $A1 // if n < k, then set t0 to 1.
BNE $T0, $ZERO, END1 // if t0 != 0, then branch to end1.
BEQ $A0, $A1, END2 // if n == k, then branch to end2
BEQ $A1, $ZERO, END2 // if n == 0, then branch to end2.
SW $FP, -4($SP)
ADDI $FP, $SP, 0
ADDI $SP, $SP, -12
SW $RA, 4 ($SP)
SW $A0, 0 ($SP)
ADDI $SP, $SP, -4
SW $T1, ($SP)
ADDI $A0, $A0, -1
JAL BINOMIALVAL
MOVE $T1, $V0
ADDI $SP, $SP, -4
SW $A1, ($SP)
ADDI $SP, $SP, -4
SW $RA, ($SP)
ADDI $A1, $A1, -1
JAL BINOMIALVAL
ADD $V0, $T1, $V0
LW $RA, ($SP)
ADDI $SP, $SP, 4
LW $A1, ($SP)
ADDI $SP, $SP, 4
LW $T1, ($SP)
ADDI $SP, $SP, 4
LW $A0, ($SP)
ADDI $SP, $SP, 4
LW $RA, ($SP)
ADDI $SP, $SP, 4
LW $FP, ($SP)
ADDI $SP, $SP, 4
J COMPLETE
END1: MOVE $V0, $ZERO
J COMPLETE
END2: ADDI $V0, $ZERO, 1
COMPLETE: JR $RA // return to normal routine.
.DATA
PRINT1: .asciiz "Enter your value for n "
PRINT2: .asciiz "Enter your value for k "
PRINT3: .asciiz "Value of c(n,k) = "
CR: .asciiz "\n"

Resources