How to get the address of a constant in MIPS? - mips

I am trying to implement a for loop in mips like this:
# {
# int sum = 0;
# for(int x = 0; x < n; x++)
# if ( v[x] > 2 )
# sum += v[x];
# return sum;
# }
I have the loop correctly but I can't get the size of the loop right. The loops functions as intended but it runs one extra time. It should be printing a 31 but it ends up running the loop an extra time and getting a 10 from somewhere then getting a 41 as the end result.
Here is my code:
.eqv SIZE 8
values: .half 6, 5, 1, 9, -2, 3, 8, 2
endl: .asciiz "\n"
endv:
# -------------------------------------------------# text/code section
.text
.globl main
main:
# ------
#TODO: call doSum(values, SIZE)
la $s0, values #&v[0]
la $s7, endv
li $a0, 0 #sum = 0
loop:
lh $t1, ($s0) #v[X]
li $t2, 2 #$t2 = 2
bgt $t1, $t2, sumPlus
j increment #skip over sumPlus if not > 2
sumPlus:
add $a0, $a0, $t1 #sum += v[x]
increment:
add $s0, $s0, 2 #x++
blt $s0, $s7, loop

You have a line feed character and a null terminator (endl: .asciiz "\n") before endv, so you're including them in the array that you're summing from. I don't know much about MIPS, but because \n has the value 10 (dec) and the \0 has the value 0. I assume that they're bytes and this is being run on a machine that is little-endian, so as a signed halfword they're interpreted as 10 (dec).
I think that just moving the endv label to the actual end of the array (just before endl:) will solve this.

Related

MIPS - how to write a for loop while also accessing and saving arrays

