How do I avoid hard coding array index/indices within a loop in MIPS? - arrays

For example,
$a0 is an index/pointer
$a1 is the address of the array base
I want to access each element of the array within a loop, perform an arithmetic operation to that element, then save it to the next. The index should increase +1 on each iteration.
For a simple example, I'm trying to square each previous element in the array. Initial hard coded values are INDEX[0]=0, ARRAY[0]=2. I've marked where I'm confused. I don't know how to make this variable for each loop.
.data
INDEX: .space 4
.align 2
ARRAY: .space 16
.align 2
.text
la $a0, INDEX
la $a1, ARRAY
li $t0, 0
sw $t0, ($a0) # set INDEX[0]=0
li $t0, 2
sw $t0, ($a1) # set ARRAY[0]=2
LOOP:
lw $t0, ($a0)
sll $t0, $t0, 2 # $a0 * 4 to get element offset
lw $t1, $t0($a1) # STUCK HERE (1)
add $t1, $t1, $t1 # square
lw $t0, ($a0)
add $t0, $t0, 1
sw $t1, $t0($a1) # AND HERE (2)
add $a0, $a0, 1
... keep looping if space remaining in $a1
(1) How do I save the element ARRAY[INDEX] to register $t1 without hardcoding the offset?
(2) How do I save the altered register $t1 to a specific element in the array: ARRAY[INDEX] = $t1
Since the indirect addressing will change on each loop, I want to avoid using 4($a1), 8($a1), etc.
Thank you.

You need to add the base address and index together. Something like this ought to work:
sll $t2, $t0, 2 # $a0 * 4 to get element offset
add $t2, $t2, $a1 # Add the array base address to the scaled index
lw $t1, ($t2) # $t1 = ARRAY[index]
add $t1, $t1, $t1 # square
add $t0, $t0, 1
sw $t1, ($t2)
Note that you're not actually squaring numbers - you're doubling them (t1 = t1 + t1). To square a number you'd multiply it by itself.

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 - Help initializing array

I am trying to initialize memory array to values 1, 2, 3.. 10. Though I am having a bit of trouble. Here is my work so far:
.data
myarray: .space 10
.text
la $t3, myarray # Load the address of myarray
addi $t2, $zero, 1 # Initialize the first data value in register.
addi $t0, $zero, 10 # Initialize loop counter $t0 to the value 10
top:
sw $t2, 0($t3) # Copy data from register $t2 to address [ 0 +
# contents of register $t3]
addi $t0, $t0, -1 # Decrement the loop counter
bne $t0, $zero, top
Any help would be much appreciated.
There are several problems with your code.
If you use sw (store word), you assume a "word" array. Its size should be 4*10. If you wand a byte array, use sb .
You do not increment the array pointer in $t3
Same problem for the array values in $t2
.data
myarray: .space 10
.text
la $t3, myarray # Load the address of myarray
addi $t2, $zero, 1 # Initialize the first data value in register.
addi $t0, $zero, 10 # Initialize loop counter $t0 to the value 10
top:
sb $t2, 0($t3) # Copy data from register $t2 to address [ 0 +
# contents of register $t3]
addi $t0, $t0,-1 # Decrement the loop counter
addi $t3, $t3, 1 # next array element
addi $t2, $t2, 1 # value of next array element
bne $t0, $zero, top
As suggested by #PeterCordes, this can be optimized by merging the loop counter and the array values register to suppressed one instruction in the loop. The corresponding loop in C will be
for(i=1, ptr=array; i!=11; ptr++,i++) *ptr=i;
And the corresponding code
.data
myarray: .space 10
.text
la $t3, myarray # Load the address of myarray
addi $t2, $zero, 1 # Initialize the first data value in register.
addi $t0, $zero, 11 # Break the loop when array value reaches 11
top:
sb $t2, 0($t3) # Copy data from register $t2 to address [ 0 +
# contents of register $t3]
addi $t2, $t2, 1 # Increment array value/loop counter
addi $t3, $t3, 1 # next array element
bne $t0, $t2, top

