MIPS recursion call in loop, preserving loop variable - loops

I'm converting the following recursive java program to MIPS asm. The algorithm computes all the possible ordering/permutations of the numbers. But the recursive call is in the for loop. I need to preserve the variable 'i' in my MIPS version but I don't know exactly where to add that. My algorithm is correct, it's just that my $t0 (which is 'i') never gets reset to 0. I just can't figure out how/where to preserve it on the stack or when to take it off the stack. Any help appreciated.
import java.util.Arrays;
public class Test
{
private static void swap(int[] v, int i, int j)
{
int t = v[i];
v[i] = v[j];
v[j] = t;
}
public void permute(int[] v, int n)
{
if (n == 1)
System.out.println(Arrays.toString(v));
else
{
for (int i = 0; i < n; i++)
{
permute(v, n-1);
if (n % 2 == 1)
swap(v, 0, n-1);
else
swap(v, i, n-1);
}
}
}
public static void main(String[] args)
{
int[] ns = {1, 2, 3, 4};
new Test().permute(ns, ns.length);
}
}
and the mips function
Note: I am permutating Strings, not integers but the algorithm is the same.
#----------------------------------------------
# anagram - Prints all the permutations of
# the given word
# a0 - the word to compute the anagrams
# s0 - n, the length of the word
# a1 - n - 1 (length-1)
#----------------------------------------------
anagram:
addi $sp, $sp, -16
sw $a0, 0($sp)
sw $a1, 4($sp)
sw $s0, 8($sp)
sw $ra, 12($sp)
add $s0, $a1, $zero # this is n
addi $a1, $s0, -1 # n-1
beq $s0, 1, printS
init: move $t0, $zero # t0 = i = 0
logic: slt $t1, $t0, $s0 # Set t1 = 1 if t0 < length
beqz $t1, endAnagram # if it's zero, it's the end of the loop
jal anagram
li $t2, 2
div $s0, $t2
mfhi $t3
beqz $t3, even # if even branch to even, otherwise it will go to odd
odd: # swap the n-1 char with the first
add $t4, $a0, $zero
add $t5, $a0, $a1
lb $t6, 0($t4) # first char
lb $t7, 0($t5) # n-1 char
sb $t7, 0($t4) # swap the two
sb $t6, 0($t5)
j inc # skip the even section
even: # swap the ith char with n-1 char
add $t4, $a0, $t0 # ith char
add $t5, $a0, $a1 # n-1 char
lb $t6, 0($t4) # ith char
lb $t7, 0($t5) # n-1 char
sb $t7, 0($t4) # swap the two
sb $t6, 0($t5)
inc: addi $t0, $t0, 1 # t0++;
j logic
endAnagram:
# reset stack pointers
lw $a0, 0($sp)
lw $a1, 4($sp)
lw $s0, 8($sp)
lw $ra, 12($sp)
addi $sp, $sp, 16 # adjust stack
jr $ra
printS: # print string and jump to return
jal printString # calls printString function which prints the string
j endAnagram

$t0 is not preserved accross subroutine calls according to convention, and you seem to follow that convention. As such, you have two choices:
you either store i in a register that is preserved, in which case
you need to preserve the register yourself in the prologue/epilogue. You already do this for $s0.
or you save $t0 yourself on the stack, around the subroutine call
In both cases, you will need additional space for your locals, so change addi $sp, $sp, -16 to addi $sp, $sp, -20 (along with the matching code in the epilogue too). If you choose option #1, use for example $s1 to store i. Add code to save and restore $s1 just like you do for $s0. If you choose option #2, add code around the jal anagram that writes $t0 to stack before the jal, and reloads it after.

Related

What is the problem with this quick sort in MIPS?

