Related
I won't lie, I'm a total beginner at programming in general with my first time being last year when I started programming in Computer Programming I. A lot of this is still over my head so uh... go easy on me will ya?
The question poised is as follows:
"1. (5 pts) Translate the following C code to MIPS. Assume that the variables i and j are assigned to registers $s0 and $s1, respectively. Assume that the base address of the arrays A and B are in registers $s6 and $s7, respectively. Assume that the elements of the arrays A and B are 4-byte words:
for (i = 0; i< j; i++)
B[i] = A[i+1] - A[i];
For my program I wrote:
.data
arrayA: .word 5,8, 12, 13, 28
sizeA: .word 5
arrayB: .space 4
i: .word 0
_j: .word 5
.text
# for (i = 0; i< j; i++)
# B[i] = A[i+1] - A[i];
main:
la $s6, arrayA #loads the base address of arrayA into register s6
la $s7, arrayB #loads the base address of arrayB into register s7
lw $s0, i #Loads zero into register s0 for i
lw $s1, _j #loads the value of 4 into register s2 for j
Loop:
beq $s0, $s1, Exit #Does the operation i < j in the for-loop
addi $s0, $s0, 1 #Does the operation i++ in the for-loop
mul $t0, $s0, 4 #Get address of i
add $t1, $t0, $s6 #Finds and stores the base address of a[i]
add $t2, $t0, $s7 #Finds and stores the base address of b[i]
lw $t3, ($s6) #get the value of a[i]
lw $t4, 4($10) #get the value of a[i + 1]
sub $t5, $t4, $t3 #subtracts a[i + 1] and a[i]
sw $t5, ($t2) #Stores the above into b[i]
add $a0, $zero, $t5
li $v0, 1
syscall
J Loop #loops back to beginning
Exit:
li $v0 10
syscall #syscall to exit the program
When I run it, it spits out 33333, which means it isn't incrementing. How do I fix this?
arrayA: .word 5,8, 12, 13, 28
may be wrong. You didn't add a space between 5, and 8 while giving spaces between the other elements. My QtSpim Version 9.1.21 didn't accept that.
arrayB: .space 4
is wrong. You are allocating only 4 bytes while space for 4 words (16 bytes) is required.
_j: .word 5
is wrong. The stored value is 5 despite of the comment saying "loads the value of 4 into register s2 for j". Using value 5 is wrong also because the array a has only 5 elements.
addi $s0, $s0, 1 #Does the operation i++ in the for-loop
is in the wrong place. The increment should be after the loop body.
lw $t3, ($s6) #get the value of a[i]
lw $t4, 4($10) #get the value of a[i + 1]
is wrong. The address of a[i] is stored in $t1, so you should use that.
J Loop #loops back to beginning
may be wrong. You used an uppercase letter for instruction name only here. At least my QtSpim Version 9.1.21 didn't accept that.
li $v0 10
may be wrong. You didn't use a comma for separating the operands while you used that in previous use of li: li $v0, 1. I'm surprised by seeing my QtSpim Version 9.1.21 accepting this.
Fixed code:
.data
arrayA: .word 5, 8, 12, 13, 28
sizeA: .word 5
arrayB: .space 4 * 4
i: .word 0
_j: .word 4
.text
# for (i = 0; i< j; i++)
# B[i] = A[i+1] - A[i];
main:
la $s6, arrayA #loads the base address of arrayA into register s6
la $s7, arrayB #loads the base address of arrayB into register s7
lw $s0, i #Loads zero into register s0 for i
lw $s1, _j #loads the value of 4 into register s2 for j
Loop:
beq $s0, $s1, Exit #Does the operation i < j in the for-loop
mul $t0, $s0, 4 #Get address of i
add $t1, $t0, $s6 #Finds and stores the base address of a[i]
add $t2, $t0, $s7 #Finds and stores the base address of b[i]
lw $t3, ($t1) #get the value of a[i]
lw $t4, 4($t1) #get the value of a[i + 1]
sub $t5, $t4, $t3 #subtracts a[i + 1] and a[i]
sw $t5, ($t2) #Stores the above into b[i]
add $a0, $zero, $t5
li $v0, 1
syscall
addi $s0, $s0, 1 #Does the operation i++ in the for-loop
j Loop #loops back to beginning
Exit:
li $v0, 10
syscall #syscall to exit the program
Don't mix friendly register names with the raw register names — very confusing.
lw $t3, ($s6) #get the value of a[i]
lw $t4, 4($10) #get the value of a[i + 1]
Here you're mixing $t3, $t4, $s6, and $10, which is actually $t2.
Your assembly code is doing the following C code (modulo the above bug):
int *s6 = A;
int *s7 = B;
for ( int i = 0; i != j; ) {
i++;
int *t1 = s6 + i; // i is automatically scaled by C
int *t2 = s7 + i; // ditto
int t3 = *s6; // A[0]
int t4 = s6[1]; // A[1]
int t5 = t4 - t3;
*t2 = t5; // B[i]=...
}
Can you see why it keeps loading A[0] & A[1]?
You should have noticed these problems during single stepping debugging. After each step, check that every effect you're expecting happens as you expect. When it doesn't, look for typos or logic problems.
I'm trying to implement matrix multiplication using MIPS assembly. There is an error on the line "lw $t4, 0($t4)" on the second loop through the k_loop. The error is: "Runtime exception at 0x00400090: fetch address not aligned on word boundary 0x1000fffd". Could someone explain what the error means and what I could do to fix it? Thank you.
.data
matrixA: .word 1,2,3,4,5,6 #Content of matrixA in array form
matrixB: .word 5,6,7,8,9,10 #Content of matrixB in array form
sizeA: .word 3,2 #Defines matrixA as being a 3x2 matrix
sizeB: .word 2,3 #Defines matrixB as being a 2x3 matrix
result: .word 0:9 #Initialize result as being an array of length 9 populated with 0
tab: .asciiz "\t"
newLine: .asciiz "\n"
.globl _main
.text
_main: la $s0, matrixA #s0 set to base address of matrixA
la $s1, matrixB #s1 set to base address of matrixB
la $s2, sizeA #s2 set to base address of sizeA
nop
lw $s3, 4($s2) #s3 set to second val in sizeA (col #)
nop
lw $s2, 0($s2) #s2 set to first val in sizeA (row #)
la $s4, sizeB #s4 set to base address of sizeB
nop
lw $s5, 4($s4) #s5 set to second val in sizeB (col #)
nop
lw $s4, 0($s4) #s4 set to first val in sizeB (row #)
la $s6, result #s6 set to base adress of result
add $s7, $s5, $zero #s7 set to col # in result matrix
add $t0, $zero, $zero #Set t0 to zero. i = 0
add $t1, $zero, $zero #Set t1 to zero. j = 0
add $t2, $zero, $zero #Set t2 to zero. k = 0
li $t3, 0 #Result position set to zero
i_loop: beq $t0, $s2, i_end #End i_loop if i = rowsA
nop
j_loop: beq $t1, $s5, j_end #End j_loop if j = colsB
nop
k_loop: beq $t2, $s4, k_end #End k_loop if k = rowsB
nop
#loop body
li $t4, 0
li $t5, 0
li $t6, 0
#i * M + k - 1
mul $t4, $t0, $s3 #i * #col in matrixA
add $t4, $t4, $t2 #t4 + k
addi $t4, $t4, -4 #t4 -1
add $t4, $t4, $s0 #Now points to value at matrixA[i][k]
lw $t4, 0($t4) #Loads value at matrixA[i][k]
#k * M + j - 1
mul $t5, $t2, $s5 #k * #col in matrixB
add $t5, $t5, $t1 #t5 + j
addi $t5, $t5, -4 #t5 -1
add $t5, $t5, $s1 #t5 now points to value at matrixB[k][j]
lw $t5, 0($t5) #t5 loads value at matrixB[k][j]
#i * M + j - 1
mul $t6, $t0, $s7 #i * #col in result
add $t6, $t6, $t1 #t6 + j
addi $t6, $t6, -4 #t6 -1
add $t6, $t6, $s6 #t6 now points to value at result[i][j]
lw $t8, 0($t6) #t6 loads value at result[i][j]
mul $t7, $t4, $t5 #t7 = matrixA[i][k]*matrixB[k][j]
add $t9, $t8, $t7 #t8 = result[i][j] + matrixA[i][k]*matrixB[k][j]
sw $t9, 0($t6)
#end loop body
addi $t2, $t2, 1 #k++
j k_loop #Return to start of k_loop
k_end:
addi $t1, $t1, 1 #j++
li $t2, 0 #Resets k counter to 0
j j_loop #Return to start of j_loop
j_end:
addi $t0, $t0, 1 #i++
li $t1, 0 #Resets j counter to 0
j i_loop #Return to start of i_loop
i_end: #print
There are three distinct problems, masked by the alignment fault.
You are computing array indexes [for an int array]. Before these can be added to the base address of the matrix, they must be converted into byte offsets.
Otherwise, you'll get [as you did get] an alignment fault because mips requires that addresses for words (i.e. lw/sw) are four byte aligned.
The second problem is when you try to subtract one from index. You're using a value of -4 even though the comment says -1. So, on some level, you're mixing and matching index calculations and offset calculations
Consider a simple 1D int array/vector that starts at address 0x10010000. The index to address mapping would be:
index offset address
----- ------ --------
0 0 10010000
1 4 10010004
2 8 10010008
In your code, you have:
addi $t4,$t4,-4 # t4 - 1
add $t4,$t4,$s0 # Now points to value at matrixA[i][k]
lw $t4,0($t4) # Loads value at matrixA[i][k]
The final index (e.g. $t4) needs to be multiplied by sizeof(int) [which is 4] before adding in the matrix base address. The idiomatic way to do this is a left shift by 2.
Also, when adding addresses to addresses or offsets to addresses, you should use the unsigned version of add (i.e. addu) to guard against overflow/wrap which can occur for addresses.
addi $t4,$t4,-1 # t4 - 1
sll $t4,$t4,2 # convert index to byte offset
addu $t4,$t4,$s0 # Now points to value at matrixA[i][k]
lw $t4,0($t4) # Loads value at matrixA[i][k]
You'll need to add this extra step whenever/wherever you do these index calculations.
The third problem is the final address 0x1000fffd while not four byte aligned, is also below the lowest address allowed for the .data segment in mars (i.e 0x10010000), so if you hadn't got the alignment fault, you'd be accessing non-existent memory [which will issue a different type of fault]
So, you may want to double check your index calculations for correctness to prevent the equivalent of accessing int myarray[2]; myarray[-1] = 3; [which is UB]
This is part of my code, and I don't know why, $t1 always ends up with 10, when it should be 16. At this point, the data is the following:
$t5 = 4
$t3 = 1
$t2 = 0
and $t1 is 0
(and $t0 is an address)
This is the part of the code:
mul $t1, $t3 , $t5
add $t1, $t1, $t2
mul $t1, $t1, 4
**From here, $t1 should be 16, but it always turns out 10 even if I do li $t1, 16****
add $t1, $t1, $t0
lw $t6, ($t1)
I'm using MIPS 32 with QTSpim
If you set registers->Decimal, the result will be 16. I guess, going from the information you give.
The following assembly code is given in my text book.
Loop:
sll $t1, $t0, 2
add $t2, $a0, $t1
sw $zero, 0($t2)
addi $t0, $t0, 1
slt $t3, $t0, $a1
bne $t3, $zero, Loop
# return where we were
jr $ra
From this code I have two question to ask.
The first question is about the second line from the top. I get that the instruction sll is shift left logical which shifts bit to the left. Since the shift amount is 2, it will make 0000 -> 0100 = 4 in decimal. But I don't get it after the first loop. If we shift this to the left by 2, isn't it multiplied by more than 4??
And the second question is if it is possible to optimize this code?? In my opinion, I can modify sll and add parts in the code but I am not sure.
Any comment??
Shifting left will insert 0's, not 1's. So 0000 would still be 0000, 0001 will become 0100 after the shift
[is it] possible to optimize this code?
A more compact way of doing the same thing would be:
sll $a1, $a1, 2
addu $a1, $a1, $a0 # $a1 = $a1 * 4 + $a0
Loop:
sw $zero, ($a0)
addiu $a0, $a0, 4
bne $a0, $a1, Loop
I'm making these assumptions:
The original values of $a0 and $a1 are not needed anymore after the loop ends. If they are needed, save the original values somewhere (in other registers or on the stack) before entering the loop, and restore them afterwards.
$t0 starts out at zero. If not, you'll have to add $t0 * 4 to $a0 before the loop. I'm also assuming that the value of $t0 after exiting the loop is irrelevant.
Consider the following binary:
0000 0001
If you shift the bits left by 1 digit you get:
0000 0010
If you shift again to the left by 1 digit:
0000 0100
And again:
0000 1000
The binary values above are equivalent to 1; 1x2=2; 2x2=4; 4x2=8.
Shifting bits to the left is multiplying the value by 2^N if N is the number of bits you are shifting.
Another example of shifting:
Assume $t1 contains 0000 1111
sll $t0, $t1, 3 # $t0 = $t1 * 2^3
Now $t0 contains 0111 1000
You can verify this by performing decimal multiplication. 15 * 8 = 120.
I know the program loads the address of .word 5 which is the initial value, I'm getting confused as to what the program is actually doing. Is it comparing stacks of the word or adding.
.data
arg: .word 5
.text
.globl main
main:
la $t3, arg
lw $t2, 0($t3)
lw $t3, 0($t3)
addi $t1, $zero, 0
beqz $t2, fin
fori:
add $t1, $t1, $t2
addi $t3, $t3, -1
bnez $t3, fori
fin:
li $v0, 10
syscall
Looks like it does addition based on the first item in the space as it doesn't access the array from inside the loop.
So for instance if you input a character representing the ascii value of 5 (not '5' itself iirc) you might see something like this:
int t2 = 5, t3 = 5, t1 = 0;
do {
t1 += t2;
t3 -= 1;
} while(t3 > 0);
Someone has already answered but I shall provide the translation I did:
.data
arg: .word 5
.text
.globl main
main:
la $t3, arg # load-address of arg into t3
lw $t2, 0($t3) # load-word from the address in t3 + 0 offset, like t3[0]
lw $t3, 0($t3) # same but to t3
addi $t1, $zero, 0 # initialize t1 to 0? weird they don't use li
beqz $t2, fin # if t2 is zero jump to fin:
fori:
add $t1, $t1, $t2 # t1 = t1 + t2
addi $t3, $t3, -1 # t3 -= 1
bnez $t3, fori # if(t3 != 0) goto fori
fin:
li $v0, 10 # load immediate 10 into v0, which is the syscall to terminate the program
syscall
Disclaimer: I don't have any direct MIPS experience, I just have been around a number of assembly languages.
That said, I think what the program does is calculating the square of 'arg' by repeated addition - in this case 5 * 5 = 25. However, the result in $t1 doesn't seem to be stored anywhere, instead the program just exits.