trouble with simple recursion in MIPS assembly - recursion

so I'm trying to write a program in mips that just does some simple recursion, ive read every other article and question on the subject i can find but to no avail. anyways i'm trying to print my name backwards. so i'm reading one byte at a time looking for the 0 at the end of my string, if i find it i start unwinding and printing the bytes i saved.
Thanks ahead of time for any help.
code:
.data
# print name backwards
Yn: .asciiz "frodobaggins" #yn stands for your name
.text
main:
li $v0, 4 #the v registers tell system what to do 4 ==
print_string
la $a1, Yn #load Yn's address into a0
addi $sp, $sp, -4 #increment stack pointer, always initially points to garbage
sw $a1, 0($sp) #store address of a1 (Yn) into stack pointer
jal recurse #recursive func call
addi $sp, $sp, 4 #increment sp back to beggining
addi $v0, $0, 10 #v0 = exit
syscall #exit
recurse:
lw $a3, 0($sp) #load my name into reg $a3
lw $t6, 0($a3) #read byte
addi $sp, $sp, -12 #increment stack ptr
sw $ra, 8($sp) #store address of $ra onto stack
sw $t6, 4($sp) #store the byte loaded into $t6 onto stack
bne $t6, $0, otherwise #if t6 == 0 goto otherwise
add $a3, $a3, 1 #a1 = a1 + 1
sw $a3, 0($sp) #store the word, without the byte $t6 used, onto the stack
jal recurse
lw $t6, 4($sp) #load the byte that was stored into $t6
lw $ra, 8($sp) #get $ra ready for returning
addi $v0, $0, 4 #setup $v0 to tell pc to print to console
lw $a0, 0($t6) #console always reads from $a0
syscall
addi $sp, $sp, 12 #put stack pointer back
jr $ra #string is finished, return.
otherwise:
#because this is just returning, i need to get the ra that was just stored on the stack
lw $ra 8($sp)
jr $ra #go back to original call of jal

