nested loop problem of mips:Printing a pattern and gettin an error in my code - mips

the following mips code is of printing a number pattern like this: 1 12 123 1234 12345 i cannot find the error in my code.I don't understand what is missing.
main:
li $t0,1
li $t1,6
li $t2,1
li $t3,6
out:
beq $t0, $t1, exit
in:
beq $t2,$t3, exit
move $a0,$t2
li $v0, 1
syscall
addi, $t2, $t2, 1 #counter
j in
lineloop:
addi $t0,$t0,1
li $v0, 4 # print new line
la $a0, newline
syscall
j out
exit:

you have not changed $t2 to 1 in the last loop so that it starts printing from 1 everytime the line changes.
Also in the "in" loop you have not added the last loop instead you wrote exit which will make it leave the outer loop as well so you have to write it like this:
beq $t2,$t3,lineloop
SO the final code looks something like this
main:
li $t0,1
li $t1,6
li $t2,1
li $t3,6
out:
beq $t0, $t1, exit
in:
beq $t2,$t3, lineloop
move $a0,$t2
li $v0, 1
syscall
addi, $t2, $t2, 1 #counter
j in
lineloop:
addi $t0,$t0,1
li $v0, 4 # print new line
la $a0, newline
syscall
li $t2,1 #changes value in $t2 back to 1
j out
exit:

Here's some C code equivalent to what you're working on:
for ( int i = 1; i != 6; i++ ) {
   for ( int j = 11; j != 62; j++ ) {
     printf ( "%d", j );
   }
   printf ( "\n" );
}
1  The inner loop should be fully nested within the outer loop — that includes its initialization part.  Your assembly code has relocated inner loop initialization out of its proper context, and placed it with the (initialization section of the) outer loop:
for ( int i = 1, int j = 11; ; i != 6; i++ ) {
   for ( ; j != 62; j++ ) {
     printf ( "%d", j );
   }
   printf ( "\n" );
}
Without the inner loop's initialization being repeated it won't function equivalent to the above C code.  You only need move your li $t2, 1 to the proper location:
#li $t2,1 # improper placement of inner loop initialzation
li $t3,6
out:
beq $t0, $t1, exit
li $t2, 1 # here's where inner loop's initialization belongs
# properly nested within the body of the outer loop
# and immediately preceding the inner loop repeating body
in:
beq $t2,$t3, lineloop
2  In order to change the behavior of the inner loop each iteration, the inner loop should be dependent upon the outer loop's control variable, but you don't have it that way.  Without that, it will function independently, yielding 12345 each time (so: 12345 12345 12345 12345 for output).  The inner loop should take a dependence on the outer loop's control variable to decide when to stop as follows:
for ( int i = 1; i != 6; i++ ) {
   for ( int j = 11; j != i+12; j++ ) {
     printf ( "%d", j );
   }
   printf ( "\n" );
}
So, compute i+1 and use that computation to stop the inner loop.
Let's note that you can remove the li $t3,6 since that isn't actually when to stop the inner loop.  Also i+1 needs to be computed for the inner loop to use, and could be done within in the inner loop itself, but it is invariant to the inner loop (i only changes in the outer loop), so that computation can be done inside the body of the outer loop instead of inside the body of the inner loop.
#li $t2,1 # improper placement of inner loop initialzation
#li $t3,6 # wrong termination value & computation location (its not constant)
out:
beq $t0, $t1, exit
li $t2, 1 # here's where inner loop's initialization belongs
# properly nested within the body of the outer loop
addi $t3, $t0, 1 # compute i+1 for the inner loop to use as when to stop
in:
#addi $t3, $t0, 1 # alternate location to compute i+1
# but would (re)compute the same value each time
# so can do this outside the inner loop instead as above
beq $t2,$t3, lineloop

Related

Reverse the order of user inputted string

