Translating C code to MIPS Assembly - Using Arrays - c

I am working on a project and can't seem to figure out what I am doing wrong. I am translating a piece of C code into MIPS Assembly Language.
The C Code is as follows:
int A[10];
int sum = 0;
int i = 0;
while(i < 10){
sum += A[i++];
sum *= 2;
}
I have produced the following MIPS code
where $s1 is base address of A
$s2 is sum
$s3 is i
s4 is 10
loop:
beq $s3, $s4, exit #if s3 and s4 equal, go to else
add $t0, $s3, $s3 #2i
add $t0, $t0, $t0 #4i
add $s1, $s1, $t0 #A[i]
lw $t2, 0($s1) #t2 = A[i]
add $s2, $s2, $t2 #sum = sum + A[i]
addi $s3, $s3, 1 #i = i + 1
add $s2, $s2, $s2 #sum = sum * 2
j loop
While my code produces a value, it doesn't match the expected value. I have been banging my head on the desk for hours trying to see what I am doing wrong and I can't figure it out. Any help is very much appreciated

You're adding $t0 to $s1 every time you loop over. As such, instead of incrementing the array pointer by 4 each time, you increment by i*4 each time.
So, instead of getting A[0], A[1], A[2], A[3], ..., you actually get A[0], A[1], A[3], A[6], ...
My suggestion would be to remove the first two add instructions, and just do add $s1, $s1, 4.

Polynomial is right, here is a fixed version of your code:
.text
.globl main
main:
li $s4 , 4
li $s3 , 0
la $s1 , array1
loop:
beq $s3, $s4, exit #if s3 and s4 equal, go to else
lw $t2, 0($s1) #t2 = A[i]
add $s2, $s2, $t2 #sum = sum + A[i]
addi $s3, $s3, 1 #i = i + 1
add $s2, $s2, $s2 #sum = sum * 2
addi $s1, $s1, 4 #$s1 = &(A[i++])
move $a0, $s2
jal print_int
j loop
exit:
li $v0, 10 # Exit
syscall
print_int:
li $v0, 1
syscall
la $a0, space
li $v0, 4
syscall
jr $ra
.data
array1: .word 3, 0, 1, 2
space: .asciiz " "

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

Understanding how this MIPS code works

so my professor put this problem in his slides and this was his answer, not understanding how he converted it into MIPS so if anyone could help explain this that would be great.
Variable i is in $s3, k is in $s5, and the base address of save[] is in $s6
He gave us this C code:
while( save[i] == k ) {
i += 1;
}
And gave us this MIPS code in response:
Loop: sll $t1, $s3, 2
add $t1, $t1, $s6
lw $t0, 0($t1)
bne $t0, $s5, Exit
addi $s3, $s3, 1
j Loop
Exit:
Loop: sll $t1, $s3, 2 # $t1 = 4*i (this is the offset to get to the ith element in your array)
add $t1, $t1, $s6 # $t1 = 4*i + base addr of save (getting the address of save[i])
lw $t0, 0($t1) # $t0 = save[i] (actually loading 4B from address of save[i], so getting the actual number here)
bne $t0, $s5, Exit # branch to Exit if save[i] != k
addi $s3, $s3, 1 # i++
j Loop
Exit:
Things to note here:
save is an int array, so each element is 4B. That's why the offset is 4*i.

C to MIPS conversions

int MyArray[30];
int n = 2;
MyArray[0] = 1;
MyArray[1] = 1;
do{
MyArray[n] = MyArray[n-1] + MyArray[n-2];
n++;
}
while(n < 30);
While I was doing this I got stuck on how i can recursively call the function without the use of return address $ra or $sp since the code doesn't actually return anything.
This is my work so far :
#s1= Myarray[0]
addi $s0, $0, 2 #$s0=n=2
addi $t0, $s1,0
addi $t1,$0,1
sw $t1, 0 ($t0) #Myarray[0]=1
addi $t0,$t0,4
sw $t1, 0 ($t0) #Myarray[1]=1
add $t0,$s0,$s1 #MyArray[n]= Myarray[0] +n
Loop:
addi $t2,$t0,-4
addi $t3,$t0,-8
lw $t4,0 ($t2)
lw $t5,0 ($t3)
add $t5,$t4,$t5
sw $t5, 0 ($t0)
addi $t0, $t0 ,4
addi $s0,s0,4
slti $s6,$s0,30
beq #s6,$0,Exit
j loop
Exit
This code
sw $t1, 0 ($t0) #Myarray[0]=1
addi $t0,$t0,4
sw $t1, 0 ($t0) #Myarray[1]=1
add $t0,$s0,$s1 #MyArray[n]= Myarray[0] +n
should be
sw $t1, 0 ($t0) #Myarray[0]=1
addi $t0,$t0,4 #update 't0' to point to Myarray[1]
sw $t1, 0 ($t0) #Myarray[1]=1
addi $t0,$t0,4 #update 't0' to point to Myarray[2]
The error is in the last line. Note that $s1 points to the array, and $s0 has the value 2, so t0=s1+s0 points t0 to the wrong address. You really want t0=s1+(s0*4) since each int is four bytes.
However, since you've already updated t0 to point to Myarray[1] (in the second line), you can just update it again (in the fourth line).

