C to MIPS conversion - c

In class we were learning how to convert C code to MIPS instructions, but i ran into a small problem. Just wanted some clarification as far as to what exactly the last line of the MIPS instructions was actually saying.
c:
do{
i=i-2;
}while(i>1);
mips:
DO: addi s1,s1,-2 // i=i-2
addi t0,t0, 1 // 1
slt t1,t0,s1 // 1<i
bne t1,$zero,DO // ???

do{
i=i-2;
}while(i>1);
The assembly code for while and do while loops test the opposite condition of the one given in high-level code. If that opposite condition is TRUE, the while loop exit.
addi $s1,$0,0 # i = 0
addi $t1,$0,1 # j = 1
while:
addi $s1,$s1,-2 #i = i -2
beq $s1,$t1,done #Branch to done If i = 1 (the opposite)
j while # jump to while for loop through again
done:

Related

from java to MIPS assembly for a simple while loop [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 25 days ago.
Improve this question
i need to convert a code from java to assembly, but it only prints the first message. but the last two message dosent print neither the message nor the numbers of the result.
the two code in java and assembly are that i wrote as follow :
the code in java:
/**
* this program count how many 10s can be in a givin number the return the extra or the remain that less than 10
*/
Scanner input=new Scanner(System.in);
System.out.println("enter num: ");//print input message
int num=input.nextInt();// user input
int numOf10=0;//counter
while(num>9){ //start of while loop
num-=10;//subtruct 10 from the input number
numOf10++;//add one to the counter
}
System.out.println("number of 10 is: "+numOf10);//print message contain
System.out.println("the remain: "+num);//print message contain
////////////////////////////////////////////////////////////////////////////////////
the code in assembly:
.data
EnterMessage: .asciiz"Enter the number:\n "
ResultMessage: .asciiz"number of 10 is:\n"
remainMessage: .asciiz"the remain:\n "
.text
main:
#ask user to enter input
li $v0,4
la $a0,EnterMessage
syscall
#read user input
li $v0,5
syscall
#save input
move $t0,$v0
#creat variables
#$t2=9
addi $t2,$zero,9
#counter=$t3=0
addi $t3,$zero,0
#jal loop
#while loop
loop:
ble $t1,$t2,exit
subi $t0,$t0,10
addi $t3,$t3,1
j loop
print:
#print ResultMessage num
li $v0,1
move $a0,$t3
syscall
#print ResultMessage
li $v0,4
la $a0,ResultMessage
syscall
#print remainMessage num
li $v0,1
move $a0,$t0
syscall
#print remainMessage
li $v0,4
la $a0,remainMessage
syscall
#close the program
exit:
#end
li $v0,10
syscall
Your problem is here:
ble $t1,$t2,exit
There are a couple of problems here. First, exit leads to:
#close the program
exit:
#end
li $v0,10
syscall
So you've jumped to the end without doing anything.
Second, you've chosen $t1 as one of your registers for the compare, which you never set up. So its value is currently unknown (most likely the kernel or OS or whatever zeroed all your registers prior to main but it's not a good practice to assume that.)
Let's think about what's being asked:
while(num>9){ //start of while loop
num-=10;//subtruct 10 from the input number
numOf10++;//add one to the counter
}
When translating a high-level language to assembly we don't have to do everything the same way it was written in the source language. In terms of goto we can think of this as:
loop_begin:
if (num <= 9) goto loop_exit;
num -= 10;
numOf10++;
goto loop_begin;
loop_exit:
You've got the right idea for the most part. The nice thing about MIPS is that the branching syntax isn't nearly as obtuse as other assembly languages, since you don't have to remember whether carry clear means less than or greater than etc.
loop:
ble $t0,$t2,print # if $t0 (num) <= 9 goto print
subi $t0,$t0,10 # num = num - 10
addi $t3,$t3,1 # counter++
j loop # goto loop
Edit: Fixed typo in comments in last section

Recursive Fibonacci MIPS