I won't lie, I'm a total beginner at programming in general with my first time being last year when I started programming in Computer Programming I. A lot of this is still over my head so uh... go easy on me will ya?
The question poised is as follows:
"1. (5 pts) Translate the following C code to MIPS. Assume that the variables i and j are assigned to registers $s0 and $s1, respectively. Assume that the base address of the arrays A and B are in registers $s6 and $s7, respectively. Assume that the elements of the arrays A and B are 4-byte words:
for (i = 0; i< j; i++)
B[i] = A[i+1] - A[i];
For my program I wrote:
.data
arrayA: .word 5,8, 12, 13, 28
sizeA: .word 5
arrayB: .space 4
i: .word 0
_j: .word 5
.text
# for (i = 0; i< j; i++)
# B[i] = A[i+1] - A[i];
main:
la $s6, arrayA #loads the base address of arrayA into register s6
la $s7, arrayB #loads the base address of arrayB into register s7
lw $s0, i #Loads zero into register s0 for i
lw $s1, _j #loads the value of 4 into register s2 for j
Loop:
beq $s0, $s1, Exit #Does the operation i < j in the for-loop
addi $s0, $s0, 1 #Does the operation i++ in the for-loop
mul $t0, $s0, 4 #Get address of i
add $t1, $t0, $s6 #Finds and stores the base address of a[i]
add $t2, $t0, $s7 #Finds and stores the base address of b[i]
lw $t3, ($s6) #get the value of a[i]
lw $t4, 4($10) #get the value of a[i + 1]
sub $t5, $t4, $t3 #subtracts a[i + 1] and a[i]
sw $t5, ($t2) #Stores the above into b[i]
add $a0, $zero, $t5
li $v0, 1
syscall
J Loop #loops back to beginning
Exit:
li $v0 10
syscall #syscall to exit the program
When I run it, it spits out 33333, which means it isn't incrementing. How do I fix this?
arrayA: .word 5,8, 12, 13, 28
may be wrong. You didn't add a space between 5, and 8 while giving spaces between the other elements. My QtSpim Version 9.1.21 didn't accept that.
arrayB: .space 4
is wrong. You are allocating only 4 bytes while space for 4 words (16 bytes) is required.
_j: .word 5
is wrong. The stored value is 5 despite of the comment saying "loads the value of 4 into register s2 for j". Using value 5 is wrong also because the array a has only 5 elements.
addi $s0, $s0, 1 #Does the operation i++ in the for-loop
is in the wrong place. The increment should be after the loop body.
lw $t3, ($s6) #get the value of a[i]
lw $t4, 4($10) #get the value of a[i + 1]
is wrong. The address of a[i] is stored in $t1, so you should use that.
J Loop #loops back to beginning
may be wrong. You used an uppercase letter for instruction name only here. At least my QtSpim Version 9.1.21 didn't accept that.
li $v0 10
may be wrong. You didn't use a comma for separating the operands while you used that in previous use of li: li $v0, 1. I'm surprised by seeing my QtSpim Version 9.1.21 accepting this.
Fixed code:
.data
arrayA: .word 5, 8, 12, 13, 28
sizeA: .word 5
arrayB: .space 4 * 4
i: .word 0
_j: .word 4
.text
# for (i = 0; i< j; i++)
# B[i] = A[i+1] - A[i];
main:
la $s6, arrayA #loads the base address of arrayA into register s6
la $s7, arrayB #loads the base address of arrayB into register s7
lw $s0, i #Loads zero into register s0 for i
lw $s1, _j #loads the value of 4 into register s2 for j
Loop:
beq $s0, $s1, Exit #Does the operation i < j in the for-loop
mul $t0, $s0, 4 #Get address of i
add $t1, $t0, $s6 #Finds and stores the base address of a[i]
add $t2, $t0, $s7 #Finds and stores the base address of b[i]
lw $t3, ($t1) #get the value of a[i]
lw $t4, 4($t1) #get the value of a[i + 1]
sub $t5, $t4, $t3 #subtracts a[i + 1] and a[i]
sw $t5, ($t2) #Stores the above into b[i]
add $a0, $zero, $t5
li $v0, 1
syscall
addi $s0, $s0, 1 #Does the operation i++ in the for-loop
j Loop #loops back to beginning
Exit:
li $v0, 10
syscall #syscall to exit the program
Don't mix friendly register names with the raw register names — very confusing.
lw $t3, ($s6) #get the value of a[i]
lw $t4, 4($10) #get the value of a[i + 1]
Here you're mixing $t3, $t4, $s6, and $10, which is actually $t2.
Your assembly code is doing the following C code (modulo the above bug):
int *s6 = A;
int *s7 = B;
for ( int i = 0; i != j; ) {
i++;
int *t1 = s6 + i; // i is automatically scaled by C
int *t2 = s7 + i; // ditto
int t3 = *s6; // A[0]
int t4 = s6[1]; // A[1]
int t5 = t4 - t3;
*t2 = t5; // B[i]=...
}
Can you see why it keeps loading A[0] & A[1]?
You should have noticed these problems during single stepping debugging.  After each step, check that every effect you're expecting happens as you expect.  When it doesn't, look for typos or logic problems.

When writing in MIPS, I am unsure whether to use li or addi. I am still unclear what the difference is.

For example, I have a piece of C code that I am trying to convert to MIPS for practice, but for the variable count, I don't know whether to use addi $t0,0 or li $t0, 0. Could I use either either or? And what is the difference?
Void haarPredict (int vector[], int N)
{
int half = N >> 1;
int count = 0;
for(int i = 0; i < half; i++)
{
int predictVal = vector[i];
int j = i + half;
vector[j] = vector[j] - predictVal
}
}
This is what I have so far after converting the above code to MIPS. Assuming $a0 is vector[] and $a1 is N. Again, I am not sure if li or addi is the correct thing to use.
srl $t0, $a1, 1 #t0 holds half. half = N >> 1
addi $t1, $t1, 0 #t1 holds count. count = 0
addi $t2, $t2, 0 #t2 holds i. i = 0
loop: slt $t3, $t2, $t0 #t3 holds 1 if i < half
beg $t3, $zero, exit #exit if t3 == 0
lw $t4, 0($a0) #t4 holds predictValue
addi $a0, $a0, 4 #4 bytes for next word address
addi $t5, $t2, $t0 #t5 holds j. j = i + half
lw $t6, $t6, $t4 #vector[j]=vector[j]-predivtVal
addi $t2, $t2, 1 #i++
j loop
exit: jr $ra
The li (Load immediate) instruction loads a specific numeric value into a register.
The addi (Add inmediate) adds a register and a sign-extended immediate value and stores the result in a register.
So, unless you are 100% sure a register has a zero value, then you shouldn't use an addi instruction to set a register.
For example:
addi $t1, $t1, 0 #t1 holds count. count = 0
You don't know if $t1 is zero at that particular moment. If thats a subroutine, you might be using a garbage value of $t1 (a value from before the invocation of the subroutine, before the jump to the address of the subroutine).
So the safe way is to set the register with li (thus, count=0), not taking into consideration the previous value of the register.