C-Code to MIPS Assembly

I was trying to convert C code to MIPS assembly. There are these two following C code snippets. The problem is that my solution differs from the standard solution. Also, I don't understand the standard solution. I hoped, someone could explain me the two following mips assembly code snippets.
First of all some additional information for the task. Only the following MIPS instructions are allowed: lw, add, beq, bne and j.
The register:
$s3 contains i
$s4 contains j
$s5 contains k
A is an array of 32-Bit-Integer and the initial address of A is in $s6
$t0 and $t1 can be used for storing temporary variables
The first one is a simple do-while loop:
do {
i = i + j;
} while(A[i] == k);
MIPS Assembly
loop: add $s3, $s3, $s4 // this is i = i+j
add $t1, $s3, $s3 // from now on
add $t1, $t1, $t1 // I cant follow anymore
add $t1, $t1, $s6 // What happens in these three lines?
lw $t0, 0($t1) // 0($t1) is new for me. What does this zero do?
beq $t0, $s5, loop
Now the second C code:
if ( i == j )
i = i + A[k];
else if( i == k )
i = i + A[j];
else
i = i + k;
Here is the MIPS assembly code:
bne $s3, $s4, Else1 // this line is if(i==j)
add $t1, $s5, $s5 // from here on
add $t1, $t1, $t1 // till
add $t1, $t1, $s6 //
lw $t0, 0($t1) //
add $s3, $s3, $t0 // here I don't understand
j done
ELSE1: bne $s3, $s5, Else2 // this line is if(i==k)
add $t1, $s4, $s4 // The same game as above
add $t1, $t1, $t1
add $t1, $t1, $s6
lw $t0, 0($t1)
add $s3, $s3, $t0 // till here
j done
ELSE2: add $s3, $s4, $s5
Could anyone explain me what really goes on? That would be very helpful
O.k. the most interesting and not-understandable piece of code is the following:add $t1,
add $t1, $s5, $s5
add $t1, $t1, $t1
add $t1, $t1, $s6
this code multiplies $s5 by four, and stores it in $t1, then adds to $t1 $s6. That is equivalent to:
$t1 = A[k]
after you understand this, the code looks much clearer.
about the lw $t0, 0($t1):
you can use an offset point in the address. Here the offset is 0.
lw is load. The mode here is indirect addressing, the most common is:
lw $t2, ($t0)
But you can also include a byte offset,
lw $t2, 4($t0) # load word at RAM address ($t0 + 4) into register $t2
The compiler is just putting the 0 placeholder in the non-offset version.
So, to load A[i] you need to do two things. Take the base address of A[] and then add i times the sizeof(A[0]). I am guessing that the values in A are 32 bit.
add $t1, $s3, $s3
$t1 = j + j or 2 * j
add $t1, $t1, $t1
$t1 = $t1 + $t1 or j +j +j +j or 4 * j
So why did it do it this way? Well, doing a straight multiply is slow in terms of clock cycles. The next choice would be a shift operation, but in this case, the compiler designers decided that two adds beat a shift.
add $t1, $t1, $s6
I would guess that $s6 is the base address of A[]. So ultimately '$t1 = A[] + 4 * j`
I have a answer.The loop, a is array of elements have basic address :0x0FE3B128.
Thanks all so much..
and this is my homework, I don't sure that it is correct.
for(i=1; i!=20;i+=3){
a[i]= a[5]+1;
}
lui $s0, 0x0FE3
ori $s0, $0, B128
lw $t1, 20($s0)
addi $s1, $0, 1
addi $s2, $0, 20
LOOP beq $s1, $s2, DONE
add $t1, $t1, $s1
sll $t1, $t1, 2
sw $t2, 0($t1)
addi $s1, $0, 3
j LOOP
DONE

Resources