I writing a MIPS program in mars.
I am looping through an array of addresses.
I write this:
addi $t4, $t4, 8
where $t4 contains the base address of my array of addresses, and it gets shifted by 8 each time the loop runs. Then, after the loop is done, I store an address at that location.
My question: am I correct in adding 8? or should i add 1(1 byte=8bits, not sure what number to use when adding to addresses...)
You should be adding the size in bytes of each element of the array. Since an address on MIPS is 32 bits (4 bytes), you should add 4.
Related
I'm trying to translate a simple C function (that tells you whether a word, made by a char array, is a palindrome) into MIPS 32 but I'm having trouble using getting to load non-multiple-of-4 positions of the array.
Apparently,
`li t0,0(a0)`
loads the first letter (char), and
`li t0,4(a0)`
loads the fifth letter of the array (I thought it would have been the second one). Trying to load the second, as in:
`li t0,1(a0)`
gets me a segmentation fault. So does using shift left logical before loading 0(a0). How do I solve this?
It's a 32-bit platform and it requires aligned access. So if a0 is aligned to 4 bytes, then li t0,0(a0) will load the first 32-bit value of a0 into t0. But li t0,1(a0) will try (and fail) to load a misaligned 32-bit value.
So don't try to load one character at a time. Embrace the fact that MIPS 32 will load 4 characters (32 bits) at a time. You can access single characters within a word using shift and bitwise-and.
Nevermind, here's how I got it to work:
addu a0,a0,t0 #t0 = i, a0 <- a0+i
lb t2,0(a0) #store array[i] in t2
subu a0,a0,t0 #returns a0 to original value
If I want to get to the second letter, I load 1 in t0, for instance.
Say I have a word array, wa, if I say:
wa: .word 1, 2, 3, 4
la $s0, wa
lw $t0, 8($s0)
does $t0 now contain the third element from right-to-left ot left-to-right (would it be 3 or 2)?
Similarly, if I have a single byte, in s0 and a constant 0 in t3, would saying:
srlv $t0, $s0, $t3
would $t0 contain the 0th bit (rightmost) or seventh bit (leftmost)>
BTW, if you have an idea, I have to do a single read to get an 8-bit value that I can shift, as how I ask above, how should I read this?
Currently, I do read int, but just realized, the shifting will not work because it is a 4 byte value.
Thanks, in advance, if you can answer anyof these questions!
Memory array is word-addressable.Each 32-bit data word has a unique 32-bit address. Both the 32-bit word
address and the 32-bit data value are written in hexadecimal.
For example, data 0xF2F1AC07 is stored at memory address 1. Hexadecimal constants are written with the prefix 0x. By convention, memory is
drawn with low memory addresses toward the bottom and high memory
addresses toward the top.[Petterson & Hennessey].
So in your case lw $t0, 8($s0) take the 3 from memory and place it in register $t0.
In MIPS, I have created an array using .byte that is initialized with values.
array: .byte 1,2,3,4,5,6,7,8,9
those values are stored in memory as 8 bit integers, for example:
0x04030201
How can I access the individual values in order to sum the integers? Is using a bit mask the only way? Is there an easier way to do it?
You could use the opcode lb $t, offset($s). It works the same as lw $t, offset($s), but it loads a byte instead of a 4-byte word into $t.
So let's say you want to load the 6th byte of the array. You would do:
la $t0, array # load the array address
lb $t1, 5($t0) # get 6th byte through an offset
# then do whatever you want with it here
EDIT: You also have lh for 2-byte halfwords. Also, here's the MIPS instruction reference I used to answer your question: http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html
I am looking at a 32x32 register file, with $s0-$s7, $t0-$t9, $zero, $a0-$a3, $v0-$v1, $gp, $fp, $ra, and $at.
My question is how is an array stored in these register files? Aren't they only each 32-bits wide?
For example, given base address of an array A is $s3, if I were to give instruction to get A[8]:
lw $t0, 32($s3)
How does it retrieve the data?
Array access is made through pointer (something the C people are very familiar with), so the register simply holds the base address of the array. You are then adding 8 * 4 = 32 bytes to that base address to get the address of the 8th element and finally dereferencing that pointer (which means seeing what's at that address) to get the value (with the lw instruction).
The instruction you have shown is the translation of this C code:
t0 = *(s3 + 8) // same as s3[8]
I am studying the MIPS assembly language and came across this example in the book and to me it seems incorrect. If it is it wouldn't be the first mistake I found in this book.
The variables f and g are assigned registers $s0 and $s1 respectively, the base addresses for the arrays A and B are $s6 and $s7 respectively.
The c code example is:
f = g - A[B[4]];
And the corresponding MIPS assembly provided is:
lw $t0, 16($s7)
lw $s0, 0($t0)
sub $s0, $s1, $s0
From my understanding the above MIPS code would load some random data from memory at the address provided by $t0 and then subtract it from $s1 and not access the index $t0 of the array denoted in $s6.
The correct MIPS assembly from my understanding would be along the lines of:
lw $t0, 4($s7)
add $t0, $t0, $s6
sll $t0, $t0, 2
lw $s0, 0($t0)
sub $s0, $s1, $s0
I am correct that this is an error in the book or am I misunderstanding something.
Edit: Fixed an error in the corrected mips code as pointed out by Chris Dodd
This is for anyone (possibly CprE 381 students) who may stumble upon this looking for a good example. The OP's edited code is still incorrect. The offset in the first load word function should be 16. It could be 4 if the memory width is 32 bits, but then the shift/multiplication would not be needed. Assuming the memory is 8 bits wide, the add and shift functions need to be switched. In OP's code, it's multiplying the address of A[B[4] / 4] by 4. Shifting/multiplying first will get the correct index. The correct code is:
lw $t0, 16($s7) # gets the value of B[4]
# offset could be 4 depending on memory width
# but then the shift would not be needed
sll $t0, $t0, 2 # this multiplies the index by 4 to get the address offset
add $t0, $t0, $s6 # adds the base address of A and the offset
lw $t0, 0($t0) # loads the value at the address
sub $s0, $s1, $t0 # performs subtraction and stores in f
In case anyone is confused about the whole offset of 16 vs 4 and whether the shift is needed, let me explain. If the memory width is 32 bits then an entire 32-bit integer can be stored in one memory location. If this is the case, then the array index is the same as the address offset. However, if the memory is only 8 bits (1 byte) wide, then a 32-bit integer is stored across 4 memory locations (1 address for each byte). This is why you need to shift the index by 2 (or multiply by 4) to get the correct address offset.
As pointed out my many there was an error in the book. Since discovering this error I found several such errors.
But it could very well be that the author copied the code before link time. This would leave open the possibility that the linker fills in the memory address of A[] in place of the 0 in the statement
lw $s0, 0($t0)
in the final executable. I don't know if MIPS allows offsets of that size (that is, the address range where A[] is being placed finally). This of course is no nice way to explain something in a book, breaking one's own premises silently and generally not knowing what is going on.