There were a few problems.
Loading $t6 should be a byte operation. The conditional branch to otherwise had an incorrect sense. The syscall to print should have been 11 [putc] and not 4 [puts]. The stack frame restore at otherwise was problematic.
I've created three versions. An annotated version that shows the bugs. A cleaned up and working version. And, a version I created from scratch.
Here's the annotated version [please pardon the gratuitous style cleanup]:
.data
Yn: .asciiz "frodobaggins" # yn stands for your name
# print name backwards
.text
main:
li $v0,4 # syscall for print string
la $a1,Yn # load Yn's address into a0
addi $sp,$sp,-4 # increment stack pointer
sw $a1,0($sp) # store address of a1 (Yn) into stack pointer
jal recurse # recursive func call
addi $sp,$sp,4 # increment sp back to beggining
addi $v0,$0,10 # v0 = exit
syscall # exit
recurse:
lw $a3,0($sp) # load my name into reg $a3
# NOTE/BUG: this should be lb
# NOTE/BUG: by loading $t6 _before_ we've saved it, we are destroying the
# value that _caller_ set
###lw $t6,0($a3) # read byte
lb $t6,0($a3) # read byte
addi $sp,$sp,-12 # increment stack ptr
sw $ra,8($sp) # store address of $ra onto stack
sw $t6,4($sp) # store the byte loaded into $t6 onto stack
# NOTE/BUG: this should be beq and _not_ bne
###bne $t6,$0,otherwise # if t6 == 0 goto otherwise
beq $t6,$0,otherwise # if t6 == 0 goto otherwise
add $a3,$a3,1 # a1 = a1 + 1
sw $a3,0($sp) # push the word, without the byte $t6 used
jal recurse
lw $t6,4($sp) # load the byte that was stored into $t6
lw $ra,8($sp) # get $ra ready for returning
# NOTE/BUG: this is the puts syscall -- what we want is putc (i.e. 11)
###addi $v0,$0,4 # setup $v0 to tell pc to print to console
addi $v0,$0,11 # setup $v0 to tell pc to print to console
# NOTE/BUG: we want to load the byte into $a0
# NOTE/BUG: at this point $t6 has the byte value and _not_ a pointer
###lw $a0,0($t6) # console always reads from $a0
move $a0,$t6
syscall
addi $sp,$sp,12 # put stack pointer back
jr $ra # string is finished, return.
otherwise:
# because this is just returning, i need to get the ra that was just
# stored on the stack
lw $ra,8($sp)
# NOTE/BUG: by _not_ restoring the $t6 value of _caller_ things are broken
jr $ra # go back to original call of jal
Here's the working version. Note the early stack frame setup and the full stack frame pop at otherwise. This is somewhat more common. That is, one and only one place to establish the stack frame. And, one and only one place to pop the frame and return:
.data
Yn: .asciiz "frodobaggins" # yn stands for your name
# print name backwards
.text
main:
li $v0,4 # syscall for print string
la $a1,Yn # load Yn's address into a0
addi $sp,$sp,-4 # increment stack pointer
sw $a1,0($sp) # store address of a1 (Yn) into stack
jal recurse # recursive func call
addi $sp,$sp,4 # increment sp back to beggining
addi $v0,$0,10 # v0 = exit
syscall # exit
recurse:
lw $a3,0($sp) # load my name into reg $a3
lb $t6,0($a3) # read byte
addi $sp,$sp,-12 # establish stack frame
sw $ra,8($sp) # store address of $ra onto stack
sw $t6,4($sp) # store the byte loaded into $t6 onto stack
beq $t6,$0,otherwise # if t6 == 0 goto otherwise
add $a3,$a3,1 # a1 = a1 + 1
sw $a3,0($sp) # push the word, without the byte $t6 used
jal recurse
lw $t6,4($sp) # load the byte that was stored into $t6
lw $ra,8($sp) # get $ra ready for returning
addi $v0,$0,11 # setup $v0 to tell pc to print to console
move $a0,$t6
syscall
otherwise:
lw $ra,8($sp) # restore return address from stack
lw $t6,4($sp) # store the byte loaded into $t6 onto stack
addi $sp,$sp,12 # put stack pointer back
jr $ra # string is finished, return.
Here's my refactored version. It is a bit more mips ABI conformant. There are a few slight variations possible:
.data
name: .asciiz "frodobaggins"
nl: .asciiz "\n"
.text
.globl main
main:
li $v0,4 # print string syscall
la $a0,name # string address
syscall
li $v0,4 # print string syscall
la $a0,nl # string address
syscall
la $a0,name # string address
jal recurse1
li $v0,4 # print string syscall
la $a0,nl # string address
syscall
li $v0,10
syscall
# recurse1 -- reverse print string
#
# arguments:
# a0 -- current string pointer
recurse1:
sub $sp,$sp,8 # create stack frame
sw $ra,4($sp) # save return address
sw $a0,0($sp) # save current string pointer
lb $t0,0($a0) # get current byte
beqz $t0,recurse1_exit # is it EOS? if yes, fly
addu $a0,$a0,1 # advance to next char
jal recurse1 # print other chars
subu $a0,$a0,1 # go back to our character
lb $a0,0($a0) # get current byte
li $v0,11 # putc syscall number
syscall
recurse1_exit:
lw $ra,4($sp) # restore return address
lw $a0,0($sp) # restore current string pointer
add $sp,$sp,8 # pop stack frame
jr $ra # return
# recurse2 -- reverse print string
#
# arguments:
# a0 -- current string pointer
recurse2:
sub $sp,$sp,8 # create stack frame
sw $ra,4($sp) # save return address
sw $a0,0($sp) # save current string pointer
lb $t0,0($a0) # get current byte
beqz $t0,recurse2_exit # is it EOS? if yes, fly
addu $a0,$a0,1 # advance to next char
jal recurse2 # print other chars
lw $a0,0($sp) # restore our pointer
lb $a0,0($a0) # get current byte
li $v0,11 # putc syscall number
syscall
recurse2_exit:
lw $ra,4($sp) # restore return address
lw $a0,0($sp) # restore current string pointer
add $sp,$sp,8 # pop stack frame
jr $ra # return
# recurse3 -- reverse print string
#
# arguments:
# a0 -- current string pointer
recurse3:
sub $sp,$sp,8 # create stack frame
sw $ra,4($sp) # save return address
lb $t0,0($a0) # get current byte
sw $t0,0($sp) # save current char value
beqz $t0,recurse3_exit # is it EOS? if yes, fly
addu $a0,$a0,1 # advance to next char
jal recurse3 # print other chars
lw $a0,0($sp) # get current byte
li $v0,11 # putc syscall number
syscall
recurse3_exit:
lw $ra,4($sp) # restore return address
add $sp,$sp,8 # pop stack frame
jr $ra # return

Related

MIPS Assembly - how to calculate the square of n recursively