Understanding a MIPS algorithm

I'm very new into coding and we started with MIPS. We were kind of "thrown in to the cold water" and had to implement an algorithm that checks whether the elements of an array are sorted in ascending order. If it is sorted, a 1 is to be stored in $v0, otherwise a 0.
The solution that was given us:
.data
A: .word 1, 2, 3, 4, 5
l: .word 5
.text
main:
la $s0, A #address of A
lw $s1, l
add $t0, $0, $0 #counter for loop
addi $v0, $0, 1
sub $s1, $s1, $v0
for:
beq $t0, $s1, done
sll $t1, $t0, 2 #byte offset
add $t1, $t1, $s0
lw $t1, 0($t1) #$t1 = A[j]
addi $t2, $t0, 1
sll $t2, $t2, 2
add $t2, $t2, $s0
lw $t2, 0($t2) #$t2 = A[j + 1]
sub $t3, $t2, $t1 #$s3 = A[j+1] - A[j]?
slt $t4, $t3, $0
addi $t3, $0, 1
beq $t3, $t4 unsort #A isn’t sorted if A[j+1] - A[j] has a negative value
addi $t0, $t0, 1 #$t0 = $t0 + 1
j for
unsort:
add $v0, $0, $0 #set $v0 if array isn’t sorted
done:
I'm having trouble understanding this code/algorithm. First of all what is an Array and why do we need specifically 5 of them?
But much more important to me is understanding this code/algorithm. So I need someone who is kind enough and explains to me step by step and in simple word :D, how this code works.
Would be very helpful and thanks in advance.
Short answer
Basically, this program iterates over the array A in a loop with loop counter, say i. The index i is initialized with 0 and in every loop iteration incremented by 1 until it reaches l-1, where l is the size/length of the array A. In every iteration the algorithm checks if A[i] < A[i + 1] (i.e. if the i-th and the (i+1)-th element in A are in ascending order and thus are sorted). If so, it continues execution and $v0 remains 1. Otherwise it sets $v0 to 0 and terminates.
Long answer
Arrays and memory
An array is basically an ordered list of data – in this case it is an ordered list of words (a word in MIPS means: 32-bit value, and it consists of 4 bytes, each of which has 8 bits of information). So in this case, every word represents a 32-bit integer.
If we have an array A of length l, the elements are indexed from 0 to l-1. The first element in an array A (notation: A[0]) is saved at a certain address in memory, let's call it addr. The i-th element in A (A[i]) is then saved at the memory address addr + 4*i. This is because memory in MIPS is byte-addressable, i.e., every byte has his own address and because a word consists of 4 bytes, word addresses are offset by 4 (see below).
.data section
A: .word 1, 2, 3, 4, 5
l: .word 5
With that you will realize that you don't have 5 arrays but only one (called A) and it contains the values 1, 2, 3, 4 and 5. Thus, the length is 5 and it is specified in the word l. You could add more values to your array but then you would have to adjust the length because otherwise, strange things will happen (or at least the result will be random). Your data is specified in this .data section and is therefore somewhere stored in your program's memory space.
MIPS assembly
In order to understand the code in the .text section, you have to understand MIPS assembly. If I write about a register, just think about it as a placeholder for a 32-bit value. For example, $0 is the zero register and it always stores a 32-bit 0. Other registers are used to temporarily store values used in your program. The instructions you use are:
la rd, label ("load address")
(rd = address of label; stores the address of the word specified by "label" in the destination register rd)
lw rd, label ("load word")
or
lw rd, offset(rs)
(load data specified by label, or data at address rs+offset into destination register rd)
add rd, rs, rt
(rd = rs + rt; adds source registers rs and rt and stores result in destination register rd)
addi rd, rs, imm ("add immediate")
(rd = rs + imm; adds source register rs and immediate (16-bit constant) value and stores result in destination register rd)
sub rd, rs, rt ("subtract")
(rd = rs - rt; subtracts source register rt from source register rs and stores result in destination register rd)
beq rs, rt, label ("branch if equal")
(if value in rs is equal to value in rt, jump to label)
sll rd, rs, shamt ("shift logic left")
(rd = rs << 2; shift value in rs by two bits the left and store it in rd)
slt rd, rs, rt ("set less than")
(rd = (rs < rt) ? 1 : 0; if the value in rs is smaller than the value in rt, set rd to 32-bit 1, otherwise to 32-bit 0)
j label ("jump")
(jump to label)
.text section
main:
la $s0, A
lw $s1, l
add $t0, $0, $0
addi $v0, $0, 1
sub $s1, $s1, $v0
for:
beq $t0, $s1, done
sll $t1, $t0, 2 #byte offset
add $t1, $t1, $s0
lw $t1, 0($t1) #$t1 = A[j]
addi $t2, $t0, 1
sll $t2, $t2, 2
add $t2, $t2, $s0
lw $t2, 0($t2) #$t2 = A[j + 1]
sub $t3, $t2, $t1 #$s3 = A[j+1] - A[j]?
slt $t4, $t3, $0
addi $t3, $0, 1
beq $t3, $t4 unsort #A isn’t sorted if A[j+1] - A[j] has a negative value
addi $t0, $t0, 1 #$t0 = $t0 + 1
j for
unsort:
add $v0, $0, $0 #set $v0 if array isn’t sorted
done:
This assembly code is equivalent to the pseudocode (if you are not used to pseudocode or to while loops, please look it up in the internet):
s0 <- address of first element in A
s1 <- l
t0 <- 0
v0 <- 1
s1 <- s1-1
while (t0 != s1) do
t1 <- 4 * t0
t1 <- t1 + s0
t1 <- word at address t1
t2 <- t0 + 1
t2 <- 4 * t2
t2 <- t2 + s0
t2 <- word at address t2
t3 <- t2 - t1
if (t3 < 0) then t4 <- 1
else t4 <- 0
t3 <- 1
if (t3 == t4) then
v0 <- 0
return
else t0 <- t0 + 1
Hope, this helped. I think it is quite ambitious to start with assembly code if you haven't coded before. Good luck!

