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

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

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

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.

Strange bug in MIPS code

I am making a program for a user to input a range of numbers, and calculate the min, max and median. Right now, I am only trying to collect the numbers and echo them back to make sure I am getting them. Here is the problem:
I input numbers like this:
1
2
3
4
5
And when the array is printed out I get:
15345
It does not matter what numbers are used, the 2nd element in the array is always replace by the last element.
Here is my mips code, I know it is kind of long, but it is the shortest workable example I can make.
Note: you must enter 9999 for the program to exit the loop.
.data
welcomeString: .asciiz "Please input one number at a time, and then press enter.\n"
intArray: .word 4000
size: .word 0
.text
main:
li $v0, 4
la $a0, welcomeString
syscall
la $a0, intArray
jal gather_numbers
la $a0, intArray
jal print_array
####################################################################################
gather_numbers:
addi $sp, $sp, -12
sw $a0, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
sw $t1, 12($sp)
move $s0, $a0 #the address of the array
lw $s1, size # load the size
li $t1, 0 # so it enters the loop
start_gather_numbers: beq $t1, 9999, exit_gather_numbers
li $v0, 5 # read the integer
syscall
sw $v0, 0($s0)
move $t1, $v0 # put the value into t1 to be tested
addi $s0, $s0, 4 #increment the address
addi $s1, $s1, 1 # increment the size
j start_gather_numbers
exit_gather_numbers: addi $s1, $s1, -1 # fix the size
sw $s1, size # store the size
lw $a0, 0($sp) # pop the stack
lw $s0, 4($sp)
lw $s1, 8($sp)
lw $t1, 12($sp)
addi $sp, $sp, 12
####################################################################################
####################################################################################
print_array:
addi $sp, $sp, -16
sw $a0, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
sw $t0, 12($sp)
sw $t1, 16($sp)
move $s0, $a0 # the address of the array
lw $s1, size # load the size of the array
li $t0, 0 # i = 0
start_print_array: bge $t0, $s1, exit_print_array
lw $t1, 0($s0) # load the int to print
li $v0, 1 # print the integer
move $a0, $t1
syscall
addi $s0, $s0, 4
addi $t0, $t0, 1
j start_print_array
exit_print_array: lw $a0, 0($sp)
lw $s0, 4($sp)
lw $s1, 8($sp)
lw $t0, 12($sp)
lw $t1, 16($sp)
addi $sp, $sp, 16
There are a few things going wrong here.
Firstly all of your functions are missing the terminating jr $ra.
Also, your stack manipulation is wrong. You are consistently allocating 4 less bytes than you use. If you want to put 5 words on the stack you should expand the stack by 20 not 16.
Most importantly though here is your intArray directive. You have used .word 4000 I'm guessing to allocate an array of ints, but rather you have allocated space for 1 word with the value 4000.
To instead allocate an array of 1000 ints you could use .space 4000 or equally .word 0:1000.
When I made these changes your program began to function as desired.

How do I access each word in Assembly?

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)

Copying elements from one array to another in MIPS assembly

I'm new at MIPS and have been trying to copy elements from one array to another. I'm unsure about how to go about this. It doesn't really matter what size the array is but lets just say for the sake of doing it that its size 10. I am little weak with MIPS loops and am kind of confused on how to proceed.
add $s0, $zero, $zero
add $t0, $zero, $zero
lui $s0, 0x1001
ori $s0,$s0,0
lui $t0, 0x1001
ori $t0, $t0, 0x0040
There my initialization with $s0 being the address first element in the first array and $t0 being the address of the first element in the 2nd one.
I do not believe the code you have provided is correct, but assuming it is, you would do something like this:
xor $t1, $t1, $t1 ; Zero out $t1
lw $t2, array_length ; Load the length of the array in $t2
loop_start:
lb $t3, $s0 ; Load the next byte from $s0 into $t3
sb $t3, $t0 ; Store the by in $t3 into $t0
addi $s0, $s0, 1 ; Move to the next byte in the source
addi $t0, $t0, 1 ; Move to the next byte in the destination
addi $t1, $t1, 1 ; increment the counter
blt $t1, $t2, loop_start ; Jump to the start of the loop of there are more bytes
Disclaimer: I have not programmed in MIPS since college so this code may not be 100% accurate, but I believe it will give you a place to start.

Resources