I want to write a program that calculates the square of n using recursion based on the equation n^2 = (n - 1)^2 + 2(n - 1) + 1 But I don't know how to write the nonbasecase: part. Can anyone help?
A python program would be
def square(n) {
if (n==0):
return 0
else:
return square(n-1) + 2*(n-1) + 1
}
Here is what I got so far.
start:
li $a0, 0x0003 #$a0 contains the number to be squared
jal square # recursive call
square:
subi $sp, $sp, 8 # decrement the stack pointer $sp
sw $ra, 4($sp) # push the return address register $ra
sw $a0, 0($sp) # push argument register $a0
li $t0, 0x0001 # load $t0 with 1 as part of test for base case
bne $a0, $t0, nonbasecase # branch if not the base case
li $v0, 0x0001 # return base result in $v0
addi $sp, $sp 8 # recover stack space
jr $ra # jump to return address in $ra
nonbasecase:
#not sure how to write when it is not the base case
jr $ra # jump to contents of return address register $ra
Write the function so that it inputs n2 and n and returns n2+2*n+1 and n+1.

When exactly are non-preserved registers cleared in MIPS?

Below is the Fibonacci value expressed in MIPS.
fib: addi $sp, $sp, -24
sw $ra, 16($sp)
sw $a0, 20(sp) # recursive calls will overwrite original $a0
sw $s0. 0($sp) # holds fib(n-1)
# end prologue
slti $t0, $a0, 4 # fib(i) = i for i = 1, 2, 3; fib(0) = 0 by C code
beq $t0, $zero, L1
addi $v0, $a0, 0 # see prior comment (assumes $a0 non-negative integer)
j exit
# fib(n) = fib(n-1) + fib(n-2)
L1: addi $a0, $a0, -1
jal fib
addi $s0, $v0, 0 # $s0 = fib(n-1) <-----how can use $v0?
addi $a0, $a0, -1
jal fib # upon return, $v0 holds fib(n-2)
add $v0, $v0, $s0
exit: # unwind stack and return
lw $s0, 0($sp)
lw $a0, 20($sp)
lw $ra, 16($sp)
addi $sp, $sp, 24
jr $ra
But there is something I don't quite understand here. As far as I know, the values ​​of the registers other than $s disappear when the function ends.
Looking at the fib function, when n is 1 or 0, 1 or 0 is stored in the value of $v0. After that, when the function ends, shouldn't the value of $v0 be deleted too? So, before calling fib(n-2), the return value of fib(n-1) is deleted, so I thought that the code to be saved in $s should be written in the fib function.
However, in the code above, the return value of the fib(n-1) function is used by the next fib(n-2) function. I don't know how this is possible.
Exactly how long are non preserved registers preserved and when will they be deleted?
Please be aware that the fib you're quoting is following custom and non-standard calling convention.  I don't recommend it for learning about call preserved vs. call clobbered registers.
In this section here:
L1: addi $a0, $a0, -1
jal fib
addi $s0, $v0, 0 # $s0 = fib(n-1) <-----how can use $v0?
addi $a0, $a0, -1 <------------- **HERE** --------
jal fib # upon return, $v0 holds fib(n-2)
At the line marked **HERE**, the code expects $a0 to have survived the function call.  This non-standard, and a disingenuous illustration of the proper MIPS calling convention.
While this code works, it does not follow the MIPS calling convention register usage.  It has made a custom alteration, that is only possible by knowing the implementation of both caller and callee.  Thinking this way is false for the general case.
exit: # unwind stack and return
lw $s0, 0($sp)
lw $a0, 20($sp) <----------- **ALSO**
lw $ra, 16($sp)
addi $sp, $sp, 24
jr $ra
This part marked **ALSO** is where the code is restoring $a0 for the caller!  This is unconventional and in the general case not to be relied upon.  In the general case, no one shall rely on $a0 being preserved, and so no one should bother to restore it.
Here's a proper (and more efficient) version of recursive fib:
fib:
beq $a0, $0, return0 # goto return 0 if n == 0
li $v0, 1 # load $v0 with 1 for comparison and return
beq $a0, $v1, return # if n == 1 return leaving 1 in $v0
addiu $sp, $sp -8 # allocate two words of stack space
sw $ra, 4($sp) # save $ra
sw $a0, 0($sp) # save n
addi $a0, $a0, -1
jal fib # fib(n-1)
lw $a0, 0($sp) # restore $a0 for next call to fib
sw $v0, 0($sp) # store return value of fib(n-1) in stack
addi $a0, $a0, -2
jal fib # fib(n-2)
lw $t0, 0($sp) # reload return value from fib(n-1)
add $v0, $t0, $v0 # fib(n-1)+fib(n-2)
lw $ra, 4($sp) # restore our return address
addiu $sp, $sp, 8 # release stack
jr $ra # and use return address
return0:
li $v0, 0
return:
jr $ra
This version actually follows the MIPS calling convention.  It does not convert $a0 into a call preserved register, instead reloading $a0 before the 2nd recursive call instead of in epilogue.
This version does not use $s registers, as they are actually a disadvantage in the circumstances of this particular function.  Stack memory is used instead.  An $s register version would be (just slightly) longer, because the same number of loads & stores would be involved (but for different purpose of preserving $s register) though a extra register-to-register copy instruction would be needed making it longer.
As far as I know, the values ​​of the registers other than $s disappear when the function ends.
All the registers are permanent and accessible to all machine code in the program, and that includes both $t, $a, $v, and $s registers, for example.
The register values only change by executing machine code instructions that target them.  Whether you're allowed (or supposed) to do that is a matter of software convention, which tells us how to share a mere 31 registers in a program that may have thousands of functions.
By software agreement, $a0 is used to pass the first (integer or pointer) parameter, and $v0 is used to return a function's (integer or pointer) return value.  Once a register is set it doesn't change unless some machine code instruction changes it.  Thus, register values have continuity and are under total control of the machine code program.