This is the C code i am using to make the quick sort on the MIPS, on the Mars editor i am having issues when running these code
#include <stdio.h>
void QuickSort(int *array, int first, int last){
int q;
if (first<last){
int value = array[last];
int i = first-1;
int tmp;
int j = first;
while(j<=last){
if (array[j] <= value) {
i++;
tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
j++;
}
q = i;
QuickSort(array,first,q-1);
QuickSort(array,q+1,last);
}
}
And this is my MIPS translation so far, i am getting an infinite loop
.data
numArray: 30, 15, 11, 40, 75, 80, 70, 60
first: 0
last: 7
.text
main:
la $a0, numArray
lw $a1, first
lw $a2, last
jal quick_sort
li $v0, 10
syscall
quick_sort:
subi $sp, $sp, 4 #reserving memory in the stack
sw $ra, 4($sp) #storing the return adress in the stack
xor $t0, $t0, $t0 #int q
bge $a1, $a2, label1 #if (first<last)
sll $t1, $a2, 2 #int value = array[last];
add $t1, $t1, $a0 #callculating array[last] in $t1
lw $t1, 0($t1) #array[last] = array direction + 4 * last
or $t2, $t1, $zero #$t2 will be value
subi $t3, $a1, 1 #int i = first-1;
xor $t4, $t4, $t4 #int tmp
or $t5, $a1, $zero #int j=first
j label2 #while(j<=last)
label3: sll $t6, $a2, 2 #calculating array[j] adress
add $t6, $t6, $a0
lw $t7, 0($t6) #calculating array[j] value
bgt $t7, $t1, label4 #if (array[j] <= value)
addi $t3, $t3, 1 #i++
sll $s0, $t3, 2 #calculating array[i] adress
add $s0, $s0, $a0
lw $s1, 0($s0) #calculating array[i] value
or $t4, $s1, $zero #tmp = array[i]
sw $t7, 0($s0) #array[i] = array[j];
sw $t4, 0($t6) #array[j] = tmp;
label4: addi $t5, $t5, 1 #end if; j++
label2: ble $t5, $a2, label3 #while condition
or $t0, $t3, $zero #q = i
lw $a1, first #first value on the second parameter
subi $a2, $t0, 1 #q-1
jal quick_sort #QuickSort(array,first,q-1)
addi $a1, $t0, 1 #q+1
lw $a2, last #last value on the third parameter
jal quick_sort #QuickSort(array,q+1,last);
label1: lw $ra, 4($sp) #Recovering the return address from the stack
addi $sp, $sp, 4 #releasing memory
jr $ra #going to the return address
Maybe i need to store something more on the stack or something that i am missing, thanks for your help, anything that you see strange there please let me know to check it.
EDIT:
As pointed out by Minn in a comment, I missed the two lines following:
sll $t1, $a2, 2 #int value = array[last];
So while this single line does not match the comment in it, the loading of the value seems correct.
END EDIT
I am not familiar with this specific assembly, but the problem seems to be what is known as "register clobbering" :
According to documentation jal only stores the return address, but does not store any of the registers.
lw $a1, first #first value on the second parameter
subi $a2, $t0, 1 #q-1
jal quick_sort #QuickSort(array,first,q-1)
addi $a1, $t0, 1 #q+1
lw $a2, last #last value on the third parameter
jal quick_sort
You are using $t0 as q local variable, but you never save it on the stack.
After the first call jal quick_sort #QuickSort(array,first,q-1) the value of $t0 would be different, but you use it immediately in the line addi $a1, $t0, 1 #q+1 as if it never changed, and then pass the result to the second call to QuickSort.
The C equivalent to this error would be to make q global and add q = 0; at the beginning of the function.
You must remember, that when working in assembly and using registers for local variables, you must save their state to the stack before calling any other function from your current function, otherwise you will lose state and your code will not work as expected!
To be honest, this is my first time seeing this particular assembly language, so there might be other errors I missed, but these are the most obvious ones so they were easy to spot.

Simple MIPS for loop not running

I'm very new to MIPs and trying to create a for loop for an assignment.
for (int i=1; i < 16; i+=2)
{
A[i] = A[i] + B[3*i]
}
With the current code I have when I try to load the value of A[i] it says fetch address not aligned on word boundary.
Here is my code:
main:
li $t0, 1 # Starting index of t0=i
lw $s7, aSize # Loop bound
la $s0, A # &A
la $s6, endA # &endA
la $s1, B # &B
loop:
#TODO: Write the loop code
addi $t3, $zero, 3 # $t3 = 3
mul $t4, $t0,$t3 # $t4 = i * 3
sll $t4, $t4, 2 # $t4 into a byte offset
add $s1, $s1, $t4 # $s1 = &B[i*3]
add $s0, $s0, $t0 # $s0 = &A[i]
lw $t1, 0($s0) # value of A[i]
lw $t2, 0($s1) # value of B[i * 3]
add $t2, $t1, $t2 # A[i] + B[i]
sw $t2, 0($s0) # A[i] = A[i] + B[i]
addi $s0, $s0, 2
addi $s1, $s1, 2
addi $t0, $t0, 1 #i++
bne $t0, $s7, loop
I'm very new to MIPs so not sure whats going on or where to even look. I appreciate any help.
When you do:
mul $t4, $t0,$t3 # $t4 = i * 3
You are calculating the array index [as you would in c].
But, before you can add that to the base address of the array, you need to convert this index into a byte offset. That is, you have to multiply it by 4. This can be done [as in c] with a shift left of 2.
So, after the mul, do:
sll $t4,$t4,2
You have to do this multiply/shift for all index values before adding them in.
UPDATE:
Okay that makes sense. I added that in but I'm still getting that word boundary error on the line "lw $t1, 0($s0)"
You're not showing the definition of A or B, so there could be an alignment issue.
When you do:
add $s1, $s1, $t4 # $s1 = &B[i*3]
You are modifying the original/base value of &B[0]. That's not what you want. Use a different register for the final address value (i.e. leave $s1 unchanged throughout the loop)
Do something like:
add $s3, $s1, $t4 # $s3 = &B[i*3]
lw $t2, 0($s3) # value of B[i * 3]
Adjust other similar register usage in a similar manner (i.e. you have a similar problem with the A array)
I've coded up a cleaned up version. I've not assembled nor tested it, but I think it will get you closer. This may have an off-by-one error [it's hard to tell without the entire program] as I'm not sure what aSize is.
main:
li $t0,1 # Starting index of t0=i
lw $s7,aSize # Loop bound
la $s0,A # &A
la $s6,endA # &endA
la $s1,B # &B
addi $t3,$zero,3 # $t3 = 3
loop:
# TODO: Write the loop code
mul $t4,$t0,$t3 # $t4 = i * 3
sll $t4,$t4,2 # $t4 into a byte offset
add $s3,$s1,$t4 # $s3 = &B[i*3]
sll $t4,$t0,2 # $t4 into a byte offset
add $s2,$s0,$t4 # $s2 = &A[i]
lw $t1,0($s2) # value of A[i]
lw $t2,0($s3) # value of B[i * 3]
add $t2,$t1,$t2 # A[i] + B[i]
sw $t2,0($s2) # A[i] = A[i] + B[i]
addi $t0,$t0,2 # i += 2
bne $t0,$s7,loop

Translate a loop in C with arrays to MIPS by using the stack

I have a doubt on one exercise that my professor did during a seminar. It's basically a translation from C to MIPS. The C code is as follows:
void subr1(char *a, int v[]) {
int i;
char c[12];
for (i=11; i >= 0; i--) {
v[i] = subr2(c[i], *a);
}
}
And the translation of my professor was the following (I've written comments about what each instruction does and what I don't understand):
addiu $sp,$sp,-28 #Activation block
sw $ra, 24($sp) #Space for $ra (4 bytes)
sw $s0, 20($sp) #Space for *a (32-bit address = 4 bytes)
sw $s1, 16($sp) #Space for c (Q1: Shouldn't this be 12 bytes? c is an array of 12 chars = 12*1Byte=12Bytes)
sw $s2 12($sp) #Space for i (4 bytes)
(Q2: Why is he stopping at 12? Doesn't he need to save space for int v[]?)
move $s0,$a0 #Save 'a' in stack
move $s1,$a1 #Save 'c' in stack
li $s2, 11 #i=11
For:
blt $s2, $zero, End #if i<0, then go to 'End'
addu $t0, $s2, $sp (Q3: I'm not sure, but I think he's trying to access v[i] by summing $sp and 'i'. Can you do that, without initialising any register at the beginning like we did for $s0 and $s1?)
lb $a0, 0($t0) #$a0 = v[i] (Q4: Isn't this wrong? It should access c[i], not v[i])
lb $a1, 0($s0) #$a1 = *a
jal sub2 #Jump to sub2
sll $t1, $s2, 2 #$t1 = 4*i
addu $t1,$t1,$sp #$t1 = 4*i + $sp (By this, I guess he's accessing v[i])
sw $v0,0($t1) #v[i] = $v0 (returned value from sub2)
addiu $s2, $s2, -1 #i--
b For
end:
#Close the activation block
lw $s2,12($sp)
lw $s1,16($sp)
lw $s0,20($sp)
lw $ra,24($sp)
addiu $sp,$sp,28
jr $ra #Jump to register address
Could somebody help me?
Thank you in advance.

Finding MAX values of two Arrays using MIPS

I'm supposed to find the max values in two different arrays. Here is an example in C
int largest( int* array, int arrayLength) {
int largest = array[0];
int index = 1;
while( index < arrayLength) {
if( array[index] > largest {
largest = array[index];
}
index++;
}
return( largest);
}
I'm supposed to do the same by implementing a function in MIPS assembly and test the function in main on two different arrays. I've included my code below, but I'm new and not sure if it even works as I don't have access to QTSPIM on this computer. If you have any suggestions, fixes, or any help at all it is much appreciated. Thanks
.data
arrayOne: .word -10, 9, 5, 60, 12, -33, 10
arrayTwo: .word 20, 44, -9, 8, 72, 25, -36
.text
main:
la $s0, arrayOne #load the array
move $a0, $s0 #move array to register the function can use
jal Largest #call the function
la $s0, arrayTwo #load the other array
move $a0, $s0
jal Largest
li $v0, 10 #exit program
syscall
Largest:
addi $sp, $sp, -4 #save return address
sw $ra, 0($sp)
move $t0, $a0 #load array into temp register
lw $v0, 0($t0) #set maximum to array[0]
li $t2, 1 #set index = 1
Loop:
slt $t3, $t2, $t1 #???
beq $t3, $zero, ExitLoop
sll $t3, $t2, 2 #find offest of next element
add $t3, $t0, $t3 #store address in $t3
lw $t4, 0($t3) #load next value into $t4
sgt $t5, $t4, $v0 #compare previous max with new value
beq $t5, $zero, notGreater #jump if not greater
move $v0, $t4 #if greater, set return value to new max
notGreater:
addi $t2, $t2, 1 #increment index
j Loop
ExitLoop:
lw $ra, 0($sp) #unload the stack
addi $sp, $sp, 4
jr $ra #return to main

Mips reading integer and store them in array then find the max

again
I'm writing a MIPS program that reads 5 integers and stores them in array. It then creates a new array whose values are the values of the initial array multiplied with their index. After that I need to find the max and min of the second array.
I'm new to MIPS, here is the code I wrote :
.data
Prompt: .asciiz "\n Enter 5 Integers :" #gets number of integers
op: .asciiz "\n Enter Option: \n 1-Find the mult \n 2- Find the max \n 3-Find the min \n 4-Exit"
invalidm: .asciiz "\n Bad Input:"
vec: .space 20
vec2: .space 20
.text
.globl __start
__start:
la $a0,Prompt
li $v0,4
syscall
#Reading integers and store theme in the array
options:
la $a0,op
li $v0,4
syscall
li $v0,5
syscall
blt $v0,1,invalid#
bgt $v0,4,invalid#
beq $v0, 1, multp#
beq $v0, 2, max#
beq $v0, 3, min#
beq $v0, 4, exitpro#
j options
multp:
#multiply every element in the array with its index and store them in the new array vec2
j options
max:
#find the max of the array vec
j options
min:
#find the min of the array vec
j options
invalid:
la $a0,invalidm
li $v0,4
syscall
j options
exitpro:
li $v0,10
syscall
I'd like to make a quick note on coding style: assembly is a bit diabolical. It's very tempting to say "oh, I do these 5 lines a lot, I should just jal here and reuse it". This makes sense at first, but tends to result in confusing spaghetti code that can't go 3 lines without jumping somewhere. As such, my code has a bit of code repetition, but nothing too bad.
I altered your initial conditions a little, the length of the array is hard coded, but it uses a stack-allocated array rather than one that's statically allocated at initialization in the .data header. Instead the array's length is allocated in the data section. This was just personal preference. It has the benefit that the code should work for any length > 0.
.data
PromptHead: .asciiz "\n Enter "
PromptTail: .asciiz " integers\n"
Minstr: .asciiz "Min: "
Maxstr: .asciiz "Max: "
newline: .asciiz "\n"
inputs: .word 5
.text
main:
# Output prompt for input
## "\n Enter "
la $a0, PromptHead
li $v0, 4
syscall
## "5"
lw $a0, inputs
li $v0, 1
syscall
## " integers\n"
la $a0, PromptTail
li $v0, 4
syscall
# Backup sp value before allocating array
move $fp, $sp
# calculate size of vector (numinputs * sizeof(int) = numinputs * 4 = numinputs << 2), store in $s1
lw $s0, inputs
sll $s1, $s0, 2
# dynamically grow stack to include array of ints
add $sp, $sp, $s1
# Loop initialization
# i = 0
move $s1, $zero
# while i < numInputs
ReadInput:
slt $s2, $s1, $s0
beq $s2, $zero, ReadInputDone
# Read integer
li $v0, 5
syscall
# Calculate array offset for this loop
sll $s2, $s1, 2
add $s2, $fp, $s2
# Store array value at calculated address
sw $v0, 0($s2)
# i++
addi $s1, $s1, 1
j ReadInput
ReadInputDone:
# Scale(array, length)
move $a0, $fp
move $a1, $s1
jal Scale
# max = FindMax(array, length)
jal FindMax
# Back up return val, print boilerplate
move $t0, $v0
li $v0, 4
la $a0, Maxstr
syscall
move $a0, $t0
li $v0, 1
syscall
la $a0, newline
li $v0, 4
syscall
# min = FindMin(array,length)
move $a0, $fp
jal FindMin
# Back up return val, print boilerplate
move $t0, $v0
li $v0, 4
la $a0, Minstr
syscall
move $a0, $t0
li $v0, 1
syscall
la $a0, newline
li $v0, 4
syscall
# Exit program
li $v0, 10
syscall
# Scale(array, length)
# Scales each array element by index+1. This does not alter any s or a registers,
# Alters the array in place.
Scale:
# Backup return address and fp on stack.
# Not strictly necessary here, but usually good to do this by habit
# when you're learning
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# Loop initialization as above
# i = 0
move $t2, $zero
# while i < 5
ScaleInput:
slt $t3, $t2, $t1
beq $t3, $zero, ScaleInputDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, multiply it by current index+1
# Grab it from the multiplication register (assume no overflow)
# then store the result back in the array
lw $t5, 0($t3)
addi $t4, $t2, 1
mult $t5, $t4
mflo $t5
sw $t5, 0($t3)
# i++
addi $t2, $t2, 1
j ScaleInput
ScaleInputDone:
# Unwind stack, restore frame pointer and
# return address. Again, not necessary here, but good
# practice
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
# int Max(array, length); result returned in $v0,
# No s or a registers are altered
FindMax:
# Backup return address and fp on stack.
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# set currMin = array[0]
lw $v0, 0($t0)
# Loop initialization
# i = 1
li $t2, 1
# while i < 5
MaxLoop:
slt $t3, $t2, $t1
beq $t3, $zero, MaxLoopDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, check if it's the new max
lw $t4, 0($t3)
sgt $t5, $t4, $v0
beq $t5, $zero, notGreater
# If so, set return value to it
move $v0, $t4
notGreater:
# i++
addi $t2, $t2, 1
j MaxLoop
MaxLoopDone:
# Unwind stack
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
# int Min(array, length); result returned in $v0,
# No s or a registers are altered
FindMin:
# Backup return address and fp on stack.
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# set currMin = array[0]
lw $v0, 0($t0)
# Loop initialization
# i = 1
li $t2, 1
# while i < 5
MinLoop:
slt $t3, $t2, $t1
beq $t3, $zero, MinLoopDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, check if it's the new min
lw $t4, 0($t3)
slt $t5, $t4, $v0
beq $t5, $zero, notLesser
# If so, set return value to it
move $v0, $t4
notLesser:
# i++
addi $t2, $t2, 1
j MinLoop
MinLoopDone:
# Unwind stack
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
As I mention in the code, the monkeying around with storing the $ra and $fp is a bit paranoid. If I was REALLY paranoid I'd store all the s registers as well. However, it's generally good practice and can save you a lot of headaches when you decide to add "function calls" in the middle of a function.
This is basically the approach to assembly where you write the program in C in your head and then translate that rather literally into assembly. Hence why I treat scaling, finding the max, etc as "functions".
A couple assemblyish notes: I repeatedly use sll $register, $register, 2 instead of multiplying by 4 (word size on MIPS32). This is because doing this is fewer instructions due to not having to cal li followed by mult followed by mflo. You can do it that way, and I used to, but once you get used to using sll and other bit fiddling operations a lot it's just cleaner to use sll as well as easier to read.
I do use a few pseudoinstructions, it's not a big deal, most modern MIPS assemblers and simulators that I'm aware of support them (including SPIM). I dance dangerously with the line after branch instructions (which are theoretically always executed regardless of the branch result), but in this case it wouldn't usually matter and most simulators and assemblers usually inject a no-op for you when it does anyway.
I know assembly can be a bit of a pain to read, so feel free to ask if you have any questions.

Resources