how do I implement a jump address table in MIPS64? [duplicate]

C++ Program
# include < iostream >
# include <string >
using namespace std;
int main ()
{
int resistance ; // in Ohms
string partNum ; // Part Number
cout << " Enter resistance : " << endl ;
cin >> resistance ;
switch ( resistance )
{
case 3 : partNum = " OD30GJE "; break ;
case 10 : partNum = " OD100JE "; break ;
case 22 : partNum = " OD220JE "; break ;
default : partNum = "No match "; break ;
}
cout << " Part number : " << partNum << endl ;
return 0;
}
Translate the C code to MIPS assembly code, add to your code the capability to match the closest resistor. Be sure to use the jr instruction for the switch statement. Have your code get the resistance as input from the user and display to the console the resistor's corresponding or closest part number.
Mips Assembly Code
.data
int_value: .space 20
.align 2
input: .asciiz "Enter resistance.\n" # declaration for string variable,
string1: .asciiz "OD30GJE\n" # declaration for string variable,
string2: .asciiz "OD100JE\n"
string3: .asciiz "OD220JE\n"
string11: .asciiz "No Match\n"
string12: .asciiz "Enter resistance\n"
.text
main:
li $v0, 4
la $a0, input # print for input
syscall
la $t0, int_value
li $v0, 5 # load appropriate system call code into register $v0;
syscall # call operating system to perform operation
sw $v0, int_value # value read from keyboard returned in register $v0;
# store this in desired location
lw $s1, 0($t0)
condition1:
slt $t1, $s1, $zero # if $s1 < 0 $t1 = 1 else $t1 = 0
beq $t1, $zero, condition2 # if $t1 = 0; InvalidEntry
bne $t1, $zero, invalid_entry
condition2:
sgt $t1, $s1, -1 # if $s1 > -1 then $t1 = 1 else $t1 = 0
beq $t1, $zero, invalid_entry # if $t1 = 0; InvalidEntry
sgt $t1, $s1, 9 # if s1 > 9 t1 = 1 else $t1 = 0
bne $t1, $zero, condition3 # if $t1 does not equal = 0; condition3
li $v0, 4
la $a0, string1
syscall
j exit
condition3:
sgt $t1, $s1, 9 # if $s1 > 9 then $t1 = 1 else $t1 = 0
beq $t1, $zero, invalid_entry # if $t1 = 0; InvalidEntry
sgt $t1, $s1, 21 # if s1 > 21 t1 = 1 else $t1 = 0
bne $t1, $zero, condition3 # if $t1 does not equal = 0; condition3
li $v0, 4
la $a0, string2
syscall
j exit
invalid_entry:
li $v0, 4
la $a0, string11
syscall
j exit
exit:
li $v0, 10 # v0<- (exit)
syscall
The following shows how to make a jump table. The offset within the jump table is assumed to be in $s1. $s1 must be a multiple of 4 (i.e. a byte offset).
This code has not been tested!
. . . .
b 1f # jump past the jump table
nop # branch delay slot
# table of jump addresses
JTAB: .word LABEL1
.word LABEL2
.word LABEL3
1: la $t0, JTAB # load start address of the jump table
add $t0, $t0, $s1 # add offset to table address
lw $t1, 0($t0) # load the address stored at JTAB + $s1
jr $t1 # and jump to that address
nop # branch delay slot
LABEL1: # do something
b 2f # break
nop
LABEL2: # do something else
b 2f # break
nop
LABEL3: # do a different thing
b 2f # break
nop
2: # after the end of the "case statement"