goodafternoon, i made this code that takes in user input and reverses the order. the code works perfectly except when there is long user input such the alphabet. in this case the output excludes the last letter, i am unsure why this is and any insight would be greatly appreciated.
.data
.align 2
array: .space 80
size: .word 20
string: .space 20000
op: .asciiz "Enter n, followed by n lines of text:"
prompt: .asciiz ""
text: .asciiz "The values are:"
newline: .asciiz "\n"
.text
.globl main
main:
#prompt user for array length
li $v0,4
la $a0,op
syscall
jal new_line #output newline
#read in array count
li $v0,5
syscall
addi $s0,$v0,0 #$v0 contains the integer we read
add $t0,$zero,$zero #index of array
addi $t1,$zero,1 #counter=1
la $s2,string #load address of string storage area
read_string:
bgt $t1,$s0,L1 #if ($t1 > length) then array is done
# prompt the user for next string
li $v0,4
la $a0,prompt
syscall
#get the string
move $a0,$s2 #place to store string
li $a1,20
li $v0,8
syscall
#store pointer to string into array
sw $a0,array($t0)
addi $t0,$t0,4 #advance offset into pointer array
addi $t1,$t1,1 #advance iteration count
addi $s2,$s2,20 #advance to next string area
j read_string
#### here i want to print the array ####
L1:
addi $t1,$zero,1 #counter = 1
#output the title
la $a0,text
li $v0,4
syscall
jal new_line
while:
addi $t0,$t0,-4 #advance array index
bgt $t1,$s0,done #if no more strings to output then done
lw $t2,array($t0) #get pointer to string
#output the string
li $v0,4
move $a0,$t2
syscall
addi $t1,$t1,1 #advance count
j while
new_line:
la $a0,newline
li $v0,4
syscall
jr $ra
done:
li $v0,10
syscall
if the user enters a n value of 26 and following lines a b c d e f g h i j k l m n o p q r s t u v w x y z then the output should be z y x v u t s r q p o n m l k j i h g f e d c b a. However what is outputted is this: z y x v u t s r q p o n m l k j i h g f e d c b. so its missing the letter a
From what I can tell, the problem is that you're ending the loop one pass too early.
while:
addi $t0,$t0,-4 #advance array index
bgt $t1,$s0,done #if no more strings to output then done
Here, you're effectively comparing $s0 to 26, since that's the whole alphabet size.
But since you started $t1 as equaling 1, you're encountering an "off-by-one" error most likely.
When compilers implement a loop like while(count < 26), often they don't do this literally like you have done here. It's usually easier to do the following:
#read in array count
li $v0,5
syscall
addi $s0,$v0,1 #$v0 contains the integer we read plus 1.
# do your other stuff here
while:
addi $t0,$t0,-4 #advance array index
lw $t2,array($t0) #get pointer to string
#output the string
li $v0,4
move $a0,$t2
syscall
addi $s0,$s0,-1
bnez $s0,while
Since you're only adding 1 to your count variable it's easier just to make it a downward for-loop (as I said eariler, a C compiler typically does this even if you declare the loop as a while.)
Sometimes you just need to adjust your loop counter by 1 and that may be enough to fix things.

Bubble sort using MIPS by user input

I'm very new to MIPS.
Ok, so my whole program is, about a user enters 10 integers and then we store it in an array, we then calculate the min and max and then print out the unsorted array. Then we begin the bubble sort algorithm where I'm struggling at right now, im not gonna post my whole program because of its a lot, I'm only gonna post my bubble sorting algorithm.
This is my thought process of my algorithm, so first i initialize my counters and size of my array.
then I begin my outer loop which is branch to endouter if i == size,
then inner loop, branch to endinner if j == size,
then get both of my indexes that ima compare,
compare then and if my first index is greater than the second index, then swap. I'm getting an endless loop of exception for some reason, anyone helps?
#BubbleSort
li $t2, 0 #outer counter = 0 i
li $t1, 9 #size of array 0-9
li $t8, 1 #inner counter j
outer: beq $t2,$t1, endouter #branch to endouter if i < 0
inner: beq $t8,$t1, endinner #branch to endinner if j < size
li $t9, 1
sub $t9,$t8, $t9 #j - 1
lw $t4,array($t9) #load (j-1) into temp
lw $t5,array($t8) #load j into temp
blt $t4,$t5, noswap #if (j-1) < j branch to no swap
lw $t4,array($t8) #swithc (j-1) with j
lw $t5,array($t9) #swithc (j-1) with j
noswap: add $t8,$t8, 1 #increment j
j inner
endinner:
add $t2,$t2,1
li $t8, 1
j outer
endouter:
Try this :
li $t0, 0 #outer counter = 0 i
li $t1, 40 #9 #size of array 10 * 4 bytes
li $t8, 40 #inner counter j
outer: blt $t0,$zero, endouter #branch to endouter if i < 0
inner_j:
subiu $t8,$t8,4
li $t0,0
beq $t8,$t0,endouter #branch to endouter if j == i
inner_i:
lw $t4,array($t0) #load i into temp
lw $t5,array($t8) #load j-1 into temp
blt $t4,$t5, noswap #if (j-1) < j branch to no swap
sw $t4,array($t8) # swap
sw $t5,array($t0) # swap
addiu $t0,$t0,4
beq $t0,$t8,inner_j
j inner_i
noswap:
addiu $t0,$t0, 4 #increment j
beq $t0,$t8,inner_j
j inner_i
endouter:

How to get the address of a constant in MIPS?

