So I've trying for quite awhile to debug this after I finish coding this bubble sort code to MIPS and I can't seem to pin point what the problem might be with my logic. Note this is only a snippet. If you feel like some parts of missing, that is because I'm dealing with each part of the program individually. Assume that I already have an unsorted array filled with 12 numbers that I must sort.
I'm using mars_4.5 to check my output.
High-Level Code:
// arr[i] will be in the correct spot after every iteration
for (int i = n-1; i > 0; i--)
for (int j = 0; j < i; j++) // Put arr[j] and arr[j+1] in order
if (arr[j] > arr[j+1]) // If they are out of order, swap them
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
Important information:
# $s1 = n size of the array
# $s2 = i outer loop counter
# $s3 = j inner loop counter.
# $s5 is the address of the size (= 12)
# $s0 is the starting address of the array
Edit: The MIPS code is working now.
MIPS Code:
lw $s1, 0($s5) # Load the size of the array into $s1.
addi $s2, $s1, -1 # Perform the initialization i = n - 1
For1: # outer for loop
bltz $s2, Exit # Checks if i < 0. If true, exit out of the outer for loop.
add $s3, $zero, $zero # sets j to zero after each iteration of the inner loop.
j For2 # executes the nested for loop.
Update1:
addi $s2, $s2, -1 #i--
j For1 # exceute back to the outer for loop.
For2: # inner for loop
slt $t0, $s3, $s2
bne $t0, 1, Update1 # If the inner loop fails, go back to outer loop
sll $t3, $s3, 2
add $t3, $s0, $t3
lw $t1, 0($t3) # $t1 = arr[j]
lw $t2, 4($t3) # $t2 = arr[j + 1]
slt $t0, $t2, $t1
bne $t0, 1, Update2 # if the conditional fails
sw $t2, 0($t3) # store contents of $arr[j + 1] into arr[j]
sw $t1, 4($t3) # store contents of $arr[j] into arr[j + 1]
Update2:
addi $s3, $s3, 1 # j++
j For2
Exit:
Every time I run the assembler in mars, there is no output for a very long time. Now I know that bubble sort is very inefficient when it comes to large arrays but this is only 12 elements. So my guess is something is wrong with the nested for loop.
You don't seem to leave spaces for delay slots after jump and branch instructions.
Related
I'm writing a program in MIPS to get the target's location from the ascending array (1-10) using both linear search and binary search. Right now, I manage to complete binary algorithm with the correct output, but for the linear, some of the integers in the array result a incorrect output.
My output right now:
//Let's say the target to find = 2
1 //from binary search
-1 //from linear search, result -1 when can't find target
On the other hand, how do I keep the numbers of comparison of each algorithm? I know that linear case is O(n), basically how many times the loop run. But I'm not sure about binary search.
Please help me.
.data
array: .word 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
length: .word 10
newline: .asciiz " \n"
.text
main:
la $a0, array # Load array into $a0
li $a1, 0 # Load 0 into a1
lw $a2, length # Load length into $a2
li $a3, 4 # Load item to search for into $a1
jal binarySearch
jal linear
move $a0, $v0 # Move $v0 into $a0
li $v0, 1 #print location of binary search
syscall #print
la $a0, newline
li $v0,4 #newline
syscall
move $a0, $v1
li $v0,1 #print location of target by linear search
syscall
la $a0, newline
li $v0,4 #new line
syscall
li $v0, 10
syscall
#length of the array: $a2
####################################
# Binary Search #
####################################
# Input: #
# $a0, the array of words #
# $a1, left index #
# $a2, right index #
# $a3, item to search for #
####################################
# Output: #
# $v0, index of item (-1 if failed)#
####################################
# Used Registers: #
# $t0, Midpoint #
# $t1, used in math #
####################################
binarySearch:
addi $sp, $sp, -4 # Lower stack pointer
sw $ra, 0($sp) # Store return address
blt $a2, $a1, binaryFailed # If r < l then we failed
# addi $t7, $t7, 1
add $t0, $a1, $0 # Move left into t0
sub $t1, $a2, $a1 # Right - Left
srl $t1, $t1, 1 # (Right - Left) / 2
add $t0, $t1, $t0 # left + (right - left) / 2 (MID)
sll $t1, $t0, 2 # Convert index into word format
add $t1, $t1, $a0 # Add offset into array
lw $t1, 0($t1) # Load array element into memory
beq $t1, $a3, binaryFound # The element was found
bgt $t1, $a3, binaryGreater # The element is greater than the pointer
addi $a1, $t0, 1 # Set left to mid+1
j binarySearch # Search with new variable
j binaryEnd
binaryFound:
add $v0, $t0, $0 # Move mid answer into memory
#move $s7, $t7
j binaryEnd # End
binaryGreater:
addi $a2, $t0, -1 # Set right to mid-1
jal binarySearch # Search with new right
j binaryEnd
binaryFailed:
li $v0, -1 # Load failed value
j binaryEnd # Load end
binaryEnd:
lw $ra, 0($sp) # Load the return
addi $sp, $sp, 4 # Raise stack poiner
jr $ra # Return
####################################
# End Binary Search #
####################################
###################################
#. Linear Search #
###################################
linear:
li $s4, 0 #index i = 0
j linearLoop
linearLoop:
bge $s4, $a2, linearFailed #if $t0 > target, it jumps out of loop
lw $t1, 0($a0) #load the array into t1
beq $t1, $a3, linearFound #if array[element] = target, found target
addi $a0, $a0, 4 #add 4 to the array size
addi $s4, $s4, 1 #add one to the index
j linearLoop #SOS THIS IS NO FUN !!!!!#
linearFound:
move $v1, $s4 #move $t0 into $v1
j exitLoop
linearFailed:
li $v1, -1
j exitLoop
exitLoop:
jr $ra
#############################
# END LINEAR SEARCH #
############################
i want to write a program that use russian peasant multiplication with mips but i face some problems
/ A method to multiply two numbers using Russian Peasant method
unsigned int russianPeasant(unsigned int a, unsigned int b)
{
int res = 0; // initialize result
// While second number doesn't become 1
while (b > 0)
{
// If second number becomes odd, add the first number to result
if (b & 1)
res = res + a;
// Double the first number and halve the second number
a = a << 1;
b = b >> 1;
}
return res;
}
I translated it in mips with MARS environment
.data
msg1: .asciiz "give your first number"
msg2: .asciiz "give the second number"
.text
#a = $t1
#b = $t2
li $t3,0 #p
li $s1,1
li $s2,2
#display msg1
li $v0,4
la $a0,msg1
syscall
#read first number
li $v0,5
syscall
move $t1,$v0
#display msg2
li $v0,4
la $a0,msg2
syscall
#read second number
li $v0,5
syscall
move $t2,$v0
WHILE:
bgt $t2,$zero,DO
move $a0,$t3
li $v0,1
syscall
li $v0,10
syscall
DO:
div $t2,$s2
mfhi $s0 #r $s0 = $t2 / $s2
beq $s0,$s1,INC # IF b MOD 2 = 1 I jump to INC
#a = 2xa
mul $t1,$t1,$s2
div $t2,$s2
mflo $t2 # b = $t2/$s2
j WHILE
# i = i+ 1
INC:
add $t3,$t3,$t1
jr $ra
please , someone could help me ?
to fix "Error in : invalid program counter value: 0x00000000"
i searched how to fix it but i have a problem with $ra
how to save propertly the return address in $ra ?
the jr $ra will jump to the address stored in register $ra, which was not set by your code, so it is still zero (initial register value from MARS before running your code). The CPU will then jump to address zero, which is failure, and reports that.
To use jr $ra to "return" from some code, you have to first use jal instruction (or other instruction modifying $ra content) to set up $ra. Also when you want to nest several jal "subroutines calls", you have to store/restore the $ra of the outer calls around to not lose the value by the next nested jal.
Following code is example of both using paired jal+jr $ra for "subroutine"-like call, and also example how straightforward the implementation of such algorithm is in assembly (because actually that C source is more like assembly in C, so your inexperience made you take a very convoluted and complex approach of something what can be implemented in assembly almost 1:1 with the original C source):
.text
main: # just minimal "main" to verify the code works
# read two integers as input
li $v0,5
syscall
move $a0, $v0 # $a0 = a
li $v0,5
syscall
move $a1, $v0 # $a1 = b
# call the russianPeasant subroutine
jal russianPeasant # $v0 = a * b
nop
# output result
move $a0, $v0
li $v0, 1
syscall
# terminate
li $v0, 10
syscall
And the subroutine itself, which can be called with arguments in a0 and a1 and returns result in v0.
# input: $a0 = a, $a1 = b
# output: $v0 = a * b
russianPeasant:
li $v0, 0 # res = 0
beq $a1, $zero, russianPeasant_b_zero
nop # neutralize "Delayed branching" setting ("nop" works with both ON/OFF setting)
russianPeasant_while_b:
andi $at, $a1, 1 # test if b is odd, for even b skip the res = res + a
beq $at, $zero, russianPeasant_b_even
nop # but such neutralization comes with performance cost of course
add $v0, $v0, $a0 # res = res + a
russianPeasant_b_even:
sll $a0, $a0, 1 # a = a << 1
srl $a1, $a1, 1 # b = b >> 1
bne $a1, $zero, russianPeasant_while_b # repeat when (b != 0)
nop # on real MIPS production code the instructions are reordered to avoid useless nop
russianPeasant_b_zero:
jr $ra # return res
nop
The code is intentionally putting after each branch nop instruction to make it work with both delayed branching setting ON or OFF.
Try to use MARS help (F1) with instruction description to figure out, how it works, also use the single-step feature of the debugger to watch out the code in action, one instruction at time, observing all the register values and code flow.
On real MIPS CPU with the delayed branching the code can be optimized like this (you can switch "Delayed branching" ON in MARS settings to make it simulate real MIPS CPU with this somewhat confusing behaviour .. from your original source it looks like you are learning the MIPS assembly without delayed branching, which is certainly reasonable approach for somebody just starting with assembly, but that's not how real MIPS CPU works):
# input: $a0 = a, $a1 = b
# output: $v0 = a * b
russianPeasant_real_MIPS: # second variant supporting delayed branching like real MIPS CPU
beq $a1, $zero, russianPeasant2_b_zero
li $v0, 0 # res = 0
russianPeasant2_while_b:
andi $at, $a1, 1 # test if b is odd, for even b skip the res = res + a
beq $at, $zero, russianPeasant2_b_even
srl $a1, $a1, 1 # b = b >> 1
add $v0, $v0, $a0 # res = res + a
russianPeasant2_b_even:
bne $a1, $zero, russianPeasant2_while_b # repeat when (b != 0)
sll $a0, $a0, 1 # a = a << 1
russianPeasant2_b_zero:
jr $ra # return res
nop
This is my first attempt at MIPS and I designed a pseudocode description of a possible algorithm. The code outline is as follows:
main: Print: "Please input a value for N ="
read v0
If(v0 >0)
{
t0 = 0
while(v0 > 0) do
{
t0 = t0 + v0
v0 = v0 +v0 -1
}
print: The sum of integers from 1 to N is = ", t0
go to main
}
else
print: "Honest Abe"
so, with this outline, I made somewhat of an attempt to translate this into MIPS but the whole storing variables and reading them in is confusing me. My attempt of the real code is:
.data
Prompt: .asciiz "\n Please input a value for N="
Result: .asciiz "\n The sum of integers from 1 to N is "
Bye: .asciiz "\n ***Honest Abe***"
.text
main:
li $v0,4 #load $v0 with print_string code
la $a0, Prompt #load $a0 with the message to be displayed
syscall
bgz $v0, else
li $t0, 0
while:
bgz $v0
add $t0,$t0,$v0
addi $v0,$vo -1
From here, I do not understand how the for loops work in MIPS and I do not understand if this is even the correct approach. The whole idea of reading in numbers and then storing the integers isn't processing in my head. Also, I am unsure if I am using the appropriate commands for the loop. Help to any of these questions would be great!
Your algorithm seems a bit odd and too much of a hassle, if I got right what you wanted to do.
You can compute a sum until a certain number with a simple for loop. As in C:
scanf("%d", &num);
for(i=0;i<num;i++)
sum=sum+i;
like an array.
With a little help from the reference card of MARS, you can end up with something like this:
.text
main:
li $v0, 5 #read a number from the keyboard
syscall
addi $s0, $v0, 1 #s0= the number from the keyboard + 1 (the limit)
li $t0, 1 #counter set to 1
loop:
beq $t0, $s0, exit #if the counter becomes equal tothe limit, exit the loop
add $t1, $t1, $t0 #add the counter to the sum to this point
addiu $t0, $t0, 1 #add 1 to the counter for each loop
j loop #go to the "loop" label above
exit:
li $v0, 1
move $a0, $t1
syscall
li $v0, 10
syscall
Trying to change the following C code into MIPS Assembly. I know it is very basic code, however my professor has yet to show us any of this...
void main(){
int sum = 0;
int high = 0;
int array[5];
for(int i = 0; i<5; i++){
array[0] = i;
sum = sum + i;
}
if(sum >10)
high = 1;
else
high = -1;
}
This is the MIPS code. I am not sure why it is not working correctly.
.data
sum: .word 0
high: .word 0
array: .word 0 0 0 0 0
.text
li $t0, 5 # constant for loop
li $t1, 0 # counter
for: lw $t1, array
add sum, $t1, sum
add $t1, 1, $t1
blt $t1, $t0, for
bgt sum, 10 , if
if: lw high, 1
ble sum, 10, else
else: lw high, -1
sw sum, high, array
The problem was with registers and with afew bugs like exiting loop. I modified your code and present you the solution. kindly let me know if there are any errors
basically the array address was not updated and counter register was used twice
.data
sum: .word 0
high: .word 0
array: .word 0 0 0 0 0
.text
main:
li $t0, 5 # constant for loop
li $t1, 0 # counter loop index i=0
li $t3,0 #sum=0
la $t4, array
for:
lw $t5, ($t4) #get array[i]
add $t3, $t5, $t3 #sum++
add $t1,$t1,1 #i++
add $t4,$t4,4 #update array address
blt $t1, $t0, for #if i<5 ,continue
sw $t3,sum #save sum
bgt $t3, 10 , if
j else
if:
li $t6,0
add $t6,$t6,1
sw $t6,high #save high
li $v0,10
syscall
else:
li $t6,0
add $t6,$t6,-1
sw $t6,high
li $v0,10
syscall
.end main
Supposed to count number of R, I, and J instructions in the code starting from the first line in main. I have updated the code to all suggestions but results do not seem correct still. :(
EDIT: added those syntax changes, the variables seem to be working better, but the loop doesn't seem to be properly running through itself at all. Any noticeable problems anyone sees?
EDIT2: How do i change the loop to make $t1 rotate through 1 instruction set at a time? THAT is my key problem right now other than the silly syntax from a new mips user.
EDIT3: I have updated the code to all suggestions but results do not seem correct still. :(
.text
Main:
la $t1, 0x400000
li $t5, 0
li $t3, 2
li $t4, 3
li $t2, 0
la $s0, 0x400000
loop:
lw $t1, 0($s0)
addi $s0, 4
addi $t5, 1
beq $t5, 20 exit
srl $s4, $t1, 26
beq $s4, $t2 R
beq $s4, $t3 J
beq $s4, $t4 J
addi $s3, 1
j loop
R:
addi $s1, 1
j loop
J:
addi $s2, 1
j loop
exit:
sw $s1, RType
sw $s2, JType
sw $s3, IType
jr $ra
I see a set of problems:
1) Initialize the registers (don't assume register initial value is zero). For example, addi $t5, 1 requieres a previous add $t5, $zero, $zero (or li to the value you want).
2) the beq jump instruction requires two registers. For example, you have to change beq $s4, 3 J by:
li $t6, 3
beq $s4, $t6, J
In addition to Miguel's points I'd like to add that you're using the instruction set in a strange manner. Perhaps your assembler is capable of figuring out what you meant, but it's still makes the code confusing to read.
lw $t3, 2
lw $t4, 3
These should be li. The lw instruction is used for loading words from memory.
la $t0, 4($t1)
This should be lw, and unless you only want to check the instructions starting from address 0x400004 you should drop the offset.
I also can't seem to find anywhere where you increment $t1, so you'll be using the same address for every iteration of the loop.