Changing C to MIPS - mips32

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

Related

How to debug a MIPS code? Affectation issues [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I am trying to replicate this Java function in MIPS:
static int gmul(int a, int b) {
int p = 0;
for (int i = 0; i < 8; i++) {
if ( (b & 1) == 1) p = p ^ a;
a = a << 1;
if ( a > 255 ) a = a ^ 283;
b = b >> 1;
}
return p ;
}
I have this MIPS code:
.data
demanda:
.asciiz "Enter a first number"
demandb:
.asciiz "Enter a second number"
.text
main:
la $a0, demanda
li $v0, 4
syscall
li $v0, 5
syscall
move $t2, $v0
la $a1, demandb
li $v0, 4
syscall
li $v0, 5
syscall
move $a1, $v0
move $a0, $t2
li $v0, 0 # this is p in the java version
li $t0, 0
jal for
li $v0, 1 # Print p
syscall
li $v0, 10
syscall
for:
bge $t0,8,finfor # if i>=8
addi $t0, $t0, 1 # Incrementation
andi $t2, $a1,1 # Preliminary condition calculation: (b & 1) == 1 in Java
beq $t2,0, bEven # if ( (b & 1) == 1) in java
xor $v0, $a0, $v0 # Execution p = p ^ a in Java
bEven:
sll $a0, $a0, 1 # Execution a = a << 1 in java
blt $a0,256, aSmall # if ( a > 255 )
xori $a0, $a0, 283 # Execution a = a ^ 283 in Java
aSmall:
srl $a1, $a1, 1 # Execution b = b >> 1 in java
j for
finfor:
jr $ra
I think the formulas are OK, as the program runs, but it does not returns p as expected. It seems that it rather return a (which is in $a0), which I don't understand as I have put p in $v0 and try to print this one.
But it is not the first time I am struggling with registers and affectations in MIPS.
Any help is welcome and thanks in advance.
Found it. The trick in MIPS is that the print out command li $v0, 1 can override the value that you may want to print in $v0, that was my mistake. The work around is to store the data in another register first:
move $a2, $v0
...
move $a0 $a2
li $v0, 1
syscall
Or preferably, not using $v0 for the variable you need to handle.
And the step by single-step functionality really helps to find out mistakes.

write russian peasant multiplication with mips

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

Trouble With Division Algorithm

I'm currently trying to code a function to divide integers in MIPS. I am supposed to use a division algorithm using this flowchart:
Here is the code that I have created:
.data
.text
main:
addi $a0, $0, 7
addi $a1, $0, 4
jal divide
addi $a0, $v1, 0
addi $v0, $0, 1
syscall
addi $v0, $0, 10
syscall
divide: # $a0/$s0 = Dividend-Remainder / $a1/$s1 = Divisor / $v1 = Quotient
# Initialize
addi $s0, $a0, 0
addi $s1, $a1, 0
addi $t0, $0, 0 # Counter = $t0
addi $t1, $0, 33 # Number of loops = $t1
# Start of loop
start:
# Step 1 : Subtract the divisor register from the remainder register and place in remainder register
sub $s0, $s0, $s1
# Step 2 : Test the remainder register. If < 0 go to part B, otherwise go to part A
addi $t3, $0, 0
slt $t2, $t3, $s0
beq $t2, $zero, partB
# Step 2A : Shift quotient register to the left, and make least significant bit 1
sll $v1, $v1, 1
ori $v1, $v1, 1
j after # Skip step 2B
# Step 2B : Add divisor to remainder and place sum in remainder register
partB:
add $s0, $s1, $s0
# Shift quotient register to left and make least significant bit 0
sll $v1, $v1, 1
after:
# Step 3 : Shift divisor right 1 bit
srl $s1, $s1, 1
# Increment counter
addi $t0, $t0, 1
# Test loop
bne $t0, $t1, start
jr $ra
I've been trying to catch my error but so far I have been unable to find it. When I run the code I get -1073741825 for the output.
Can someone check my logic and tell me what I'm doing wrong?

infinite nested for loop in MIPS

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.

Refined bitwise division algorithm in MIPS

Our assignment is to implement a refined bitwise division algorithm in MIPS.
Basically in the refined program, we load the dividend into the LO bits of the 64 bit register and the remainder will accumulate in the HI bits, which are originally all set to 0.
Then we simply test if HI (our remainder) > = divisor.
If our accumulating remainder is not currently > = divisor, we simply left shift the entire register and fill in with a 0. This is the beginning of our quotient. As the dividend bits move into HI, we will finally have a situation where HI > =
divisor. Then we will subtract the divisor from HI, shift the entire register left, and fill in with a 1.
In short, we can simply continue to shift all 64 bits left, filling in with a 0 when HI < divisor, subtracting the divisor from HI and filling in with a 1 when HI > = divisor.
In our homework, we are to write a program to accept two positive numbers (16 bit signed numbers) from the console, divide the former by the latter.
I put the divisor in $t0, the remainder in $t3, and the dividend in $t2. $t3 and $t2 are both 16 bits, and together they pair up to be a 32-bit register. Somehow, my program returns a really funky quotient: the least significant few bits are correct, but the most significant is always 1; somehow I can't get rid of the 1, no matter how many bits I shift left. For instance, if the dividend is 1000, by shifting left, instead of getting 0001 0000, I actually get 10000 - MSB is always 1. Could someone please take a look at my program and give me some suggestions? Thanks a lot!
.data
GETA: .asciiz "Enter Dividend: "
GETB: .asciiz "Enter Divisor: "
QT: .asciiz "Quotient = "
RM: .asciiz "Remainder = "
NL: .asciiz "\n"
.text
main:
li $v0, 4 #Prompt for a dividend
la $a0, GETA
syscall
li $v0, 5 #Read dividend from user
syscall
move $t2, $v0 #dividend in $t2
li $v0, 4 #Prompt for a divisor
la $a0, GETB
syscall
li $v0, 5 #Read divisor from user
syscall
move $t0, $v0 #divisor in $t0
addi $t4, $zero, 17 #counter
j Loop
Loop:
slt $t1, $t3, $t0 #if remainder < divisor, $t1=1
beq $t1, $zero, subroutine #remainder >= divisor, $t1=0
#if remainder < divisor
sll $t3, $t3, 1 #shift remainder by to left by 1
andi $t5, $t2, 32768 #get MSB of $t2
bne $zero, $t5, updateRemainder1 #MSB of $t2 is 1
#MSB of $t2 is 0
sll $t2, $t2, 1 #shift dividend to left by 1
addi $t4, $t4, -1 #decrease counter by 1
bne $zero, $t4, Loop
j Exit
subroutine:
sub $t3, $t3, $t0 #subtract divisor from remainder
sll $t3, $t3, 1 #shift remainder by to left by 1
andi $t5, $t2, 32768 #get MSB of $t2
bne $zero, $t5, updateRemainder2 #MSB of $t2 is 1
#MSB of $t2 is 0
sll $t2, $t2, 1 #shift dividend to left by 1
ori $t2, $t2, 1 #fill in LSB of $t2 with a 1
addi $t4, $t4, -1 #decrease counter by 1
bne $zero, $t4, Loop
j Exit
updateRemainder1:
ori $t3, $t3, 1 #set LSB of remainder to 1
sll $t2, $t2, 1 #shift dividend to left by 1
addi $t4, $t4, -1 #decrease counter by 1
bne $zero, $t4, Loop
j Exit
updateRemainder2:
ori $t3, $t3, 1 #set LSB of remainder to 1
sll $t2, $t2, 1 #shift dividend to left by 1
ori $t2, $t2, 1 #fill in LSB of $t2 with a 1
addi $t4, $t4, -1 #decrease counter by 1
bne $zero, $t4, Loop
j Exit
Exit:
sub $t2, $t2, 524288
li $v0, 4 #print out output line
la $a0, QT
syscall
li $v0, 1 #print out quotient
move $a0, $t2
syscall
li $v0, 4 #print out new line
la $a0, NL
syscall
li $v0, 4 #print out output line
la $a0, RM
syscall
li $v0, 1 #print out remainder
move $a0, $t3
syscall
li $v0, 10
syscall

Resources