I am trying to implement a for loop in mips like this:
# {
# int sum = 0;
# for(int x = 0; x < n; x++)
# if ( v[x] > 2 )
# sum += v[x];
# return sum;
# }
I have the loop correctly but I can't get the size of the loop right. The loops functions as intended but it runs one extra time. It should be printing a 31 but it ends up running the loop an extra time and getting a 10 from somewhere then getting a 41 as the end result.
Here is my code:
.eqv SIZE 8
values: .half 6, 5, 1, 9, -2, 3, 8, 2
endl: .asciiz "\n"
endv:
# -------------------------------------------------# text/code section
.text
.globl main
main:
# ------
#TODO: call doSum(values, SIZE)
la $s0, values #&v[0]
la $s7, endv
li $a0, 0 #sum = 0
loop:
lh $t1, ($s0) #v[X]
li $t2, 2 #$t2 = 2
bgt $t1, $t2, sumPlus
j increment #skip over sumPlus if not > 2
sumPlus:
add $a0, $a0, $t1 #sum += v[x]
increment:
add $s0, $s0, 2 #x++
blt $s0, $s7, loop
You have a line feed character and a null terminator (endl: .asciiz "\n") before endv, so you're including them in the array that you're summing from. I don't know much about MIPS, but because \n has the value 10 (dec) and the \0 has the value 0. I assume that they're bytes and this is being run on a machine that is little-endian, so as a signed halfword they're interpreted as 10 (dec).
I think that just moving the endv label to the actual end of the array (just before endl:) will solve this.

When writing in MIPS, I am unsure whether to use li or addi. I am still unclear what the difference is.

For example, I have a piece of C code that I am trying to convert to MIPS for practice, but for the variable count, I don't know whether to use addi $t0,0 or li $t0, 0. Could I use either either or? And what is the difference?
Void haarPredict (int vector[], int N)
{
int half = N >> 1;
int count = 0;
for(int i = 0; i < half; i++)
{
int predictVal = vector[i];
int j = i + half;
vector[j] = vector[j] - predictVal
}
}
This is what I have so far after converting the above code to MIPS. Assuming $a0 is vector[] and $a1 is N. Again, I am not sure if li or addi is the correct thing to use.
srl $t0, $a1, 1 #t0 holds half. half = N >> 1
addi $t1, $t1, 0 #t1 holds count. count = 0
addi $t2, $t2, 0 #t2 holds i. i = 0
loop: slt $t3, $t2, $t0 #t3 holds 1 if i < half
beg $t3, $zero, exit #exit if t3 == 0
lw $t4, 0($a0) #t4 holds predictValue
addi $a0, $a0, 4 #4 bytes for next word address
addi $t5, $t2, $t0 #t5 holds j. j = i + half
lw $t6, $t6, $t4 #vector[j]=vector[j]-predivtVal
addi $t2, $t2, 1 #i++
j loop
exit: jr $ra
The li (Load immediate) instruction loads a specific numeric value into a register.
The addi (Add inmediate) adds a register and a sign-extended immediate value and stores the result in a register.
So, unless you are 100% sure a register has a zero value, then you shouldn't use an addi instruction to set a register.
For example:
addi $t1, $t1, 0 #t1 holds count. count = 0
You don't know if $t1 is zero at that particular moment. If thats a subroutine, you might be using a garbage value of $t1 (a value from before the invocation of the subroutine, before the jump to the address of the subroutine).
So the safe way is to set the register with li (thus, count=0), not taking into consideration the previous value of the register.

Mips loop iteration

I have the following code which is effectively supposed to increment from 1-6 while doing arithmetic on a variable.
An example in C would be
int res = 0;
for(i=1; i<6; i++) {
res += i*i+1;
}
Now for my mips code:
.data
res: .word 0
.text
li $t0, 1 #int j = 1
loop:
beq $t0, 6, end #for(int j = 1; j < 6; j++)
add $t0, $t0, 1
mul $t1, $t0, $t0 #res += j * j + 1
addi $t1, $t1, 1
sw $t1, res
b loop
end:
li $v0, 1 #checking the end result
la $a0, res
syscall
li $v0, 10 #graceful exit
syscall
For some reason the result I get towards the end is ~300 million and that is obviously wrong. Does anyone see what the issue is? Im fairly new to mips assembly.
Thanks
You've got a few problems here.
Firstly, the line you have marked #res += j * j + 1 is only multiplying, there is no addition involved.
You seem to be attempting to use res to store a running total but you are overwriting it in each iteration. In fact, there shouldn't be any need to store res in memory, a register is much more appropriate for this purpose.
.text
main:
move $t2 $zero
li $t0 1
loop:
mul $t1 $t0 $t0
addi $t1 $t1 1
add $t2 $t2 $t1 # res += i * i + 1
addi $t0 $t0 1
blt $t0 6 loop
# print res
li $v0 1
move $a0 $t2
syscall
# exit
li $v0 10
syscall
Prints 60.
You want to do something like this.
I am assuming you wanted something like this:
int res = 0;
for(i=1; i<6; i++) {
res += i*i+1;
}
.data
res: .word 0
.text
li $t0, 1 #int i = 1
loop:
bgt $t0, 6, exit
add $t0, $t0, 1
mul $t1, $t0, $t0
addi $t1, $t1, 1
sw $t1, res // I'm sure you can just move here
b loop
exit: