MIPS array update - arrays

I have a problem in converting my C code to MIPS code. The original C code is below:
int A[4];
int i;
int diff;
for(i=0; i<3; i++){
diff = A[i+1] - A[i];
if (diff > 0)
A[i] = 5*A[i];
else
A[i+1] = -5*A[i];
}
And here is my MIPS Code below:
### MIPS PROJECT PART 1: ARRAY USING FOR LOOPS
.data
# allocate 16 bytes memory for 4 integer array
A: .space 16
.text
# Store array values in registers
addi $s0,$zero,2
addi $s1,$zero,4
addi $s2,$zero,6
addi $s3,$zero,8
# Index = $t0
addi $t0,$zero,0
# Store the first index and then store others by increasing $t0 by 4 bytes
sw $s0,A($t0)
addi $t0,$t0,4
sw $s1,A($t0)
addi $t0,$t0,4
sw $s2,A($t0)
addi $t0,$t0,4
sw $s3,A($t0)
main:
li $s4,0 # counter i = $s4
li $t2,3 # constant $t2
loop:
beq $s4, $t2, end # if t1 == 3 we are done
# diff = A[i+1] - A[i]
lw $t3,A($zero) # $t3 = A[i]
addi $t6, $zero,4 # next element A[i+1]
lw $t4,A($t6) # $t4 = A[i+1]
sub $t5,$t4,$t3 # dif = $t5 = A[i+1] - A[i]
# if (diff > 0)
bltz $t5, Else
# A[i] = 5*A[i]
add $t5,$zero,$t5
add $t5,$zero,$t5
add $t5,$zero,$t5
add $t5,$zero,$t5
sw $t5 A($zero)
Else:
# else A[i+1] = -5 * A[i];
addi $zero, $zero,4 #next element A[i+1]
#Loop body
addi $s4, $s4, 1 # add 1 to $s4
j loop # jump back to the top
end:
li $v0,10
syscall
The thing is that program does not update array values. Additionally I am restricted to use multiplication commands (mult or mull), that is why I am trying just to add it 5 times in control statement.