Getting address out of range in MIPS assembly?

I am trying to reverse an array using only one array. When the index of high is less than or equal to low, I am supposed to keep switching the content of high and low. However, I am getting address out of range and am having trouble finding resources on how to fix it.
I'm supposed to only use one array while reversing the array. What am I doing wrong? Starred is where I am getting the error.
reverseArray:
li $t6, 0 #head = first index of array
la $s0, array
li $t5, 4
mult $s0, $t5
mflo $t7 #tail = last index of array
swap:
lw $t6, 0($s0)
**lw $t4, 0($t7)**
sw $t4, 0($s0)
sw $t6, 0($t7)
add $t7, $t7, -4
add $s0, $s0, 4
sle $t1,$t7,$s0
beq $t1,$0,swap
I assume what you want in $t7 is the address of the last element of the array. Multiplying the base address by 4 is not going to give you that address. What you need to calculate is array + (length-1)*4.
So you need to know the length of the array. The code you've posted doesn't indicate that you know the length of the array, but let's say that you've got the length in register $t6. The calulation you need to perform then is:
addiu $t7,$t6,-1 # $t7 = length-1
sll $t7,$t7,2 # $t7 = (length-1)<<2 == (length-1)*4
addu $t7,$t7,$s0 # $t7 = (length-1)*4 + array
swap:
lw $t6, ($s0)
lw $t4, ($t7)
... etc ...

MIPS assembly - simple query on how to make the numbers inputted into an array

Below is the code I have which does work. It inputs and stores the numbers the user types (can only be a list of 3 or 4 numbers).
However, this is really long and using an array index would be a lot less code and use less registers but I am uunsure how to do this. Would I need to put brackets around the source2 to make it indexed?
# Entering the user's numbers
# Prompt user to enter their numbers
la $a0, number # load the address of number into $a0
li $v0, 4 # 4 is the print_string syscall
syscall
# Declare an array
la $t3, array # load address of array into $t3
li $t4, 0 # index value 0 is the start of the memory address of the array
mul $t5, $t4, 4 # multiply index value by 4 because each element is four bytes
add $t5, $t3, $t5 # add base address of array to index value into $t5
# Get the first number from the user, put into $t1
li $v0, 5 # load syscall read_int into $v0
syscall # make the syscall
move $t1, $v0 # move the number read into $t1
sw $t1, 0($t5) # store number held in $t1 into memory address location $t5
# Get the second number from the user, put into $t2 and store in array
li $v0, 5 # load syscall read_int into $v0
syscall # make the syscall
move $t1, $v0 # move the number read into $t1
add $t5, $t5, 4 # add 4 bytes to go to the next position in the array
sw $t1, 0($t5) # store number into memory address location of $t5
# Get the third number from the user, put into $t3
li $v0, 5 # load syscall read_int into $v0
syscall # make the syscall
move $t1, $v0 # move the number read into $t1
add $t5, $t5, 4 # add 4 bytes to go to the next position in the array
sw $t1, 0($t5) # store number into memory address location of $t5
# Branches to L3 if user chose to enter only three numbers
beq $t0, 3, L3 # if content in $t0 = 3, branch to L3
# Get the fourth number from the user, put into $t4
li $v0, 5 # load syscall read_int into $v0
syscall # make the syscall
move $t1, $v0 # move the number read into $t1
add $t5, $t5, 4 # add 4 bytes to go to the next position in the array
sw $t1, 0($t5) # store number into memory address location of $t5
# Branches to L3 if user chose to enter only four numbers
beq $t0, 4, L3 # if content in $t0 = 4, branch to L3
EDIT: So far i have this....it isnt working though - it isnt accepting integers which the user has entered
loop:
lw $t2, 0($a0) # load array element from memory
addi $t2, $t2, 1 # increment element
sw $t2, 0($a0) # write back to memory
addi $a0, $a0, 4 # increment array pointer by 4 (word = 4 bytes)
addi $t1, $t1, 1 # increment loop counter by 1
blt $t1, $t0, loop # loop, if necessary
Is it not working because I already have numbers in $t0? I ask the user what number of list they want (either 3 or 4 and that is stored in $t0) but then I ask them to enter their list of numbers (which I need to put into an array). If I then put this array also in $t0. Would it overwrite the numbers the user entered first when prompeted to say what list length they wanted?