I started to read MIPS to understand better how my C++ and C code works under the computer skin. I started with a recursive function, a Fibonacci function.
The C code is:
int fib(int n) {
if(n == 0) { return 0; }
if(n == 1) { return 1; }
return (fib(n - 1) + fib(n - 2));
}
MIPS code:
fib:
addi $sp, $sp, -12
sw $ra, 8($sp)
sw $s0, 4($sp)
addi $v0, $zero, $zero
beq $a0, $zero, end
addiu $v0, $zero, 1
addiu $t0, $zero, 1
beq $a0, $t0, end
addiu $a0, $a0, -1
sw $a0, 0($sp)
jal fib #fib(n-1)
addi $s0, $v0, $zero
lw $a0, 0($sp)
addiu $a0, $a0, -1
jal fib #fib(n-2)
add $v0, $v0, $s0
end:
lw $s0, 4($sp)
lw $ra, 8($sp)
addi $sp, $sp, 12
jr $ra
When n>1 it goes until the code reaches the first jal instruction. What happens next? it return to fib label ignoring the code below (the fib(n-2) call will never be executed?)? If that happens, the $sp pointer decreases 3 words again and the cycle will go until n<=1. I can't understand how this works when first jal instruction is reached.
Can you follow how the recursion works in C?
In some sense, recursion has two components: the forward part and the backward part.  In the forward part, a recursive algorithm computes things before the recursion, and in the backward part, a recursive algorithm computes things after the recursion completes.  In between the two parts, there is the recursion.
See this answer: https://stackoverflow.com/a/71551098/471129
Fibonacci is just slightly more complicated as it performs recursion twice, not just once as in the above list printing example.
However, the principles are the same:  There is work done before the recursion, and work done after (either of which can be degenerate).  The before part happens as code in front of the recursion executes, and the recursion builds up stack frames that are placeholders for work after the recursion yet to be completed.  The after part happens as the stack frames are released and the code after the recursive call is executed.
In any given call chain, the forward part goes until n is 0 or 1, then the algorithm starts returning back to the stacked callers, for whom the backward part kicks in unwinding stack frames until it returns to the original caller (perhaps main) rather than to some recursive fib caller.&npsp; Again, complicated by use of two recursive invocations rather than one as in simpler examples.
With fib, the work done before is to count down (by -1 or -2) until reaching 0 or 1.  The work done after the recursion is to sum the two prior results.  The recursion itself effectively suspends an invocation or activation of fib with current values, to be resumed when a recursive call completes.
Recursion in MIPS algorithm is the same; however, function operations are spread out over several machine code instructions that are implicit in C.
Suggest single stepping over a call to fib(2) as a very small example that may help you see what's going on there.  Suggest first doing this in C — single step until the outer fib call has full completed and returned to the calling test function (e.g. main).
To make the C version just a bit easier to view in the debugger you might use this version:
int fib(int n) {
if (n == 0) { return 0; }
if (n == 1) { return 1; }
int fm1 = fib(n-1);
int fm2 = fib(n-2);
int result = fm1 + fm2;
return result;
}
With that equivalent C version, you'll be able to inspect fm1, fm2, and result during single stepping.  That will make it easier to follow.
Next, do the same in the assembly version.  Debug single step to watch execution of fib(2), and draw parallels with the equivalents in C.
There's another way to think about recursion, which is ignore the recursion, pretending that the recursive call is to some unrelated function implementation that just happens to yield the proper results of the recursive function; here's such a non-recursive function:
int fib(int n) {
if (n == 0) { return 0; }
if (n == 1) { return 1; }
int fm1 = fibX(n-1); // calls something else that computes fib(n-1)
int fm2 = fibX(n-2); // "
int result = fm1 + fm2;
return result;
}
With this code, and the assumption that fibX simply works correctly to return proper results, you can focus strictly on the logic of one level, namely, the body of this fib, without considering the recursion at all.
Note that we can do the same in assembly language — though the opportunities for errors / typos are always much larger than in the C, since you still have to manipulate stack frames and preserve critical storage for later use after the calling.
The code you've posted has a transcription error, making it different from the C version.  It is doing the C equivalent of:
return fib(n-1) + fib(n-1);

MIPS Assembly how to store values to an array?