Your program has some bugs and is incomplete. I've created two versions. One with annotations for the bugs and a refactored one. Please pardon the gratuitous style cleanup.
Here's your asm program with annotations:
### MIPS PROJECT PART 1: ARRAY USING FOR LOOPS
.data
A: .space 16
# allocate 16 bytes memory for 4 integer array
.text
# Store array values in registers
addi $s0,$zero,2
addi $s1,$zero,4
addi $s2,$zero,6
addi $s3,$zero,8
# Index = $t0
addi $t0,$zero,0
# Store the first index and then store others by increasing $t0 by 4 bytes
sw $s0,A($t0)
addi $t0,$t0,4
sw $s1,A($t0)
addi $t0,$t0,4
sw $s2,A($t0)
addi $t0,$t0,4
sw $s3,A($t0)
# NOTE/BUG: this should go after the .text as the above code may be skipped
main:
li $s4,0 # counter i = $s4
li $t2,3 # constant $t2
loop:
beq $s4,$t2,end # if t1 == 3 we are done
# diff = A[i+1] - A[i]
# NOTE/BUG: this always loads from A[0]
lw $t3,A($zero) # $t3 = A[i]
# NOTE/BUG: this always loads from A[1]
addi $t6,$zero,4 # next element A[i+1]
lw $t4,A($t6) # $t4 = A[i+1]
sub $t5,$t4,$t3 # dif = $t5 = A[i+1] - A[i]
# if (diff > 0)
bltz $t5,Else
# NOTE/BUG: this is _not_ A[i] but is diff
# NOTE/BUG: this is _not_ 5* but higher (see below)
# A[i] = 5*A[i]
add $t5,$zero,$t5 # diff*2
add $t5,$zero,$t5 # diff*4
add $t5,$zero,$t5 # diff*8
add $t5,$zero,$t5 # diff*16
# NOTE/BUG: this always stores to A[0]
sw $t5,A($zero)
# NOTE/BUG: this falls through to the else case
Else:
# else A[i+1] = -5 * A[i];
# NOTE/BUG: this is just a nop because a $zero as destination reg does
# nothing
addi $zero,$zero,4 # next element A[i+1]
# Loop body
addi $s4,$s4,1 # add 1 to $s4
# NOTE/BUG: this stores nothing
j loop # jump back to the top
end:
li $v0,10
syscall
I rewrote the C code slightly to make the asm easier:
int A[4] = { 2, 4, 6, 8 };
void
calc(int *arr,int cnt)
{
int tmp;
int diff;
int *endp;
endp = &arr[cnt];
for (; arr < endp; arr += 1) {
tmp = arr[0];
diff = arr[1] - tmp;
if (diff > 0) {
tmp = 5 * tmp;
arr[0] = tmp;
}
else {
tmp = -5 * tmp;
arr[1] = tmp;
}
}
}
Here's the refactored asm code:
### MIPS PROJECT PART 1: ARRAY USING FOR LOOPS
.data
# allocate 16 bytes memory for 4 integer array
A: .word 2,4,6,8
space: .asciiz " "
nl: .asciiz "\n"
.text
main:
la $a0,A
li $a1,4
jal print
la $a0,A
li $a1,3
jal calc
la $a0,A
li $a1,4
jal print
li $v0,10
syscall
# calc -- calculate results
#
# arguments:
# a0 -- pointer to array
# a1 -- count of array
calc:
# get endp
sll $a1,$a1,2 # convert int count to byte count
addu $a1,$a1,$a0 # add in array base
j calc_start # start loop
calc_loop:
lw $t3,0($a0) # tmp = A[i]
lw $t4,4($a0) # A[i + 1]
sub $t5,$t4,$t3 # get diff
bltz $t5,calc_neg # < 0
add $t5,$t3,$t3 # tmp = A[i]*2
add $t5,$t5,$t5 # tmp = A[i]*4
add $t5,$t5,$t3 # tmp = A[i]*5
sw $t5,0($a0)
b calc_next
calc_neg:
neg $t3,$t3
add $t5,$t3,$t3 # tmp = A[i]*2
add $t5,$t5,$t5 # tmp = A[i]*4
add $t5,$t5,$t3 # tmp = A[i]*5
sw $t5,4($a0)
calc_next:
addiu $a0,$a0,4
calc_start:
bne $a0,$a1,calc_loop # more to do? if yes, fly
jr $ra # return
# print -- print results
#
# arguments:
# a0 -- pointer to array
# a1 -- count of array
#
# temporaries:
# a2 -- array pointer
# a3 -- array count
print:
move $a2,$a0
move $a3,$a1
j print_next
print_loop:
li $v0,4
la $a0,space
syscall
li $v0,1
lw $a0,0($a2) # get current value
syscall
addiu $a2,$a2,4
addi $a3,$a3,-1
print_next:
bnez $a3,print_loop
li $v0,4
la $a0,nl
syscall
jr $ra # return

Related

How to initialize the element of char array to a 2d array in mips?