MIPS code broken

I'm working on some MIPS code for my Computer Organizations class, and well I just can't seem to get the MIPS to work correctly and there's not that many MIPS resources online. I'm running the code on PCSPIM. The code is supposed to add 10 to the contents of array2 and store them in array1 and then print array 1. Reworked the code works properly now.
.text
main:
la $t0, array1
la $t1, array2
la $s0, valuec
li $s2, 6
add $t6, $zero, 1 #i = 1
Loop:
addi $t6, $t6, 1 #i++
lw $t2, ($t0)
lw $t5, ($t1)
lw $s1, ($s0)
addu $t2, $t5, $s1
sw $t2, ($t0)
add $t0, $t0, 4
add $t1, $t1, 4
li $v0, 1
move $a0, $t2
syscall
blt $t6, $s2, Loop
li $v0, 10
syscall
.data
array1: .space 20
array2: .word 1,2,3,5,9
valuec: .word 10
PCSPIM prints 0 5 times and returns Exception 7 [Bad data Address] occured and ignored
This is homework so I'm only going to give you clues for now and add to it as you go. A couple of things:
1/ You need to tell us what it's supposed to do. That's the most important thing.
2/ You store array1 address into t0 then reuse t0 within the first loop.
3/ You appear to be confused about addresses and the contents of those addresses ("la $s0, valuec" and "addu $t0, $t1, $s0").
UPDATE:
Actually I have to sign off for a while, so I'll post my solution so as to not leave you in the lurch.
The confusion I referred to before was the fact that you're loading up two addresses into $t1 and $s0, then adding them together to get another address - this is likely to be well beyond your data area (you should really be adding an address and an offset).
That's basically the problem you have with your code (both the zeros being printed and the crash). Your best bet would be to fix that and refer to my code below only as a last resort to see how I would have done it. Copying code will not help you in the long term and you would be wise to assume your educator is also checking all web sites for plagiarism.
This is the code I've come up with (quickly, so you'll need to test it - it may have bugs). I suggest you read the comments in great detail to understand what it's doing.
I'll be back in a few hours to see how you're doing. Cheers.
.text
main:
# Initialization of array pointers and loop
la $t0, array1 # address of array 1
la $t1, array2 # address of array 2
li $t2, 1 # element number
li $t3, 6 # upper limit of elements
# Process each word in array 2, adding 10 and placing
# into array 1.
Loop:
lw $t3, 0($t1) # get word from array 2
addi $t3, $t3, 10 # add 10 to word
sw $t3, 0($t0) # store word into array 1
addi $t0, $t0, 4 # move to next entry in array 1 and 2
addi $t1, $t1, 4
addi $t2, $t2, 1 # increment element number
blt $t2, $t3, Loop # loop until all elements done
# Initialize printing loop by going back to start of array 1
la $t0, array1 # address of array 1
li $t2, 1 # element number
# Loop through array 1, printing each element.
pLoop:
lw $t2, 0($t0) # get word from array 1
li $v0, 1 # 'print' command code
move $a0, $t2 # needs value in $a0
syscall # print it
addi $t0, $t0, 4 # move to next entry in array 1
addi $t2, $t2, 1 # increment element number
blt $t2, $t3, Loop # loop until all elements done
li $v0, 10 # 'terminate' command code
syscall # exit
# Data arrays for array 1 and 2
.data
array1: .word 0,0,0,0,0
array2: .word 1,2,3,4,5
There's a number of possible issues, but the most obvious/likely is that you aren't returning from main before the start of your data section.
jr ra

Resources