I am trying to create a program where I can store up to 8 values in an array and then compare all these values to find the smallest number. For some reason my loop overwrites the first position in the array every time. Here's what I have. I then add 4 to $t1 on the loop so once it goes back around it should store the next integer in the space after that. I don't see what I'm doing wrong here?
.data
myArray: .space 32
Msg1: .asciiz "Enter an integer: "
.text
main:
# Print Message
li $v0, 4
la $a0, Msg1
syscall
# Prompt the user to enter an integer
li $v0, 5
syscall
# Store the first integer in $t0
move $t0, $v0
# Declare $t1 for the array position that the integer will be stored at
addi $t1, $zero, 0
# Store the integer in the array
sw $t0, myArray($t1)
#Add 4 to $t1 so store the next value in the next array position
addi $t1, $zero, 4
beq $t0, $zero, Exit
j main
Exit:
# Declare an exit to the program
li $v0, 10
syscall
First, let's start with a working algorithm in C:
int a [] = { /* array elements */ };
int n = /* count of number of elements */;
...
int currMin = 0;
for ( int i = 0; i < n; i++ ) {
int next = a[i]; // next array element to check
if ( next < currMin ) // is it smaller than what we've seen so far?
currMin = next; // yes: capture new min value
}
// on exit from the loop currMin holds the min value
Ok, now we'll make some simple logical transformations on the way to taking this to assembly language.  First, we remove the for loop in favor of the slightly simpler while loop construct.
int currMin = 0;
int i = 0;
while ( i < n ) {
int next = a[i]; // next array element to check
if ( next < currMin ) // is it smaller than what we've seen so far?
currMin = next; // yes: capture new min value
i++;
}
// on exit from the loop currMin holds the min value
Next, we'll transform the while loop into assembly's if-goto-label.  (We could work the if-then first instead; the order we transform doesn't matter.)
int currMin = 0;
int i = 0;
loop1:
if ( i >= n ) goto endLoop1;
int next = a[i]; // next array element to check
if ( next < currMin ) // is it smaller than what we've seen so far?
currMin = next; // yes: capture new min value
i++;
goto loop1;
endLoop1:
// on exit from the loop currMin holds the min value
Next, we'll do the if-then statement.  We could have done it first, that wouldn't change the analysis or results.
int currMin = 0;
int i = 0;
loop1:
if ( i >= n ) goto endLoop1;
int next = a[i]; // next array element to check
if ( next >= currMin ) goto endIf1;
currMin = next; // yes: capture new min value
endIf1:
i++;
goto loop1;
endLoop1:
Next we'll take this to assembly language.
First, assign variables to physical storage, here good choice is registers.  Mental map:
$a0 array a
$a1 element count n
$v0 currMin, the result
$t0 loop control variable i, also used as index
$t1 temporary variable "next"
Second, translate code as per the last above:
li $v0, 0 # currMin = 0
li $t0, 0 # i = 0
loop1:
bge $t0, $a1, endLoop1
# array reference, variable index: a[i], capture in "next"/$t1
sll $t9, $t0, 2
add $t9, $a0, $t9
lw $t1, 0($t9)
# the if-then inside the loop body
bge $t1, $v0, endIf1
move $v0, $t1 # capture newly seen lowest value
endIf1:
# finish the rest of the while loop, having the for-loop i++ here
addi $t0, $t0, 1
j loop1
endLoop1:
All that's left is to put some starting and ending code around that, assuming the register numbers match up.
The starting code for this would put the address of the array into $a0, and the count of elements into $a1.  Could use different registers of course, with appropriate modifications.
The ending code should expect the result in $v0, to print or otherwise.
The starting code would be entirely before this code, and the ending code entirely after.
Yes, there are a few steps — but each one is a relatively simple and logical transformation.  Logical transformations enable translating the C code, first staying in C but simplifying to make it easy to go right to assembly.
Remove for loops in favor of while loops.
Change all control structures into if-goto-label
Each control structure (if, while) can be changed one at a time, stay in C, and keep checking that the code continues to work in C!  Order of control structure transformations doesn't matter (inside out, outside in).
Translate simplified C code into assembly:
a. Map logical variables of C into physical storage of machine code
b. Convert statements & expressions from C into assembly language

while loop with 2 conditions to assembly code

I want to convert a while loop like that:
i=0;
while ( (s[i]!='t') && (i<=20) ) {
d[i]=c[i];
i++;
}
with 2 conditions to assembly code.
how can i do that?
below is the version with 1 condition in the while.
#################################################
# lab3_3a.s #
# while loop char!=ASCII 0 #
#################################################
.text
.globl __start
__start: # execution starts here
li $t1,0 # counter for string
li $s0,'t' # chararacter to end copy
while: lbu $t0,string($t1) # load a character
sb $t0,copy($t1) # copy character
beq $t0,$s0,end # if character to end copy then exit loop
addi $t1,$t1,1 # increment counter
b while # repeat while loop
end: li $t2,0
sb $t2,copy($t1) # append end character to copied string
la $a0,copy # display copy
li $v0,4
syscall
li $v0,10 # exit
syscall
.data
string: .asciiz "Mary had a little lamb"
copy: .space 80
thanks guys.
So, you've already successfully inverted one of the conditions and used it to jump out of the loop. Does it occur to you that you can do the same for the other?
li $t1,0 # counter for string
li $s0,'t' # chararacter to end copy
li $s1,20 # count to end copy
while: lbu $t0,string($t1) # load a character
sb $t0,copy($t1) # copy character
beq $t0,$s0,end # if character to end copy then exit loop
bgt $t1,$s1,end # if count exceeds limit then exit loop
addi $t1,$t1,1 # increment counter
b while # repeat while loop
i=0;
while ( (s[i]!='t') && (i<=20) ) {
d[i]=c[i];
i++;
}
Would contain two bugs, if s would be defined as char s[20];, first the (i<=20) would be one-too-much. It's very unusual to use <= in array length tests, it may be still correct if char s[21]; is defined, but then you have two different "magic numbers" in source, 20 and 21. And the second bug is, even if you have correct length test, the (s[i]!='t') will execute ahead of i validation, so at the last character you will have out-of-bounds access.
Anyway, that C be written in C in a bit more "assembly-like" way like this:
i=0;
while(true) { // Your "b while" is doing this already
if (20 < i) break; // test length first, to avoid s[21] (out of bounds)
if ('t' == s[i]) break; // test for "terminator" character
//^^ Yoda notation, to catch typo like (s[i]='t') as error
d[i]=c[i];
++i; // pre-increment form is more accurate to your intent
// As you don't need original value after incrementation
}
// breaks will jump here.
This should be easy to rewrite in assembly, try it...
edit: and your original assembly is not "while", but "do-while", i.e. copy of first byte will execute under all circumstances, which is not what the C example is doing.
edit2: meanwhile of course this assumes you know the boolean logic algebra, as every programmer has to. I.e. you know that:
!(A && B) <=> (!A) || (!B)

How to calculate the sum of even integers between two values in MIPS?

I am confused on how to convert C code to MIPS. I seem to to get the loops confused and I think I am possibly using the wrong command. The C code I made to do this is as follows:
int main()
{
int x, y;
int sum = 0;
printf("Please enter values for X and Y:\n ");
scanf("%d %d",&x,&y);
if (x > y)
{
printf("\n** Error");
exit(0);
}
while (x <= y)
{
if (x%2 == 0)
sum += x;
x++;
}
printf("\nThe sum of the even integers between X and Y is: %d\n\n",sum);
return 0;
}
My attempt at the MIPS translation is as follows:
.data
Prompt: .asciiz "Please enter values for X and Y:\n"
Result: .asciiz "The sum of the even integers between X and Y is: \n"
.text
li $v0,4 #load $v0 with the print_string code.
la $a0, Prompt #load $a0 with the message to me displayed
syscall
li $v0,5 #load $v0 with the read_int code for X
syscall
move $t0,$v0
li $v0,5 #load $v0 with the read_int code for Y
syscall
move $t1, $v0
while:
slt $t2, $t1,$t0 #$t1 = y $t0 = x
li $t3,2
div $t2,$t3
beq $t2,$0,else
add $s1,$s1,$t0 #s1 = s1 + x
addi $t0,$t0,1 #x++
j while
else:
li $v0,4
la $a0, Result
syscall
move $a0,$s1
li $v0,1
syscall
I think my error is in the loop in my MIPS code. My result keeps producing zero and I think my code is checking the loop and then just jumping to my else statement.
After further work, I got it to calculate the sum of all integers and I'm not exactly sure why it is doing so. Here is my most recent update:
while:
sle $t2, $t0,$t1 #$t1 = y $t0 = x
li $t3,2 #t3 = 2
div $t2,$t3 #$t2/2
beq $t2,$0, else #if ($t2/2 == 0), jump to the else, otherwise do else
add $s1,$s1,$t0 #s1 = s1 + x
addi $t0,$t0,1 #x++
j while
So now, if I enter 1 and 5, it calculates 1 and 3 is gives me 6 instead of just the even sum which should be just 2.
To answer my own question, the main confusion was with the the branches. I now understand that they kind of work like opposites so for example, I had to set the "beq" in my while loop to bnez so it would do the calculations when $t2 was != 0. Another minor fix was adding the increment outside of the loop. So, when $t2 != 0, I jump to my "else" which then incremented to find the next number. However, if the remainder was 0, it did the math of sum=sum + x. In conclusion, the main confusion came from thinking opposite about the branches. I now understand that if I wanted to say:
while(a1 < a2)
I would have to write it as
while:
bgeu $a1,$a2, done
addi "whatever"
b while
done:
do done stuff
Before this understanding, I was writing it as ble $a1,$a2,done and that is not the way it is to be typed. Logically, that says if a1 < a2...but it is really saying if a1 < a2, jump to the "done" and skip calculations. So I just had to think opposite.

Resources