According to the c code the value in the base address of array p is supposed to be equal value of base address of array str but they doesn't match I am still new to mips assbley so excuse me and feel free to do any edits thanks in advance
I think the problem is in loading bytes and storing bytes in the array but still can't figure it out,
I left extra parts of my assembly code in order to make it simple for the viewer of how the code is about but the part that I am asking about in specific is within the while loop
this is the psudo code
i=0;
j=0;
k=0;
while(j < my_strlen(str))
{
if (str[j] == ' ')
{
p[i][k] = '\0';
i++;
k = 0;
}
else
{
p[i][k++] = str[j];
}
j++;
}
this is my attempted mips code
.data
msg: .asciiz "Enter a string \n"
str: .space 30 # get the paragraph from the user
p: .space 400 #p[20][20]
stringlen: .asciiz "String length is "
blankSpace: .ascii " "
.text
main:
la $a3,blankSpace
lb $s4,0($a3) # $s4 =blankspace
la $s5,str # address start of str
la $s6,p # address start of p
la $s7,ptr1 # address start of ptrl
li $t9,20 # width =20
li $t0,0 # i=0
li $t2,0 # j=0
li $t3,0 #k=0
WHILE:
bge $t2,$s3,End_While # branch if j<=strlength
lbu $t7,($s5) # $t7 = str[j]
beq $t7,$s4,L # if str[j]== ' ' branch L
mul $t8,$t9,$t0 # width *i
add $t8,$t8,$t3 # width * i+k
add $t8,$t8,$s6 # base array (width *i+k)
sb $t7,($t8) # p[i][k++]=str[j]
addi $t3,$t3,1 # k++
L2: addi $t2,$t2,1 # j++
addi $s5,$s5,1 # str[j+1]
j WHILE
L: mul $t8,$t9,$t0 # array width * i
add $t8,$t8,$t3 # width * i+k
add $t8,$t8,$s6 # base array +(width * i+k)
sb $zero,($t8) # p[i][k++]='\0'
addi $t0,$t0,1 # i++
addi $t3,$zero,0 # k=0
j L2
End_While:
The whole programe
.data
msg: .asciiz "Enter a string \n"
str: .space 30 # get the paragraph from the user
ptr1 : .space 400 #ptr1[20][20]
p: .space 400 #p[20][20]
stringlen: .asciiz "String length is "
blankSpace: .ascii " "
next: .asciiz "\n Next while"
.text
main:
li $v0,4
la $a0,msg
syscall # printing the message to the user
li $v0,8
la $a0,str
li $a1,30
syscall # getting a paragraph from the user
li $v0,4
la $a0,stringlen #"String length is "
syscall
la $a0,str
jal strlen
addi $a0,$v0,0
addi $s3,$a0,0 #strlen value $s3
li $v0,1
syscall # calling the strlen function to count the string lengt
la $a3,blankSpace
lb $s4,0($a3) # $s4 =blankspace
la $s5,str # address start of str
la $s6,p # address start of p
la $s7,ptr1 # address start of ptrl
li $t9,20 # width =20
li $t0,0 # i=0
li $t2,0 # j=0
li $t3,0 #k=0
WHILE:
bge $t2,$s3,End_While # branch if j<=strlength
lbu $t7,($s5) # $t7 = str[j]
beq $t7,$s4,L # if str[j]== ' ' branch L
mul $t8,$t9,$t0 # width *i
add $t8,$t8,$t3 # width * i+k
add $t8,$t8,$s6 # base array (width *i+k)
sb $t7,($t8) # p[i][k++]=str[j]
addi $t3,$t3,1 # k++
L2: addi $t2,$t2,1 # j++
addi $s5,$s5,1 # str[j+1]
j WHILE
L: mul $t8,$t9,$t0 # array width * i
add $t8,$t8,$t3 # width * i+k
add $t8,$t8,$s6 # base array +(width * i+k)
sb $zero,($t8) # p[i][k++]='\0'
addi $t0,$t0,1 # i++
addi $t3,$zero,0 # k=0
j L2
End_While:
li $v0,4
la $a0,p
syscall
li $v0 ,10
syscall # Exit terminating the program
strlen:
addi $sp,$sp,-4
sw $ra,0($sp)
addi $t0,$0,0 # i=0
addi $t1,$0,0 # len=0
l: add $t2,$a0,$t0 # add base address to offset
lbu $t3,0($t2) # load base unsigned for char array
beq $t3,$0,finish # if value of array[i] = '\0'
addi $t0,$t0,1 # i++
addi $t1,$t1,1 # len++
j l
finish:
subi $s0,$s0,1
add $v0,$t0,$s0
lw $ra,0($sp) # restore old $s0
addi $sp $sp 4
jr $ra

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

Translating C function with args to MIPS: loop over an int array and count negatives