Hexadecimal to decimal in MIPS. I'm getting really strange and negative output from $s3 could really use some help for I am all out of ideas

Been trying to write this hex to decimal converter in MIPS. Should take in up to 8 arguments (string) from command line, all hex starting with "0x", 4 bytes long and terminated with null character after every argument.
$s1= number of args; $s0-->args(0).
For these Program Arguments: 0x1A 0xF1 0x0B3:
.data
buffer: .space 32
args: .asciiz "\nProgram arguments:\n"
space: .asciiz " "
d_rep: .asciiz "\n\nInteger values:\n"
newLine: .asciiz "\n"
maxVal: .asciiz "Maximum value:\n"
.text
main:
add $t9, $zero, $zero # t9 will be max value
add $s1, $a0, $0 # store number of program arguments
add $s2, $a1, $0 # store address of where program arguments are
addi $s0, $0, 0 # use s0 as index for looping
addi $s7, $zero, 0 # used as index for print loop
add $s6, $s1, $0 # store numArgs for printloop
add $s5, $s2, $0 # store address for printloop
addi $t5, $zero, 16
addi $t6, $zero, 0
la $a0, args # load and print "Program arguments" string
li $v0, 4 # print
syscall
printloop:
beq $s7, $s6, printInt # only print as many times as there are args
lw $a0, 0($s5) # get the first word
li $v0, 4 # print
syscall
la $a0, space # print a space between words
li $v0, 4 # print
syscall
addi $s5, $s5, 4 # increment to next word
addi $s7, $s7, 1 # increment printloop counter
j printloop # loop again if there are more args
printInt:
la $a0, d_rep # print "Decimal values:"
li $v0, 4 # print
syscall
while:
beq $s0, $s1, done # while $s0 <= numArgs
lb $t1, 2($s2) # load s2[2] into
addu $t2, $zero, $t1 # make a copy so we dont lose place
subiu $sp, $sp, 4
convert:
beqz $t2, postConv # while t2 != null
blt $t2, 65, convNumber # if less than value of A then got a num
subiu $t3, $t2, 0x37 # -55 for uppercase letter
sll $t3, $t3, 4 # sll by 4 is same as mult by 16
sw $t3, ($sp) # store word to do some addition
addiu $t2, $t2, 1 # increment t2
subiu $sp, $sp, 4 # increment sp
j convert # go back to convert
convNumber:
addu $t3, $zero, $t2 # put t2 into t3 for to do some math
subiu $t3, $t3, 0x30 # -48 for number
sll $t3, $t3, 4 # sll by 4 is same as mult by 16
sw $t3, ($sp)
addiu $t2, $t2, 1 # increment t1
addiu $sp, $sp, 4 # increment sp
j convert # jump back to convert
postConv:
addi $sp, $sp, 4 # load the second value
lw $s4, ($sp) # load the first value
addi $sp, $sp, 4
# sll $s4, $s4, 4
lw $s3 ($sp)
addu $s3, $s3, $s4 # add em up for converted decimal
move $a0, $s3 # move to a0 so they pint
li $v0, 1 # print
syscall
addiu $s0, $s0, 1 # increment loop counter
addiu $s2, $s2, 4 # increment to next word
ble $t9, $t6, newMax # check for a new max value
j while # jump back to loop
newMax:
addu $t9, $zero, $t6 # got a new max so save it
j while
done:
la, $a0, newLine
li $v0, 4
syscall
la, $a0, newLine
li $v0, 4
syscall
la, $a0, maxVal
li $v0, 4
syscall
move $a0, $t9
li $v0, 1
syscall
la, $a0, newLine
li $v0, 4
syscall
li $v0, 10 # end program
syscall
My output is this:
Program arguments:
0x1A 0xF1 0x0B3
Integer values:
-2121474836290
Maximum value:
0
-- program is finished running --
But it should be : 26 241 179. And maxValue should be: 241
I would be very grateful if someone could help me debug this. Thank you! :D
You should be able to debug this yourself.  Single stepping the code to find this error is trivial.  If you don't know how to debug code, then suggest you focus on learning that.  Check each instruction to make sure it is doing what you expect, and if not, that's where the problem is.
argv is a pointer to pointers to characters — if we consider that a pointer to characters is a string, then we can describe argv as a pointer to strings.  To get single character from argv we have to choose which of the strings, then choose a character from that string.
You are doing this correctly when printing the program arguments, but are not following that approach for accessing their bytes, instead you're accessing individual bytes from the pointers themselves, which of course makes no sense.  Put another way, you're missing an indirection aka dereference.
This kind of error should stand out to you like a flashing warning sign when you single step over the lb $t1, 2($s2) instruction — because you are expecting it to fetch the ascii 1, 0x31, but are getting an 0xff instead.  When you see that an instruction is not getting what you expect that's where and when to rethink things: it is doing what you told it to do but you can observe that is not what you want.

