Bad Addressing in MIPS - Matrix Multiplication - c

A followup from my post yesterday,
I'm working on a matrix multiplication assignment in MIPS Assembly. The following is the code from my inner most 'k' loop where I compute A[i][k]*B[k][j].
# compute A[i][k] address and load into $t3
# A[i][k] = A+4*((4*i) + k)
sll $t3, $t5, 2 # Store 4*i in $t3
addu $t3, $t3, $t7 # Adds $t3 to k
sll $t8, $t3, 2 # Computes 4*($t3) = 4*(4*i+k) by temporarily storing the product in $t8
move $t3, $t8 # Stores 4*($t3) into $t3
addu $t3, $t3, $a0 # Adds A to $t3
lw $t3, 0($t3)
# compute B[k][j] address and load into $t4
# B[k][j] = B+4*((4*k) + j)
sll $t4, $t7, 2 # Stores 4*k in $t4
addu $t4, $t4, $t6 # Adds $t4 to j
sll $t8, $t4, 2 # Computes 4*($t4) = 4*(4*k+j) by temporarily storing the product in $t8
move $t4, $t8 # Stores 4*($t4) into $t4
addu $t4, $t4, $a1 # Adds B to $t4
lw $t4, 0($t4)
# multiply
multu $t3, $t4
mflo $t9
# insert the multiplied value into $a2
sll $t1, $t5, 2 # Multiplies $t5 by 4 and stores the value in $t1
addu $t1 $t1, $t6 # Adds $t1 and $t6 (j) for the final bit offset
move $t2, $a2 # Stores $a2's base register in $t2 temporarily
addu $a2, $a2, $t1 # Adds bit offset to $a2
sw $t9, 0($a2) # Store $t9 into its respective location in with offset from $a2
move $a2, $t2 # Restores base address back into $a2
# increment k and jump back or exit
addi $t7, $t7, 1
j kLoop
From what I can see, the multiplying works as it should. For reference, my $t5 - $t7 are for my i, j, and k.
My next step is to insert the result, stored in $t9, into my result array, situated at register $a2. In order to store $t9 at its proper location in $a2, I need to compute the offset. I know the offset is $a2 * the row + the column. However, when I run my code, I get Bad Addressing errors. I know something has to be wrong with this offset computation, because when I remove it, the program continues normally but there's an issue with my output. This issue stems from the lack of offset calculation. If anyone can help me out here, it would be much appreciated. Stackoverflow has helped me out so much with understanding MIPS, so I'm grateful for everyone's assistance! Thanks

So it turns out I wasn't calculating the bit offset properly - I wasn't multiplying the 4*i+j by 4 to get a multiple of 4 that can be addressed. After making that change, my multiplying part of my k loop now looks like this:
# multiply
multu $t3, $t4
mflo $t9
# insert the multiplied value into $a2
sll $t1, $t5, 2 # Multiplies $t5 (i) by 4 and stores the value in $t1
addu $t1 $t1, $t6 # Adds $t1 and $t6 (j) for the col offset
sll $t1, $t1, 2 # Multiplies $t1 by 4 and stores value back into $t1 for final bit offset
move $t2, $a2 # Stores $a2's base register in $t2 temporarily
addu $t2, $t2, $t1 # Adds bit offset to $t2 / $a2's temp alias
lw $t1, 0($t2) # Overwrites $t1 with the value currently at $t2
addu $t9, $t9, $t1 # Adds the current $t2 val ($t1) to $t9
sw $t9, 0($t2) # Store $t9 into its respective location in with offset from $a2
I figured I might as well share this with people so if anyone else has this issue, it can be resolved with relative ease. Thank you to everyone who assisted me with this problem!

Related

MIPS Assembly why are these registers loading the same array values?

I'm trying to load in array values in the first and second position but for some reason it's loading in only the value in the first position? Been trying to figure it out for an hour but no luck. This is the code I have. I set $t1 to 0 and $t3 to 4 so I should be loading in the first and second value, yet it only loads in the first value for both $t1 and $t3?
addi $t1, $zero, 0
addi $t3, $zero, 4
FindLessThenLoop:
la $t1, myArray # Load first number
addu $t1, $zero, $t1 # add offset and base together
lw $t1, ($t1) # fetch the data
la $t3, myArray # Load second number
addu $t3, $zero, $t3 # add offset and base together
lw $t3, ($t3) # fetch the data
The code for the first and the second are essentially the same except using a different register, so, we would expect the same results: the value from array position/index 0.
To get the second element, you can do either:
la $t3, myArray # Load second number
addiu $t3, $t3, 4 # add offset and base together
lw $t3, ($t3) # fetch the data
-or-
la $t3, myArray # Load second number
lw $t3, 4($t3) # fetch the data with offset 4
You'll prefer a variation of the first form if you're going to do variable indexing (e.g. a[i]), say in a loop, whereas the second form is for constant indexing (e.g. a[1]).
If you have an integer index, then the following is useful:
# usually done outside of and before a loop:
la $t3, myArray # Load second number
# this part done inside a loop:
sll $t4, $t2, 2 # scale the integer index by 4
addu $t4, $t3, $t4 # add base and offset
lw $t1, ($t4) # fetch the data
Note the following:
there is pre-loop setup of $t3
inside the loop we preserve $t3 so it can be used each iteration
we scale the simple integer index by size of the elements (4x)
An alternative is pointers, which are fairly easy in assembly, but suggest to convert your algorithm to pointers in C first, then take to assembly.
A pointer combines into one variable the notion of base + scaled offset.  A pointer can be incremented to refer to the next element.
Here's a simple illustrative loop that does max value of array, assuming elements are positive:
li $t0, array # t0 is the base array address: &array[0]
li $t1, 0 # t1 is min value variable, initially zero
li $t2, 0 # t2 is the index, aka loop control variable call it "i"
li $t3, 10 # where to stop with "i"
loop1:
beq $t2, $t3, loop1End # if "i" == 10 we're done
sll $t4, $t2, 2 # scale index by 4 due to word-sized elements
add $t4, $t0, $t4 # add base and scaled offset
lw $t4, 0($t4) # fetch element from a[i]
ble $t4, $t1, if1End
move $t1, $t4 # capture the new max, if larger
if1End:
addi $t2, $t2, 1 # next index position
j loop1
loop1End:
... # t1 holds max value from the array

MIPS - Having trouble finding the minimum value in an array

so I've been given an assignment in which I'm supposed to find the index of the max and min values in a user-created 10 digit array in MIPS assembly language. I've been able to find the max value but I'm having trouble finding the min value. I figured it should be a simple solution once I find the max value but I was wrong.
Here is what i have to find the max value:
deleteMax:
# $s7 is the register where the address of the array is stored
# $s0 is the register where the array size is stored
# Clear out unimportant registers
addi $s1, $zero, 0
addi $t0, $zero, 0
addi $s3, $zero, 0
addi $s4, $zero, 0
addi $t6, $zero, 0
addi $t7, $zero, 0
addi $t0, $zero, 0 # Address of first element in bytes
li $t3, -1 # Max index
li $s2, 0 # Max value
li $t1, 0 # Counter
loop:
sll $s1, $t1, 2
add $s1, $s1, $s7
lw $s3, 0($s1) # Load the first element into $s3
li $v0, 1
move $a0, $s3
syscall
slt $t2, $s3, $s2 # If $s3 < $s2, $t2 = 1, if $s3 >= $s2, $t2 = 0
bne $t2, $zero, find_max
ori $s2, $s3, 0 # Updates the maximum value
ori $t3, $t1, 0 # Updates the maximum value index
find_max:
addi $t1, $t1,1 # Increase the counter
bne $t1, $s0, loop # if the counter hasn't reached the end, go
back to the loop
j print_max
This does what I want: Find the max and it's index. However when I try switching these 2 lines of codes:
slt $t2, $s3, $s2 # If $s3 < $s2, $t2 = 1, if $s3 >= $s2, $t2 = 0
bne $t2, $zero, find_max
to lets say:
slt $t2, $s3, $s2 # If $s3 < $s2, $t2 = 1, if $s3 >= $s2, $t2 = 0
bne $t2, 1, find_max
The min value comes out to 0?
I'm not sure why this is happening and unsure what I can do for my current situation to find the min value.
I hope I've explained myself and my code + comments have explained the situation enough. Im more than happy to answer any more confusion you guys may have.

Arrays in nested for loops, MIPS assembly

I was given the following C code to implement in MIPS assembly.
for(i=0; i<16, i++){
for(i=0; j<16, j++){
C[i][j] = A[i][j] = B[j][i]
}
}
The arrays are already initialized for us, we only have to deal with the memory.
Here's how i made my nested loop.
First:
bge $t1, $t0, Exit
Second:
bge $t2, $t0, Continue
#do work here.
addi $t2, $t2, 1
j Second
Continue:
addi $t1, $t1, 1
j First
Exit:
Loading the counters:
addi $t0, $t0, 16
move $t1, $zero
move $t2, $zero
la $t3, A
la $t4, B
la $t5, C
And logic for A[i][j] using the formula Base + Word Length * (Row * Max size + Col):
sllv $t6, $t0, $t1 #Row, shift 16 by current counter -> 32 -> 64..
addu $t6, $t6, $t2 #Column, add column counter.
sll $t6, $t6, 2 #Shift entire count by word length.
addu $t6, $t6, $t3 #Add A base address.
lw $t7, ($t6) #Load word from that address.
Full code:
addi $t0, $t0, 16
move $t1, $zero
move $t2, $zero
la $t3, A
la $t4, B
la $t5, C
First:
bge $t1, $t0, Exit
Second:
bge $t2, $t0, Continue
###
#Zero out counters first.
move $t6, $zero
move $t7, $zero
move $t8, $zero
move $t9, $zero
sllv $t6, $t0, $t1 #Row, shift 16 by current counter -> 32 -> 64..
addu $t6, $t6, $t2 #Column, add column counter.
sll $t6, $t6, 2 #Shift entire count by word length.
addu $t6, $t6, $t3 #Add A base address.
lw $t7, ($t6) #Load word from that address.
sllv $t7, $t0, $t2 #Row, shift 16 by current counter -> 32 -> 64..
addu $t7, $t7, $t1 #Column, add column counter.
sll $t7, $t7, 2 #Shift entire count by word length.
addu $t7, $t7, $t4 #Add B base address.
lw $t8, ($t7) #Load word from that address.
addu $t9, $t7, $t8 #add A and B results.
addu $t7, $t6, $t5 #add C base address, reuses $t7, copies $t6 from *A* array.
sw $t9, 0($t7) #store above result to C.
###
addi $t2, $t2, 1
j Second
Continue:
addi $t1, $t1, 1
j First
Exit:
I'm getting a bad address error but I can't seem to figure out what is wrong.
There are at least three errors:
You are overwriting $t6 which should have the offset of A and C with the base address of A
You are overwriting $t7 which should hold the content of A[i][j] with the address of B[j][i]
You are miscalculating the row offset. Instead of shifting 16 row times, you should shift row 4 times (which effectively multiplies row by 16)
You may change
sllv $t6, $t0, $t1 #Row, shift 16 by current counter -> 32 -> 64..
...
addu $t6, $t6, $t3 #Add A base address.
lw $t7, ($t6) #Load word from that address.
...
sllv $t7, $t0, $t2 #Row, shift 16 by current counter -> 32 -> 64..
...
addu $t9, $t7, $t8 #add A and B results
with
sll $t6, $t1, 4 # Row
...
addu $t7, $t6, $t3 #Add A base address.
lw $t9, ($t7) #Load word from that address.
...
sll $t7, $t2, 4 # Row
...
addu $t9, $t9, $t8 #add A and B results.

Array sorting in MIPS

