How do I access each word in Assembly? - arrays

Given:
.data
arr: .word 2,5,1,3,4
len: .word 5
sum: .word 0
How would I access each word in "arr" such as 2, 3 and 4?
Eventually, what I would like to do is find a sum of all of the values in "arr", but I'm having difficulty iterating through "arr".
Thank you for your time!
Additional Info:
I'm using eduMIPS64

First load the address of the array into a register, then you can access the items with a constant offset. (Your assembler might support constructs such as lw $t0, arr+12 as convenience shorthand for this. See your manual.) For iteration, either increment the address register, or add another register containing the offset. Be sure to account for item sizes. Folling example is for 32 bit mips, adjust as necessary for 64 bit:
.data
arr: .word 2,5,1,3,4
len: .word 5
sum: .word 0
.text
.globl main
main:
la $t0, arr
lw $t1, 12($t0) # load arr[3] using byte offset
li $t1, 3 # index
sll $t1, $t1, 2 # multiply by item size
addu $t1, $t1, $t0 # add base address
lw $t1, ($t1) # load arr[3]
# iteration type 1
# for(i=len, ptr=arr; i != 0; i -= 1, ptr += 1)
# ... use *ptr ...
la $t0, arr
lw $t1, len # load length
loop1:
lw $t2, ($t0) # load item
# process item here
addi $t0, $t0, 4 # add item size to pointer
addi $t1, $t1, -1 # decrement counter
bne $t1, $0, loop1 # if counter != 0, repeat
# iteration type 2
# for(i=0, i != len; i += 1)
# ... use arr[i] ...
la $t0, arr
lw $t1, len # load length
li $t2, 0 # index
loop2:
sll $t3, $t2, 2 # multiply by item size
addu $t3, $t3, $t0 # add base address
lw $t3, ($t3) # load item
# process item here
addi $t2, $t2, 1 # increment index
bne $t2, $t1, loop2 # if index != len, repeat
(note these sample loops do not handle zero length array, add check if necessary)

Related

MIPS 1D Integer Array, inserting at index

I'm new to MIPS and having some trouble with my program.
Right now my code works at inserting an integer value into the array at a specified index. But currently, when the integer is inserted it replaces the value that was previously there. How do I instead shift all elements after said index so the array's length increases by 1 and no values are lost?
Note that I'm using a special integer -22 to indicate the end of my array.
Any help would be greatly appreciated! Thank you!
Below is my code:
.data
beginarray: .word 1, 2, 3, 4, -22
array: .space 4000
str_command: .asciiz
str1: .asciiz "\nEnter an Integer: "
str2: .asciiz "\nAt what index: "
.text
.globl main
main:
# get length
la $a0, beginarray
jal length
addi $s0, $v0, 0 # $s0 = returned length
# copy beginarray into array
la $a0, beginarray
la $a1, array
jal copyarray
jal insert
# print array
la $a0, array
jal printarray
li $v0,10
syscall
# takes pointer to array as input
length: addi $t0, $zero, -22 # $t0 = -22
addi $v0, $zero, 0 # $v0 = 0 (length)
add $t1, $a0, $zero # $t1 = pointer to first int
calclength: lw $t2, 0($t1) # $t1 = value of current int
beq $t2, $t0, returnlength # if $t1 = -22
addi $v0, $v0, 1 # increment length
addi $t1, $t1, 4 # move pointer to next int
j calclength # loop
returnlength: jr $ra
# takes two pointers as input (pointer to first array and pointer to second array)
# copies contents of first array into second array
copyarray: addi $t0, $a0, 0 # $t0 = pointer to array1
# addi $t1, $a1, 0 # $t1 = pointer to array2
addi $t4, $zero, -22 # $t4 = -22
calccopy: lw $t2, 0($t0) # $t2 = value of array1
#lw $t3, 0($t1) # $t3 = value of array2
sw $t2, 0($a1) # store int in array
beq $t2, $t4, returncopy # if $t2 = -22
addi $t0, $t0, 4 # move a1 pointer to next int
addi $a1, $a1, 4 # move a2 pointer to next int
j calccopy
returncopy: jr $ra
# print array
printarray: add $t1, $a0, $zero # $t1 = pointer to first int
addi $t0, $zero, 0 # set counter to zero
printloop: beq $t0, $s0, done
# load word from addrs and goes to the next addrs
lw $t2, 0($t1)
addi $t1, $t1, 4
# syscall to print value
li $v0, 1
move $a0, $t2
syscall
# syscall for printing space
li $a0, 32
li $v0, 11
syscall
#increment counter
addi $t0, $t0, 1
j printloop
done: jr $ra
insert:
li $v0, 4 # system call code for print_str
la $a0, str1
syscall
li $v0 5 # system call code for read_int
syscall
move $t3, $v0 # $t3 = integer entered
li $v0, 4 # system call code for print_str
la $a0, str2
syscall
li $v0 5 # system call code for read_int
syscall
move $t1, $v0 # $t1 = index entered
li $t0, 0 # $t0 = counter
la $t2, array # $t2 = base address of array
for: bge $t0, $t1, end_for
add $t2, $t2, 4 # get address of next array element
add $t0, $t0, 1 # increment loop induction variable
b for
end_for:sw $t3, ($t2)
jr $ra