My question is about procedures in MIPS and using arguments.
I'm trying to translate this small C Function to MIPS and I wasn't sure if I was in the right track. This is the C function:
0 int countNegatives(int table[] , int n) {
1 int count = 0;
2 int i;
3
4 for (i=0; i<n; i++) {
5 if (table[i] <0) {
6 count++;
7 }
8 }
9
10 return count;
11 }
And this what I have on MIPS
main:
jal countNegatives
countNegatives:
li $t0, 0 #count = 0
li $t1, 0 #i = 0
loop:
bge $t1, $a1, endloop
sll $t2, $t1, 2 #$t2 = 4*i
add $t2, $a0, $t2 #$t2 = &table[i]
lw $t3, 0($t2) #temp = table[i]
bge $t3, $zero, endif
addi $t0, $t0, 1 #counter++
endif:
addi $t1, $t1, 1 #i++
endloop:
jr $ra
My code doesn't really run on QTSpim, and so I'm also trying to know if I'm missing any MIPS convention, and if I'm using the arguments in the procedure in a correct manner.
Thanks in advance if anyone can check the code out and see if something is wrong.
Except for some missing boilerplate, you were very close. Here's a version annotated with the bugs:
main:
# NOTE/BUG: a0/a1 are _not_ set up for the call
jal countNegatives
# NOTE/BUG: we just fall through into countNegatives again [which is bad]
countNegatives:
li $t0,0 # count = 0
li $t1,0 # i = 0
loop:
bge $t1,$a1,endloop
sll $t2,$t1,2 # $t2 = 4*i
add $t2,$a0,$t2 # $t2 = &table[i]
lw $t3,0($t2) # temp = table[i]
bge $t3,$zero,endif
addi $t0,$t0,1 # counter++
endif:
addi $t1,$t1,1 # i++
# NOTE/BUG: we need to loop here
endloop:
jr $ra
Here's a working version [with the added boilerplate]:
.data
arr: .word 10 20 -5 7 -6 0 1 -1 37
.text
.globl main
main:
la $a0,arr # point to array
li $a1,9 # array count
jal countNegatives
move $a0,$v0
li $v0,1
syscall
li $v0,10
syscall
# countNegatives -- count number of negatives
#
# RETURNS:
# v0 -- number of negative numbers found
#
# arguments:
# a0 -- pointer to array
# a1 -- array count
#
# temporaries:
# t1 -- index variable "i"
# t2 -- array offset / &table[i]
# t3 -- temp (value of table[i])
countNegatives:
li $v0,0 # count = 0
li $t1,0 # i = 0
loop:
bge $t1,$a1,endloop # i >= count? if yes, fly
sll $t2,$t1,2 # $t2 = 4*i
addu $t2,$a0,$t2 # $t2 = &table[i]
lw $t3,0($t2) # temp = table[i]
bge $t3,$zero,endif
addi $v0,$v0,1 # counter++
endif:
addi $t1,$t1,1 # i++
j loop
endloop:
jr $ra
Here's a just for fun version that uses slt instead of a conditional branch [and eliminates an extra jump inside the loop]:
.data
arr: .word 10 20 -5 7 -6 0 1 -1 37
.text
.globl main
main:
la $a0,arr # point to array
li $a1,9 # array count
jal countNegatives
move $a0,$v0
li $v0,1
syscall
li $v0,10
syscall
# countNegatives -- count number of negatives
#
# RETURNS:
# v0 -- number of negative numbers found
#
# arguments:
# a0 -- pointer to array
# a1 -- array count
#
# temporaries:
# t1 -- index variable "i"
# t2 -- array offset / &table[i]
# t3 -- temp (value of table[i])
countNegatives:
li $v0,0 # count = 0
li $t1,0 # i = 0
j loop_start # start the loop
loop:
sll $t2,$t1,2 # $t2 = 4*i
addu $t2,$a0,$t2 # $t2 = &table[i]
lw $t3,0($t2) # temp = table[i]
slt $t3,$t3,$zero # temp = (temp < 0)
add $v0,$v0,$t3 # counter += temp
addi $t1,$t1,1 # i++
loop_start:
blt $t1,$a1,loop # i < count? if yes, fly
jr $ra
Here's another version that uses pointer arithmetic instead of index variables.
Note that under the mips ABI, only the s* regs must be preserved by callee, so a0 and a1 are used as temporaries.
Also note that when adding addresses/pointers, as good practice, we want to use the unsigned versions of the add instructions (i.e. addu and addiu) to prevent [the unlikely possibility of] an overflow exception.
.data
arr: .word 10 20 -5 7 -6 0 1 -1 37
.text
.globl main
main:
la $a0,arr # point to array
li $a1,9 # array count
jal countNegatives
move $a0,$v0
li $v0,1
syscall
li $v0,10
syscall
# countNegatives -- count number of negatives
#
# RETURNS:
# v0 -- number of negative numbers found
#
# arguments:
# a0 -- pointer to array (ptr)
# a1 -- array count
#
# temporaries:
# a1 -- array limit (endp)
# t3 -- temp (value of table[i])
countNegatives:
li $v0,0 # count = 0
sll $a1,$a1,2 # get byte offset
addu $a1,$a0,$a1 # endp = &arr[count]
j loop_start # start the loop
loop:
lw $t3,0($a0) # temp = *ptr
slt $t3,$t3,$zero # temp = (temp < 0)
add $v0,$v0,$t3 # counter += temp
addiu $a0,$a0,4 # ptr += 4
loop_start:
bne $a0,$a1,loop # ptr != endp? if yes, fly
jr $ra
So, the final asm version, translated back into C would look something like this:
int
countNegatives(int *table, int n)
{
int *endp;
int count = 0;
endp = &table[n];
for (; table != endp; ++table)
count += (*table < 0);
return count;
}
It is not too difficult I think to see what decent compiler does:
https://godbolt.org/g/PiR8Ds
And you will have all the call conventions and another stuff toking by themselves.
#include <stdio.h>
int __attribute__((noinline)) countNegatives(int table[] , int n) {
int count = 0;
int i;
for (i=0; i<n; i++) {
if (table[i] <0) {
count++;
}
}
return count;
}
volatile int x[] = {454,-3,-5343,-3434,4534};
int main(void)
{
printf("%d\n",countNegatives((int *)x, sizeof(x)/sizeof(x[0])));
}
countNegatives:
blez $5,$L6
sll $5,$5,2
addu $5,$4,$5
move $2,$0
$L5:
lw $3,0($4)
addiu $4,$4,4
slt $3,$3,0
bne $4,$5,$L5
addu $2,$2,$3
j $31
nop
$L6:
j $31
move $2,$0
$LC0:
.ascii "%d\012\000"
main:
lui $4,%hi(x)
addiu $sp,$sp,-32
li $5,5 # 0x5
sw $31,28($sp)
jal countNegatives
addiu $4,$4,%lo(x)
lui $4,%hi($LC0)
move $5,$2
jal printf
addiu $4,$4,%lo($LC0)
lw $31,28($sp)
move $2,$0
j $31
addiu $sp,$sp,32
x:
.word 454
.word -3
.word -5343
.word -3434
.word 4534