MIPS Palindrome Check

I am trying to write a MIPS program that checks if an input string is a palindrome. I tested the string "HelllleH" and when stepping through the program, I saw that during the first loop of PAL_CHECK t0 = 0 but t1 = 104. Logically, t0 = 0 and t1 = 0 also in the first loop. Can someone tell what's wrong in this program?
# a0 is input
# a1 is current character we are looking at
# a2 is length of string
# t0 is character at beginning of string
# t1 is character at end of string
# v0 stores whether string is palindrome or not (0 for false, 1 for true)
ispalindrome:
addi $a2, $0, 0 # length counter
FIND_LEN:
lbu $a1, 0($a0) # load character into $a1
beq $a1, $0, PAL_CHECK # Break if string is at the end
addi $a2, $a2, 1 # increment counter
addi $a0, $a0, 1 # increment str address
j FIND_LEN
PAL_CHECK:
# Is palindrome if length is less than 2
slti $t0, $a2, 2
bne $t0, $0, RETURN_TRUE
# Check first and last chars to see if they are the same
lbu $t0, 0($a0) # first char is t0
add $t1, $a2, $a0 # last char is t1
lbu $t1, 0($t1)
bne $t0, $t1, RETURN_FALSE # if they are not equal, return false
# continue recursion
addi $a2, $a2, -2
addi $a0, $a0, 1
j PAL_CHECK
RETURN_FALSE:
addi $v0, $0, 0
jr $ra
RETURN_TRUE:
addi $v0, $0, 1
jr $ra
While finding the length of the string you continually increment $a0 to point at the next character, until you find the NUL terminator at the end of the string. You never reset $a0 before the palindrome check loop, so it's still pointing at the NUL terminator when you begin that loop. So you'll actually be comparing data that's past your string.
It would make more sense to implement the check this way (I'm using C to illustrate the idea; I'll leave the MIPS translation to you):
a0 = the_string;
a1 = the_string + strlen(the_string) - 1;
while (a1 > a0) {
if (*a0 != *a1) return false;
a0++;
a1--;
}
return true;
By the way, a terminology nitpick: # continue recursion. Your implementation isn't recursive, it's iterative.