Simple MIPS for loop not running

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

MIPS: How to access an array full of zero with loop

I have an array like this:
r_clues: .word 0 : 512 # array full of zero
I do
la $s0, r_clues
lw $t1, 0($s1)
and I take the address of first 4 bytes and if i want to take, for example, 4th address i will do
lw $t1, 16($s1)
because its 4(address) * 4(bytes)
How i can access to this array with one loop and load word to a register for each 8 bytes ?
Calculate address of elements and load words.
la $s0, r_clues # the address
addiu $s2, $zero, 0 # offset
addiu $s3, $zero, 64 # number of loops
loop_begin:
addu $s1, $s0, $s2 # address = base + offset
lw $t1, 0($s1) # load the array
addiu $s2, $s2, 8 # proceed to the next element
addi $s3, $s3, -1 # substract the counter
bne $s3, $zero, loop_begin # if there are more elements to load, go to loop
nop # prevent next instruction from being executed before exiting the loop

MIPS Assembler trouble - Arrays

I have an assembly code containing an array, and I simply cannot understand what actually the result in the $s2 register shows. If someone could help and explain or simplify it for me that would be great.
Here's the code:
.data arr: .word 3 2 -6 1 4 10 530 115 2231 1422
arrSize: .word 10
.text
.global main
main:
la $s0, arr
la $t0, arrSize
lw $s1, 0($t0)
add $s2, $zero, $zero
loop:
lw $t1, 0($s0)
andi $t2, $t1, 1
bne $t2, $zero, skip
addi $s2, $s2, 1
skip:
addi $s0, $s0, 4
addi $s1, $s1, -1
bne $s1, $zero, loop
end:
add $v0, $zero, $s2
I've added some pseudo-code comments to the code:
.data arr: .word 3 2 -6 1 4 10 530 115 2231 1422
arrSize: .word 10
.text
.global main
main:
la $s0, arr ;; s0 = arr // init s0 = pointer to start of arr
la $t0, arrSize ;; t0 = &arr_size // get no of elements in arr
lw $s1, 0($t0) ;; s1 = arr_size = 10
add $s2, $zero, $zero ;; s2 = 0 // init count of even elements = 0
loop: ;; do { // for all elements in arr do
lw $t1, 0($s0) ;; t1 = *s0 // get element from arr
andi $t2, $t1, 1 ;; t2 = t1 & 1 // test element for odd/even-ness
bne $t2, $zero, skip ;; if (t2 != 0) // if not odd (i.e. even) then
addi $s2, $s2, 1 ;; s2++ // increment count in s2
skip:
addi $s0, $s0, 4 ;; s0++ // increment pointer to next element in arr
addi $s1, $s1, -1 ;; s1-- // decrement count of elements to process
bne $s1, $zero, loop ;; } while (s1 != 0) // end of do loop
end:
add $v0, $zero, $s2 ;; v0 = s2 // return result in v0
It seems that this just iterates through the elements of arr, testing each element to see if it is even, and incrementing a count of even elements found. The final result (in v0 and s2) will be 6, since there are 6 even elements in the array.