Converting a C function to MIPS assembly

I'm currently learning MIPS Assembly and I am attempting to convert the following C function into MIPS Assembly:
int count (int a[], int n, int x)
{
int res = 0;
int i = 0;
int j = 0;
int loc[];
for(i = 0; i != n; i++)
if(a[i] == x)
{
res = res + 1;
loc [j] = i;
j = j + 1;
}
return res, loc;
}
I've succeeded in converting most of it, and I believe I have successfully returned res (a value of 1), though I'm uncertain about returning loc (I also get a value of 1, and I don't think that's correct). However, I am having difficulty with this program and I am unsure as to how I can ensure that loc is returning the correct value or how to even code it to do so.
Here is my Assembly code:
.data
a: .word 5,6,7,8,9,10
n: .word
x: .word
res: .word 0
i: .word 0
jj: .word 0
loc: .space 40
.text
main:
la $s0, a
lw $s1, res
lw $s2, x
lw $t0, i
lw $t1, jj
lw $t2, n
la $s3, loc
li $t4, 6
start:
sll $t3, $t0, 2
add $t5, $t3, $s0
lw $t4, 0($t5)
beq $t0, $t4, start
addi $t0, $t0, 1
beq $t0, $t2, exit
addi $s1, $s1, 1
sll $t7, $t1, 2
add $t6, $s3, $t7
sw $t0, 0($t6)
addi $t1, $t1, 1
exit:
li $v0, 1
add $a0, $s1, $zero
syscall
li $v0, 1
add $a1, $s3, $zero
syscall
Any help, pointers, or suggestions would be very much appreciated.
EDIT: I've revised my code and now receive 0 for the res return and "268501028" for loc. Not sure where this number is coming from.
.data
a: .word 5,6,7,8,9,10
n: .word #n
x: .word #x
res: .word 0
i: .word 0
jj: .word 0
loc: .space 40
.text
main:
la $s0, a
lw $s1, res
lw $s2, x
lw $t0, i
lw $t1, jj
lw $t2, n
la $s3, loc
li $t4, 6
start:
beq $t0, $t2, exit #for(i = 0; i != n; i++)
bne $s0, $s2, else #if(a[i] == x)
j start
else:
addi $s1, $s1, 1 #res = res + 1;
sw $t0, ($t1) #loc [j] = i;
addi $t1, $t1, 1 #j = j+1
addi $t0, $t0, 1 #Increment i
addi $s3, $s3, 4 #Setting next element for loc
addi $s0, $s0, 4 #Setting next element for a
j start
exit:
li $v0, 1
move $a0, $s1
syscall
li $v0, 1
move $a0, $s3
syscall
Okay, there were a few bugs. I've annotated the source and added "BUG:" to hightlight them. I then created a cleaned up and corrected version
Here's your original code--no bug fixes, just annotations [please pardon the gratuitous style cleanup]:
# int
# count(int a[], int n, int x)
# {
# int res = 0;
# int i = 0;
# int j = 0;
# int loc[n];
#
# for (i = 0; i != n; i++) {
# if (a[i] == x) {
# res = res + 1;
# loc[j] = i;
# j = j + 1;
# }
# }
#
# return res, loc;
# }
.data
a: .word 5,6,7,8,9,10
n: .word
x: .word
res: .word 0
i: .word 0
jj: .word 0
loc: .space 40
nl: .asciiz "\n"
.text
.globl main
main:
la $s0,a
lw $s1,res
lw $s2,x
lw $t0,i
lw $t1,jj
lw $t2,n
la $s3,loc
li $t4,6 # BUG: extraneous (gets trashed below)
start:
sll $t3,$t0,2 # get i << 2
add $t5,$t3,$s0 # get &a[i]
lw $t4,0($t5) # fetch it
# BUG: we're comparing a[i] against i but we want to compare against x
# _and_ we want to flip the sense of the branch
beq $t0,$t4,start # is it a match? if yes, loop
addi $t0,$t0,1 # increment i
beq $t0,$t2,exit # i == n? if no, loop. if yes, exit
# BUG: the indexing here is wrong
addi $s1,$s1,1 # j += 1
sll $t7,$t1,2 # get jj << j
add $t6,$s3,$t7 # &loc[jj << j] (BUG: we want &loc[j])
sw $t0,0($t6) # set it to i
addi $t1,$t1,1 # jj += 1
# BUG: we should loop here and _not_ fall through
exit:
# print j (with newline)
li $v0,1
add $a0,$s1,$zero
syscall
li $v0,4
la $a0,nl
syscall
# print _address_ of loc[0]
# BUG: if we care to print anything, we should print the _values_ of the
# whole array
li $v0,1
# BUG: this should be a0 and _not_ a1
###add $a1,$s3,$zero
add $a0,$s3,$zero
syscall
li $v0,4
la $a0,nl
syscall
li $v0,10 # exit program
syscall
Here's the cleaned up and corrected version. I had to do a bit of restructuring and simplification to make it work, so it may seem a bit "alien" at first. However, I tried to retain your register usage where possible.
I also increased the size of the a array and added a user prompt for the x value:
# int
# count(int a[], int n, int x)
# {
# int i = 0;
# int j = 0;
# int loc[n];
#
# for (i = 0; i != n; i++) {
# if (a[i] == x) {
# loc[j] = i;
# j += 1;
# }
# }
#
# return j, loc;
# }
.data
a: .word 5,6,7,8,9,10
.word 5,6,7,8,9,10
.word 5,6,7,8,9,10
.word 5,6,7,8,9,10
.word 5,6,7,8,9,10
ae:
loc: .space 1000
prompt: .asciiz "Enter x value: "
msgnl: .asciiz "\n"
msgj: .asciiz "j: "
msgloc: .asciiz "loc: "
.text
# main -- main program
#
# RETURNS [sort of as this is a main program]:
# s1 -- j value (count of elements in "loc")
# loc -- filled in indexes into "a" array of matches to x
#
# registers:
# s0 -- a (base address of "a" array)
# t2 -- n (number of elements in "a" array)
#
# s2 -- x (value to match)
# t0 -- i (current index into "a" array)
# s3 -- loc (base address of "loc" array)
# s1 -- j (current index into "loc" array)
#
# t6 -- quick temporary [reusable]
# t7 -- used in array offset/index calculations [reusable]
.globl main
main:
# prompt for x value
li $v0,4 # syscall: print string
la $a0,prompt
syscall
# read in x value
li $v0,5 # syscall: read integer
syscall
move $s2,$v0
# get address of "a" array and compute length
la $s0,a # get &a[0]
la $t2,ae # get address of &a[n]
sub $t2,$t2,$s0 # get number of bytes in a
srl $t2,$t2,2 # get number of words in a (i.e. n)
li $t0,0 # i = 0
li $s1,0 # j = 0
la $s3,loc # base address of loc array
# main matching loop
loop:
sll $t7,$t0,2 # get i << 2
add $t7,$t7,$s0 # get &a[i]
lw $t6,0($t7) # fetch from it
bne $t6,$s2,next # a[i] == x? if no, advance to next element
# add new "i" value to loc array
sll $t7,$s1,2 # get j << 2
add $t7,$s3,$t7 # &loc[j << 2]
sw $t0,0($t7) # store i into loc
addi $s1,$s1,1 # j += 1
next:
addi $t0,$t0,1 # i += 1
blt $t0,$t2,loop # i < n? if yes, loop (or, we're done)
# done with calculation/fill loop
done:
la $s6,msgj # get prefix string
move $s7,$s1 # get j
jal prtnum # pretty print the number
blez $s1,exit # bug out if _no_ values in loc
# prepare to print all values of loc
la $t6,loc # base address of "loc"
li $t7,0 # initial index
# loop and print all values of loc
prtlocloop:
la $s6,msgloc # prefix string
lw $s7,($t6) # get loc[...]
jal prtnum # pretty print the number
add $t6,$t6,4 # increment address
add $t7,$t7,1 # increment index
blt $t7,$s1,prtlocloop # done? if no, loop
exit:
li $v0,10 # exit program
syscall
# prtnum -- print a number with a prefix string on a single line
#
# arguments:
# s6 -- prefix string
# s7 -- value to print
#
# registers:
# v0 -- syscall number [trashed]
# a0 -- syscall argument [trashed]
prtnum:
li $v0,4 # syscall: print string
move $a0,$s6 # string to print
syscall
li $v0,1 # syscall: print integer
move $a0,$s7 # value to print
syscall
li $v0,4 # syscall: print string
la $a0,msgnl
syscall
jr $ra # return
UPDATE:
What exactly is the difference between print and prtnum?
print is the label for the top of the loop that prints the values in loc. prtnum is subroutine/function that does the printing of a single number.
I added prtnum to demonstrate the use of a function and to avoid needless replication of some code.
Can they not be properly merged?
Sure, with some caveats. I did a slight/cosmetic edit to try to make things clearer. In particular, I renamed print: to prtlocloop: to try and make its role clearer.
The syscall(1) for "print integer" just prints the integer but does not add any whitespace or newline to separate them (i.e. it's exactly like printf("%d",a0)). So, we need something.
Originally, I just had the syscall(print_integer). With that, we get one "very long" number. Then, I added syscall(4) to print a newline. This was fine except the output was a bit confusing as to which value was j and which were the loc values.
(1) So, I added the "prefix" string. So, that became three syscalls for each number.
(2) This was used in two places: To print j and to print the loc values.
Same code in two or more places. That's the standard criterion for "split out code to function" in any language. It's a design/style choice [so there is no absolute answer].
So, with (1) and (2), I moved it to the prtnum function. Actually, I wrote the prtnum function first because I already knew the structure, and added the prefix argument after the output "looked ugly" [to me].
When I first coded it, I used "j: " for j and used a " " prefix for loc. It still looked a little funky. So, I changed the prefix to "loc: " to be consistent.
Could it be inlined? Sure. But, in addition to printing the number itself, we still have to add a separater. So, we need two syscalls per number to do it.
The separater could be a space if we want to put all numbers on the same output line. Fine for short vectors. This would require a slight change to the code as it exists now and we'd have to add a final output of newline to close the line. For longer arrays [that might not fit on a single line], one per line is [probably] tidier.
We only had to print j and loc. If the problem stated that we had to print a, then j, and then loc, I would have gone the other way.
I would have changed prtlocloop into another function (e.g. prtarray), that would loop on the given array and call prtnum for each element.
The first step was getting the calculation loop correct. The second was the printing. But, sometimes, they have to be done together. (i.e.) How can you debug something that you can't see?
So, with calculation correct, you are free to recode the output printing in any way you choose. The prtnum was just my way. But, it is by no means the only way.
Beyond the basic mechanics of working with the asm instructions, the choices are just like in any other language [notably C]. Comment well, choose the simplest and most effective way to architect/split the code, use descriptive variable names, etc. Comments should show "intent", the "what/why". The asm instructions are the "how".
Side note: Some OPs have had serious difficulty understanding how sll [which you already understand] works. They just didn't "get" the fact that a left shift by 2 was like a multiply by 4 and converts an index value into byte/address offset. So, you may already be ahead of the game ...
Yesterday, I gave an answer for a mips question where I went the other way and recommended inlining two functions. The problem was to calculate sin(x) using a Taylor series expansion [summation of terms] of the form: x**(2n)/factorial(2n-1).
With inlining, it was possible to reuse partial results from the previous term in the series without having to recalculate each term from scratch. This would not have been [conveniently] possible with multiple functions.
I didn't write the mips code, but I wrote the C/pseudo-code: mips program to calculate sin(x) The resulting mips code would [probably] have been simpler and would definitely run faster.

MIPS recursion call in loop, preserving loop variable

I'm converting the following recursive java program to MIPS asm. The algorithm computes all the possible ordering/permutations of the numbers. But the recursive call is in the for loop. I need to preserve the variable 'i' in my MIPS version but I don't know exactly where to add that. My algorithm is correct, it's just that my $t0 (which is 'i') never gets reset to 0. I just can't figure out how/where to preserve it on the stack or when to take it off the stack. Any help appreciated.
import java.util.Arrays;
public class Test
{
private static void swap(int[] v, int i, int j)
{
int t = v[i];
v[i] = v[j];
v[j] = t;
}
public void permute(int[] v, int n)
{
if (n == 1)
System.out.println(Arrays.toString(v));
else
{
for (int i = 0; i < n; i++)
{
permute(v, n-1);
if (n % 2 == 1)
swap(v, 0, n-1);
else
swap(v, i, n-1);
}
}
}
public static void main(String[] args)
{
int[] ns = {1, 2, 3, 4};
new Test().permute(ns, ns.length);
}
}
and the mips function
Note: I am permutating Strings, not integers but the algorithm is the same.
#----------------------------------------------
# anagram - Prints all the permutations of
# the given word
# a0 - the word to compute the anagrams
# s0 - n, the length of the word
# a1 - n - 1 (length-1)
#----------------------------------------------
anagram:
addi $sp, $sp, -16
sw $a0, 0($sp)
sw $a1, 4($sp)
sw $s0, 8($sp)
sw $ra, 12($sp)
add $s0, $a1, $zero # this is n
addi $a1, $s0, -1 # n-1
beq $s0, 1, printS
init: move $t0, $zero # t0 = i = 0
logic: slt $t1, $t0, $s0 # Set t1 = 1 if t0 < length
beqz $t1, endAnagram # if it's zero, it's the end of the loop
jal anagram
li $t2, 2
div $s0, $t2
mfhi $t3
beqz $t3, even # if even branch to even, otherwise it will go to odd
odd: # swap the n-1 char with the first
add $t4, $a0, $zero
add $t5, $a0, $a1
lb $t6, 0($t4) # first char
lb $t7, 0($t5) # n-1 char
sb $t7, 0($t4) # swap the two
sb $t6, 0($t5)
j inc # skip the even section
even: # swap the ith char with n-1 char
add $t4, $a0, $t0 # ith char
add $t5, $a0, $a1 # n-1 char
lb $t6, 0($t4) # ith char
lb $t7, 0($t5) # n-1 char
sb $t7, 0($t4) # swap the two
sb $t6, 0($t5)
inc: addi $t0, $t0, 1 # t0++;
j logic
endAnagram:
# reset stack pointers
lw $a0, 0($sp)
lw $a1, 4($sp)
lw $s0, 8($sp)
lw $ra, 12($sp)
addi $sp, $sp, 16 # adjust stack
jr $ra
printS: # print string and jump to return
jal printString # calls printString function which prints the string
j endAnagram
$t0 is not preserved accross subroutine calls according to convention, and you seem to follow that convention. As such, you have two choices:
you either store i in a register that is preserved, in which case
you need to preserve the register yourself in the prologue/epilogue. You already do this for $s0.
or you save $t0 yourself on the stack, around the subroutine call
In both cases, you will need additional space for your locals, so change addi $sp, $sp, -16 to addi $sp, $sp, -20 (along with the matching code in the epilogue too). If you choose option #1, use for example $s1 to store i. Add code to save and restore $s1 just like you do for $s0. If you choose option #2, add code around the jal anagram that writes $t0 to stack before the jal, and reloads it after.

Resources