So I am working on an assignment to sort an array in MIPS. The data to sort is given in a separate .asm file as follows:
.data
.word 3
.word 40
.word 30
.word 70
.text
I decided to use bubble sort. I am fairly certain that my algorithm is sound, and it appears to sort the data correctly. The fun thing about this project is that the values we sort will be played as MIDI notes as a means of testing the program (so, naturally, the notes should play in ascending order). This is working pretty well when I test my code, however, I'm hearing a few strange beats at the end of the MIDI notes. I stepped through my code and discovered that, at the end, $t1 (the value I use to compare to my loop iterator for completeness) holds a value of 7, when I expected it to hold a value of three. I assign $t1 as 0($t2), $t2 is 0x10010000 which I assume is still the base address of my array. The base address of the array should hold 3, shouldn't it?
I'm a little confused about why $t1 is 7 at all... any advice? I have included all of my code below.
#ec2.asm
.include "ec2_data.asm" #must be in the same directory as ec2.asm
#ec2_data.asm: puts some values in the "data segment"
#the first value is the number of values to sort (n)
#the remaining n word values are the data to sort
#the values will be stored in memory as word values starting
# address 0x10010000
j main
#$t2 is hasChanged
#$t3 is itemCount
#$t4 is i, our iterator in the for loop
#$t5 is array[i] (temp)
#$t6 is array[i+1] (temp)
while_label:
beq $t2, $zero, done_sorting #checks to see if the previous iteration switched any values. If not, we are done.
addi $t2, $zero, 0 #initialize hasChanged to 0
addi $t3, $t3, -1 #decrement itemCount by -1
addi, $t0, $t0, 4 #add 4 to the array offset
addi, $t4, $zero, 0 #set our for loop iterator to 0
for_label:
lw $t5, 0($t0) #set a temp value equal to array[i]
lw $t6, 4($t0) #set a temp value equal to array[i+1]
beq $t4, $t3, while_label #for loop: if i = itemcount, we have incremented all the way through our for loop
bge $t6, $t5, skip_swap #if array[i+1] is greater than or equal to array[i], we don't need to swap these values
#swap
sw $t6, 0($t0) #set array[i] equal to array[i+1]
sw $t5, 4($t0) #set array[i+1] equal to the temp value
addi $t2, $t2, 1 #set hasChanged to 1 to indicate that a swap has been made
skip_swap:
addi, $t4, $t4, 1 #increment i by 1 (for loop iterator)
j for_label #keep the for loop going!
main:
#read values
addi $t0, $zero, 0x10010000 #sets $t0 to be the base address of the array
addi $t2, $zero, 1
lw $t3, 0($t0) #stores n in $t3
j while_label
done_sorting:
# adapted from MIDI example
#duration 25 ms
#instrument (whichever)
#volume 64
addi $v0, $zero, 33 # midi out synchronous
addi $t2, $zero, 0x10010000 # address of original array (which should by now be sorted)
addi $a1, $zero, 250 # duration (ms)
addi $a2, $zero, 1 # instrument
addi $a3, $zero, 64 # volume
addi $t0, $zero, 0 # counter
lw $t1, 0($t2) # end of the loop (should be n)
addi $t1, $t1, 4 # adds four to the array offset in $t1
lw $a0, 0($t2) # stores the first sorted value ( 4($t1) ) in $a0
midi_loop:
beq $t0, $t1, done
addi $t2, $t2, 4
lw $a0, 0($t2)
syscall
addi $t0, $t0, 1
j midi_loop
done:
#addi $v0, $zero, 10 # syscall for exit
#syscall # clean exit
Your sorting algorithm is quite broken. You haven't noticed that because it happens to get the right answer with your test data. It will produce the wrong answer with other test data (e.g. I believe your algorithm will sort 70, 40, 30 to 40, 30, 70).
But your question was about why $t1 ends up with value 7. The answer is simple. You wrote this:
addi $t2, $zero, 0x10010000 # address of original array (which should by now be sorted)
addi $a1, $zero, 250 # duration (ms)
addi $a2, $zero, 1 # instrument
addi $a3, $zero, 64 # volume
addi $t0, $zero, 0 # counter
lw $t1, 0($t2) # end of the loop (should be n)
addi $t1, $t1, 4 # adds four to the array offset in $t1
We can see that you set $t2 to 0 + 0x10010000 = 0x10010000. Then you load the word at 0($t2) (= 0x10010000) into $t1. The word at 0x10010000 is 3, so at that point $t1 is 3. Then you add 4 to $t1, storing the result in $t1. At that point $t1 is 7. You never modify $t1 after that.
SO I really shouldn't have asked this question without giving my code another once-over. The errors were extremely simple. This appears to work.
#ec2.asm
.include "ec2_data.asm" #must be in the same directory as ec2.asm
#ec2_data.asm: puts some values in the "data segment"
#the first value is the number of values to sort (n)
#the remaining n word values are the data to sort
#the values will be stored in memory as word values starting
# address 0x10010000
j main
#$t2 is hasChanged
#$t3 is itemCount
#$t4 is i, our iterator in the for loop
#$t5 is array[i] (temp)
#$t6 is array[i+1] (temp)
while_label:
beq $t2, $zero, done_sorting #checks to see if the previous iteration switched any values. If not, we are done.
addi $t2, $zero, 0 #initialize hasChanged to 0
addi $t3, $t3, -1 #decrement itemCount by -1 WHY
addi, $t0, $zero, 0x10010000 #add 4 to the array offset
addi, $t4, $zero, 0 #set our for loop iterator to 0
for_label:
addi, $t0, $t0, 4
lw $t5, 0($t0) #set a temp value equal to array[i]
lw $t6, 4($t0) #set a temp value equal to array[i+1]
beq $t4, $t3, while_label #for loop: if i = itemcount, we have incremented all the way through our for loop
bge $t6, $t5, skip_swap #if array[i+1] is greater than or equal to array[i], we don't need to swap these values
#swap
sw $t6, 0($t0) #set array[i] equal to array[i+1]
sw $t5, 4($t0) #set array[i+1] equal to the temp value
addi $t2, $t2, 1 #set hasChanged to 1 to indicate that a swap has been made
skip_swap:
addi, $t4, $t4, 1 #increment i by 1 (for loop iterator)
j for_label #keep the for loop going!
main:
#read values
addi $t0, $zero, 0x10010000 #sets $t0 to be the base address of the array
addi $t2, $zero, 1
lw $t3, 0($t0) #stores n in $t3
j while_label
done_sorting:
# adapted from MIDI example
#duration 25 ms
#instrument (whichever)
#volume 64
addi $v0, $zero, 33 # midi out synchronous
addi $t2, $zero, 0x10010000 # address of original array (which should by now be sorted)
addi $a1, $zero, 250 # duration (ms)
addi $a2, $zero, 1 # instrument
addi $a3, $zero, 64 # volume
addi $t0, $zero, 0 # counter
lw $t1, 0($t2) # end of the loop (should be n)
lw $a0, 0($t2) # stores the first sorted value ( 4($t1) ) in $a0
midi_loop:
beq $t0, $t1, done
addi $t2, $t2, 4
lw $a0, 0($t2)
syscall
addi $t0, $t0, 1
j midi_loop
done:

C to MIPS translation

Trying to convert this c code into MIPS and run it in SPIM.
int A[100], B[100];
for(i=1; i<100; 1++){
A[i] = A[i-1] + B[i];
}
So far this is what I have.
# comments are delimted by has marks
.data
A: .word 0:100 # array of 12 integers
B: .word 0:100 # array of 12 integers
.text
main:
li $v0, 1 # load the value "1" into register $v0
li $t0, 1 # load the value "1" into register $t0
li $t1, 100 # load the value "100" into register $t1
blt $t0, $t1, loop # branches to Loop if $t0 < 100
la $t9, B
la $t8, A
loop:
sll $t0, $t0, 2
add $t2, $t9, $t0
lw $s4, 0($t9)
add $t3, $t0, -1
add $t4, $t8, $t3
lw $s5, 0($t4)
add $t5, $t2, $s5
add $t6, $s0, $t0
sw $t7, 0($t5)
addi $t0, $t0, 1
li $v0, 1 # system call for print_int
move $a0, $t0 # the sum to print
syscall # print the sum
When running in SPIM I get the following errors:
Exception occurred at PC=0x00400040
Bad address in data/stack read: 0x00000004
Exception occurred at PC=0x0040004c
Unaligned address in inst/data fetch: 0x00000003
Exception occurred at PC=0x00400058
Bad address in data/stack read: 0x00000000
Attempt to execute non-instruction at 0x0040006c
Some direction would be nice. Thanks
You are branching to loop (blt $t0, $t1, loop) before you initialize the pointers to A and B. You need to move the blt $t0, $t1, loop to the end of your code, not have it at the beginning.
I hate to do this, but there are too many things wrong to list them all. Try this:
.data
A: .word 0:100 # array of 100 integers
B: .word 0:100 # array of 100 integers
.text
main:
li $t0, 4 # load the value "1" into register $t0
li $t1, 400 # load the value "100" into register $t1
la $t9, B
la $t8, A
loop:
add $t2, $t9, $t0 # $t2 = B + i
lw $s4, 0($t9) # $s4 = B[i]
add $t3, $t0, -4 # $t3 = i - 1
add $t4, $t8, $t3 # $t4 = A + i - 1
lw $s5, 0($t4) # $s5 = A[i - 1]
add $t5, $t8, $t0 # $t5 = A + i
add $t6, $s4, $s5 # $t6 = B[i] + A[i - 1]
sw $t6, 0($t5) # A[i] = $t6
addi $t0, $t0, 4 # i++
li $v0, 1 # system call for print_int
move $a0, $t6 # the sum to print
syscall # print the sum
blt $t0, $t1, loop # branches to Loop if $t0 < 100
Right off the bat, s1 and s2 should be initialized to the stack-based (I assume) address of your arrays.

Resources