Mips reading integer and store them in array then find the max

again
I'm writing a MIPS program that reads 5 integers and stores them in array. It then creates a new array whose values are the values of the initial array multiplied with their index. After that I need to find the max and min of the second array.
I'm new to MIPS, here is the code I wrote :
.data
Prompt: .asciiz "\n Enter 5 Integers :" #gets number of integers
op: .asciiz "\n Enter Option: \n 1-Find the mult \n 2- Find the max \n 3-Find the min \n 4-Exit"
invalidm: .asciiz "\n Bad Input:"
vec: .space 20
vec2: .space 20
.text
.globl __start
__start:
la $a0,Prompt
li $v0,4
syscall
#Reading integers and store theme in the array
options:
la $a0,op
li $v0,4
syscall
li $v0,5
syscall
blt $v0,1,invalid#
bgt $v0,4,invalid#
beq $v0, 1, multp#
beq $v0, 2, max#
beq $v0, 3, min#
beq $v0, 4, exitpro#
j options
multp:
#multiply every element in the array with its index and store them in the new array vec2
j options
max:
#find the max of the array vec
j options
min:
#find the min of the array vec
j options
invalid:
la $a0,invalidm
li $v0,4
syscall
j options
exitpro:
li $v0,10
syscall
I'd like to make a quick note on coding style: assembly is a bit diabolical. It's very tempting to say "oh, I do these 5 lines a lot, I should just jal here and reuse it". This makes sense at first, but tends to result in confusing spaghetti code that can't go 3 lines without jumping somewhere. As such, my code has a bit of code repetition, but nothing too bad.
I altered your initial conditions a little, the length of the array is hard coded, but it uses a stack-allocated array rather than one that's statically allocated at initialization in the .data header. Instead the array's length is allocated in the data section. This was just personal preference. It has the benefit that the code should work for any length > 0.
.data
PromptHead: .asciiz "\n Enter "
PromptTail: .asciiz " integers\n"
Minstr: .asciiz "Min: "
Maxstr: .asciiz "Max: "
newline: .asciiz "\n"
inputs: .word 5
.text
main:
# Output prompt for input
## "\n Enter "
la $a0, PromptHead
li $v0, 4
syscall
## "5"
lw $a0, inputs
li $v0, 1
syscall
## " integers\n"
la $a0, PromptTail
li $v0, 4
syscall
# Backup sp value before allocating array
move $fp, $sp
# calculate size of vector (numinputs * sizeof(int) = numinputs * 4 = numinputs << 2), store in $s1
lw $s0, inputs
sll $s1, $s0, 2
# dynamically grow stack to include array of ints
add $sp, $sp, $s1
# Loop initialization
# i = 0
move $s1, $zero
# while i < numInputs
ReadInput:
slt $s2, $s1, $s0
beq $s2, $zero, ReadInputDone
# Read integer
li $v0, 5
syscall
# Calculate array offset for this loop
sll $s2, $s1, 2
add $s2, $fp, $s2
# Store array value at calculated address
sw $v0, 0($s2)
# i++
addi $s1, $s1, 1
j ReadInput
ReadInputDone:
# Scale(array, length)
move $a0, $fp
move $a1, $s1
jal Scale
# max = FindMax(array, length)
jal FindMax
# Back up return val, print boilerplate
move $t0, $v0
li $v0, 4
la $a0, Maxstr
syscall
move $a0, $t0
li $v0, 1
syscall
la $a0, newline
li $v0, 4
syscall
# min = FindMin(array,length)
move $a0, $fp
jal FindMin
# Back up return val, print boilerplate
move $t0, $v0
li $v0, 4
la $a0, Minstr
syscall
move $a0, $t0
li $v0, 1
syscall
la $a0, newline
li $v0, 4
syscall
# Exit program
li $v0, 10
syscall
# Scale(array, length)
# Scales each array element by index+1. This does not alter any s or a registers,
# Alters the array in place.
Scale:
# Backup return address and fp on stack.
# Not strictly necessary here, but usually good to do this by habit
# when you're learning
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# Loop initialization as above
# i = 0
move $t2, $zero
# while i < 5
ScaleInput:
slt $t3, $t2, $t1
beq $t3, $zero, ScaleInputDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, multiply it by current index+1
# Grab it from the multiplication register (assume no overflow)
# then store the result back in the array
lw $t5, 0($t3)
addi $t4, $t2, 1
mult $t5, $t4
mflo $t5
sw $t5, 0($t3)
# i++
addi $t2, $t2, 1
j ScaleInput
ScaleInputDone:
# Unwind stack, restore frame pointer and
# return address. Again, not necessary here, but good
# practice
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
# int Max(array, length); result returned in $v0,
# No s or a registers are altered
FindMax:
# Backup return address and fp on stack.
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# set currMin = array[0]
lw $v0, 0($t0)
# Loop initialization
# i = 1
li $t2, 1
# while i < 5
MaxLoop:
slt $t3, $t2, $t1
beq $t3, $zero, MaxLoopDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, check if it's the new max
lw $t4, 0($t3)
sgt $t5, $t4, $v0
beq $t5, $zero, notGreater
# If so, set return value to it
move $v0, $t4
notGreater:
# i++
addi $t2, $t2, 1
j MaxLoop
MaxLoopDone:
# Unwind stack
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
# int Min(array, length); result returned in $v0,
# No s or a registers are altered
FindMin:
# Backup return address and fp on stack.
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# set currMin = array[0]
lw $v0, 0($t0)
# Loop initialization
# i = 1
li $t2, 1
# while i < 5
MinLoop:
slt $t3, $t2, $t1
beq $t3, $zero, MinLoopDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, check if it's the new min
lw $t4, 0($t3)
slt $t5, $t4, $v0
beq $t5, $zero, notLesser
# If so, set return value to it
move $v0, $t4
notLesser:
# i++
addi $t2, $t2, 1
j MinLoop
MinLoopDone:
# Unwind stack
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
As I mention in the code, the monkeying around with storing the $ra and $fp is a bit paranoid. If I was REALLY paranoid I'd store all the s registers as well. However, it's generally good practice and can save you a lot of headaches when you decide to add "function calls" in the middle of a function.
This is basically the approach to assembly where you write the program in C in your head and then translate that rather literally into assembly. Hence why I treat scaling, finding the max, etc as "functions".
A couple assemblyish notes: I repeatedly use sll $register, $register, 2 instead of multiplying by 4 (word size on MIPS32). This is because doing this is fewer instructions due to not having to cal li followed by mult followed by mflo. You can do it that way, and I used to, but once you get used to using sll and other bit fiddling operations a lot it's just cleaner to use sll as well as easier to read.
I do use a few pseudoinstructions, it's not a big deal, most modern MIPS assemblers and simulators that I'm aware of support them (including SPIM). I dance dangerously with the line after branch instructions (which are theoretically always executed regardless of the branch result), but in this case it wouldn't usually matter and most simulators and assemblers usually inject a no-op for you when it does anyway.
I know assembly can be a bit of a pain to read, so feel free to ask if you have any questions.

Resources