the answer is always wrong in this MIPS recursion . got 10, supposed to be 55

This code is supposed to print the sum of numbers from 10 to 0. It should be printing 55, but is printing 10 instead. Can you help me figure out where it's going wrong?
main:
# initialize values to 3 registers
addi $a0,$zero,10
jal sum # call method
# Print out the summation upto 10
li $v0,1 # print integer
add $a1,$v0,$zero # load return value into argument
syscall
li $v0,10 # Exit
syscall
sum:
addi $sp,$sp,-8 # allocate space on stack
sw $ra,0($sp) # store the return address
sw $a0,4($sp) # store the argument
slti $t0,$a0,1 # check if n > 0
beq $t0,$0,recurse # n > 0 case
add $v0,$0,$0 # start return value to 0
addi $sp,$sp,8 # pop 2 items off stack
jr $ra # return to caller
recurse:
addi $a0,$a0,-1 # calculate n-1
jal sum # recursively call sum(n-1)
lw $ra,0($sp) # restore saved return address
lw $a0,4($sp) # restore saved argument
addi $sp,$sp,8 # pop 2 items off stack
add $v0,$a0,$v0 # calculate n + sum(n-1)
jr $ra # return to caller
Functions return their value in $v0; the print integer syscall expects its argument in $a0.
You need to move $a0 to $v0 before you do the print, otherwise you'll be printing whatever happened to be in $a0 (which in the case is the last value that you recursively added; i.e. 10).

I am using MIPS simulator to calculate the sum of numbers from N (10 in my case) to 1

I tried with the following code, but the answer is wrong. It should be 55 but i got 10. any help folks?
main:
# initialize values to 3 registers
addi $a0,$zero,10
jal sum # call method
# Print out the summation upto 10
li $v0,1 # print integer
add $a1,$v0,$zero #load return value into argument
syscall
li $v0,10 # Exit
syscall
sum:
addi $sp,$sp,-8 # allocate space on stack
sw $ra,0($sp) # store the return address
sw $a0,4($sp) # store the argument
slti $t0,$a0,1 # check if n > 0
beq $t0,$0,recurse # n > 0 case
add $v0,$0,$0 # start return value to 0
addi $sp,$sp,8 # pop 2 items off stack
jr $ra # return to caller
recurse:
addi $a0,$a0,-1 # calculate n-1
jal sum # recursively call sum(n-1)
lw $ra,0($sp) # restore saved return address
lw $a0,4($sp) # restore saved argument
addi $sp,$sp,8 # pop 2 items off stack
add $v0,$a0,$v0 # calculate n + sum(n-1)
jr $ra # return to caller
You are moving the sum from $v0 to $a1 after you store 1 in $v0.
Also, You are storing the sum in $a1, while $a0 is the register that gets printed..
add $a1,$v0,$zero #load return value into argument
^ HERE
This should fix it.
add $a0,$v0,$zero #load return value into argument
li $v0,1 # print integer
syscall

Resources