I came across this problem that asks to convert from c to MIPS code.
B[8] = A[i-j];
where $s6 and $s7 stores the base address of array A and B. i and j are stored in $s3 and $s4.
This is my solution:
sub $t0,$s3,$s4 # i - j
sll $t0,$t0,2 # (i - j) * 4
add $t0,$s6,$t0 # A + (i - j) * 4
lw $t1,0($t0) # *(A + (i - j) * 4)
sw $t1,32($s7) # *(B + 32) = *(A + (i - j) * 4)
This is the solution given to us:
sub $t0, $s3, $s4
add $t0, $s6, $t0
lw $t1, 16($t0)
sw $t1, 32($s7)
The solution given to us doesn't make any sense to me as it certainly fails almost all cases as the address might not be word aligned. Does anyone think that the solution given to us is correct?
Related
I am doing a homework problem for the computer design and architecture class where we are required to implement a simple for loop in C, into MIPS using the MARSim. I implemented the for loop step by step, initialized (if that is the proper term) the variables in memory, yet when I assemble and run it throws this error: question1.asm line 11: Runtime exception at 0x00400008: address out of range 0x00000000
I've looked at line 11 : lw $t1, 0($a1),
and as I understand it, this should work properly. As I understand here we are setting our t1 value to a1 (b[i]).
Here is the C we have to reproduce:
for (i=0; i<=100; i++) { a[i] = b[i] + C ; }
Here is my attempt:
# t0 = i
# t1 = b[i]
# t2 = a[i]
# t3 = 101 (the end value of i)
# s0 = c
# $a0 = a
# $a1 = b
begin:
addi $t3, $zero, 101 #loop terminate value
add $t0, $zero, $zero # set our counter to zero
loop: lw $t1, 0($a1) # set t1 to b[i]
add $t1, $t1, $s0 # B[i] + c
sw $t1, 0($a0) #store $t1 into a[i]
addi $t0, $t0, 1 # loop increment
addi $a0,$a0,4 # increment a0 to point to the next block in the array (4 bits)
addi $a1, $a1, 4 # likewise with b[i]
beq $t0, $s0, finish
finish:
The MIPS code I've written, should mimic the action of the C code w/o error. However on the first iteration of the loop it states it is accessing an out of range address 0x00000000. Could someone shine some light on what I am doing wrong? I would really appreciate a thorough explanation so I can understand this better for my class. Thanks for your assistance and much love.
Cheers.
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
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I was trying to solve this and convert it to MIPS assembly code
but the answer in the book confused me.so, can anyone explain how this code get this result in c ?
B[g] = A[f] + A[f+1];
I have inserted comments for what I think that's right please correct me if I was wrong.
Assume we have variables f, g, h, i, j stored in $s0, $s1, $s2, $s3 and $s4, respectively. Assume the base addresses of arrays A and B are at $s6 and $s7.
The code:
add $t0, $s6, $s0 #This will add f bytes to the base address and it's not equal to A[f].
add $t1, $s7, $s1 #This will add g bytes to the base address and it's not equal to B[g]
lw $s0, 0($t0) #This will point to the value in (f +base address of A +0) bytes
lw $t0, 4($t0) #This will point to the value in (f +base address of A +4) bytes
add $t0, $t0, $s0
sw $t0, 0($t1)
Your compiled fragment annotated by me:
add $t0, $s6, $s0
add and store in register t0 whatever is in register s6 and s0. Since you have pointed out that f is stored in s0 and the base address of A is stored in s6, this adds the addresses in preparation for a register indirect load later on. More simply A[f] == *(A + f) in C, and this is preparing for the (A + f) de-reference later on.
add $t1, $s7, $s1
The same thing happening for B and g. Add their contents and store them in an intermediate register, to be later used as address based de-reference targets.
lw $s0, 0($t0)
This is loading to the s0 register, using what's known as the register-indirect addressing mode of the cpu, whatever is at the address pointed to by t0 plus 0 bytes. In c this is equal to s0 = *(A + f).
lw $t0, 4($t0)
The same thing as above, only that this time it loads to register t0 whatever is pointed at t0 plus 4 bytes. Equal to C t0 = *(A + f + 1).
add $t0, $t0, $s0
This is the point where it performs the addition in your code. It's equal to the C code fragment of A[f] + A[f + 1].
sw $t0, 0($t1)
This is storing the result of the previous addition to the address pointed to by t1.
~~~~~~~~~~~
If you are looking for some references for the code you have, I found both this MIPS instruction set reference useful and, of course, Matt Godbolt's interactive compiler.
If you want to see what code does what using the interactive compiler, just wrap your code in a void function, select as the compiler x86 clang and at the compiler options --target=mips. Then from the filter apply colourise, and you will be able to see what C code generates what assembly code, to get something like the image below
add $t0, $s6, $s0 # t0 = A + f
add $t1, $s7, $s1 # t1 = B + g
lw $s0, 0($t0) # s0 = *(t0 + 0) = *(A + f) = A[f]
lw $t0, 4($t0) # t0 = *(t0 + 4) = *(A + f + 1) = A[f+1]
add $t0, $t0, $s0 # t0 = t0 + s0 = A[f] + A[f+1]
sw $t0, 0($t1) # *(t1 + 0) = *(B + g) = B[g] = t0
Remember that C pointer arithmetic scales by item size, but assembly uses bytes. Hence, advancing 4 bytes is advancing 1 item in C.
On second thought, this in fact means f and g should also be scaled by 4, which they don't seem to be.
sll $t0, $s0, 2 # $t0 = f * 4
add $t0, $s6, $t0
sll $t1, $s1, 2 # $t1 = g * 4
add $t1, $s7, $t1
lw $s0, 0($t0)
lw $t0, 4($t0)
add $t0, $t0, $s0
sw $t0, 0($t1)
Yes, the code is missing these two lines, you just need to scale both f and g by 4.
Could someone help walk me through what some lines in this MIPs code are doing?
The C code is B[8] = A[i - j] with i = $s3, j = $s4, &A[] = $s6, and &B[] = $s7
The MIPs code is as follows...
sub $t0, $s3, $s4 # i - j
sll $t0, $t0, 2 #Gets the offset of 8 for B[]
add $t0, $s6, $t0 #Goes to the offset in B[] ?
lw $t1, 0($t0) #????
sw $t1, 32($s7) #????
I get a bit lost once it gets to the last 3 lines.
Why is it 0($t0) and 32($s7)? Or just why 0 and 32?
sll $t0, $t0, 2 // This multiplies (i-j) * 4, not 8. Because the indexes are 4-byte ints
add $t0, $s6, $t0 // $t0 = A + (i-j)*4. So $t0 = &A[i-j]
lw $t1, 0($t0) // $t1 = A[i-j]
sw $t1, 32($s7) // B[32/4] = $t1
32($s7) means $s7 + 32. The reason you add 32 because you are accessing the 8th element of an integer array, which is located in the memory address B + 8*sizeof(int) = B + 32
I'm trying to learns the MIPS ISA. If I want to do this function in MIPS:
A[2*i]=A[2*k+j];
How would I go about this? I would also appreciate any other content that I can read, i.e links where I can read up on how to solve this kind of problem.
We can break this down to 2 parts:
Calculate the addresses of A[2*i] and A[2*k+j]
Assign the value at the second address to memory at the first address
I'm only going to address (ahem) #1.
To calculate the address of an array element, you need to know 3 things:
The address of the start of the array
The index of the element you want
The size of the array elements
I assume you know how to compute, or just know, #1 & #3. That leaves #2, which involves simple arithmetic. (Since you haven't indicated how i, j & k are represented, I can't help too much there).
The final step, then, is to multiply the index by the size of an array element; this gives you the offset of your desired element from the start of the array. Add that to the address of the start of the array, and you have your element's address.
P.S. The code you're translating doesn't swap two elements; it copies one over the other.
It's been a while, but this could be close. You'll never learn assembly language without trying youself. Make more examples and code them. More study material here .
# int calc(int *A, int i, int j, int k)
# {
# return A[2 * i] = A[2 * k + j];
# }
# Args: a0=A, a1=i, a1=j, a3=k Rtn: v0
.text
.set nomacro
.set noreorder
.global calc
.ent calc
calc:
sll $t0, $a1, 3 ; t0 = i * 8
sll $t1, $a3, 1 ; t1 = k * 2
add $t1, $t1, $a1 ; t1 += j
sll $t1, $t1, 2 ; t1 *= 4
add $t0, $t0, $a0 ; t0 += A
add $t1, $t1, $a0 ; t1 += A
lw $v0, 0($t1) ; r = A[4 * (2 * k + j)]
sw $v0, 0($t0) ; A[4 * (2 * i)] = r
.end calc