Related
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
I had tried my best to turn the C code to mips code.
I cant find why I cant printf all of the output like C code do.
I think problem is about $ra, but I cant fix it.
I need help.
thanks for the nice guy like you.
I need output like this
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5 *
2 3 4
2 3 5
2 4 5
3 4 5
but MIPS code will exit at *.
C
#include <stdio.h>
static int cl[3];
static int n=5;
static int k=3;
void dfs(int cur, int s ){
int i;
if (cur==k) {
for (i=0; i<k; i++){
printf("%d ",cl[i]);
}
printf("\n");
}
else{
int p=n-k+cur+1;
for (i=s; i<=p; i++){
cl[cur]=i;
dfs(cur+1,i+1);
}
}
}
int main(){
dfs(0,1);
}
ASM
.data
cl: .word 0,0,0 #cl[3]
n: .word 5 #n=5
k: .word 3 #k=3
space: .asciiz " "
enter: .asciiz "\n"
.text
main: li $s0, 0 #cur = 0
li $s1, 1 #s = 1
jal dfs
j exit
dfs: addi $sp, $sp, -12
sw $ra, 0($sp)
li $t3, 0 #i=0
lw $t0, k #k=3
if: bne $s0, $t0, else #if(cur==k)
la $t0, cl #get address of cl[i]
loop:
lw $a0, ($t0) #printf
li $v0, 1
syscall
la $a0, space # load address of spacer for syscall
li $v0, 4 # specify Print String service
syscall
addi $t3, $t3, 1 # i++
addi $t0, $t0, 4 # cl[i++]
lw $t1, k
blt $t3, $t1,loop #i<k
la $a0, enter # load address of enter for syscall
li $v0, 4 # specify Print String service
syscall
j done
else: lw $s2, n # n
lw $t0, k # k
sub $s2, $s2, $t0 #n-=k
add $s2, $s2, $s0 #+=cur
addi $s2, $s2, 1 # +=1
move $t0, $s1 #i=s
loop2: la $t1, cl #load address from cl
sll $t2, $s0, 2 #t2=cur*4
add $t1, $t1, $t2 #t1=*cl+cur*4
sw $t0, ($t1) #cl[cur]=i
addi $s0, $s0, 1 #cur++
addi $s1, $t0, 1 #s=i+1
sw $t0, 4($sp)
sw $s2, 8($sp)
jal dfs
addi $s0, $s0, -1
addi $s1, $s1, -1
addi $t0, $t0, 1
ble $t0, $s2, loop2 #i<=s2
done:
addi $sp, $sp, 12
lw $ra, 0($sp)
lw $t0, 4($sp)
lw $s2, 8($sp)
jr $ra #return void
exit:
You are incrementing the stack pointer — popping it — too early in the function epilogue. To reverse the prologue we change the sign of the immediate in adding to the stack pointer and move it to the end and otherwise change sw's to lw's.
done:
addi $sp, $sp, 12 <--- this is too early
lw $ra, 0($sp)
lw $t0, 4($sp)
lw $s2, 8($sp)
<--- it belongs here
jr $ra #return void
This is the C code i am using to make the quick sort on the MIPS, on the Mars editor i am having issues when running these code
#include <stdio.h>
void QuickSort(int *array, int first, int last){
int q;
if (first<last){
int value = array[last];
int i = first-1;
int tmp;
int j = first;
while(j<=last){
if (array[j] <= value) {
i++;
tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
j++;
}
q = i;
QuickSort(array,first,q-1);
QuickSort(array,q+1,last);
}
}
And this is my MIPS translation so far, i am getting an infinite loop
.data
numArray: 30, 15, 11, 40, 75, 80, 70, 60
first: 0
last: 7
.text
main:
la $a0, numArray
lw $a1, first
lw $a2, last
jal quick_sort
li $v0, 10
syscall
quick_sort:
subi $sp, $sp, 4 #reserving memory in the stack
sw $ra, 4($sp) #storing the return adress in the stack
xor $t0, $t0, $t0 #int q
bge $a1, $a2, label1 #if (first<last)
sll $t1, $a2, 2 #int value = array[last];
add $t1, $t1, $a0 #callculating array[last] in $t1
lw $t1, 0($t1) #array[last] = array direction + 4 * last
or $t2, $t1, $zero #$t2 will be value
subi $t3, $a1, 1 #int i = first-1;
xor $t4, $t4, $t4 #int tmp
or $t5, $a1, $zero #int j=first
j label2 #while(j<=last)
label3: sll $t6, $a2, 2 #calculating array[j] adress
add $t6, $t6, $a0
lw $t7, 0($t6) #calculating array[j] value
bgt $t7, $t1, label4 #if (array[j] <= value)
addi $t3, $t3, 1 #i++
sll $s0, $t3, 2 #calculating array[i] adress
add $s0, $s0, $a0
lw $s1, 0($s0) #calculating array[i] value
or $t4, $s1, $zero #tmp = array[i]
sw $t7, 0($s0) #array[i] = array[j];
sw $t4, 0($t6) #array[j] = tmp;
label4: addi $t5, $t5, 1 #end if; j++
label2: ble $t5, $a2, label3 #while condition
or $t0, $t3, $zero #q = i
lw $a1, first #first value on the second parameter
subi $a2, $t0, 1 #q-1
jal quick_sort #QuickSort(array,first,q-1)
addi $a1, $t0, 1 #q+1
lw $a2, last #last value on the third parameter
jal quick_sort #QuickSort(array,q+1,last);
label1: lw $ra, 4($sp) #Recovering the return address from the stack
addi $sp, $sp, 4 #releasing memory
jr $ra #going to the return address
Maybe i need to store something more on the stack or something that i am missing, thanks for your help, anything that you see strange there please let me know to check it.
EDIT:
As pointed out by Minn in a comment, I missed the two lines following:
sll $t1, $a2, 2 #int value = array[last];
So while this single line does not match the comment in it, the loading of the value seems correct.
END EDIT
I am not familiar with this specific assembly, but the problem seems to be what is known as "register clobbering" :
According to documentation jal only stores the return address, but does not store any of the registers.
lw $a1, first #first value on the second parameter
subi $a2, $t0, 1 #q-1
jal quick_sort #QuickSort(array,first,q-1)
addi $a1, $t0, 1 #q+1
lw $a2, last #last value on the third parameter
jal quick_sort
You are using $t0 as q local variable, but you never save it on the stack.
After the first call jal quick_sort #QuickSort(array,first,q-1) the value of $t0 would be different, but you use it immediately in the line addi $a1, $t0, 1 #q+1 as if it never changed, and then pass the result to the second call to QuickSort.
The C equivalent to this error would be to make q global and add q = 0; at the beginning of the function.
You must remember, that when working in assembly and using registers for local variables, you must save their state to the stack before calling any other function from your current function, otherwise you will lose state and your code will not work as expected!
To be honest, this is my first time seeing this particular assembly language, so there might be other errors I missed, but these are the most obvious ones so they were easy to spot.
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.
I'm converting the following recursive java program to MIPS asm. The algorithm computes all the possible ordering/permutations of the numbers. But the recursive call is in the for loop. I need to preserve the variable 'i' in my MIPS version but I don't know exactly where to add that. My algorithm is correct, it's just that my $t0 (which is 'i') never gets reset to 0. I just can't figure out how/where to preserve it on the stack or when to take it off the stack. Any help appreciated.
import java.util.Arrays;
public class Test
{
private static void swap(int[] v, int i, int j)
{
int t = v[i];
v[i] = v[j];
v[j] = t;
}
public void permute(int[] v, int n)
{
if (n == 1)
System.out.println(Arrays.toString(v));
else
{
for (int i = 0; i < n; i++)
{
permute(v, n-1);
if (n % 2 == 1)
swap(v, 0, n-1);
else
swap(v, i, n-1);
}
}
}
public static void main(String[] args)
{
int[] ns = {1, 2, 3, 4};
new Test().permute(ns, ns.length);
}
}
and the mips function
Note: I am permutating Strings, not integers but the algorithm is the same.
#----------------------------------------------
# anagram - Prints all the permutations of
# the given word
# a0 - the word to compute the anagrams
# s0 - n, the length of the word
# a1 - n - 1 (length-1)
#----------------------------------------------
anagram:
addi $sp, $sp, -16
sw $a0, 0($sp)
sw $a1, 4($sp)
sw $s0, 8($sp)
sw $ra, 12($sp)
add $s0, $a1, $zero # this is n
addi $a1, $s0, -1 # n-1
beq $s0, 1, printS
init: move $t0, $zero # t0 = i = 0
logic: slt $t1, $t0, $s0 # Set t1 = 1 if t0 < length
beqz $t1, endAnagram # if it's zero, it's the end of the loop
jal anagram
li $t2, 2
div $s0, $t2
mfhi $t3
beqz $t3, even # if even branch to even, otherwise it will go to odd
odd: # swap the n-1 char with the first
add $t4, $a0, $zero
add $t5, $a0, $a1
lb $t6, 0($t4) # first char
lb $t7, 0($t5) # n-1 char
sb $t7, 0($t4) # swap the two
sb $t6, 0($t5)
j inc # skip the even section
even: # swap the ith char with n-1 char
add $t4, $a0, $t0 # ith char
add $t5, $a0, $a1 # n-1 char
lb $t6, 0($t4) # ith char
lb $t7, 0($t5) # n-1 char
sb $t7, 0($t4) # swap the two
sb $t6, 0($t5)
inc: addi $t0, $t0, 1 # t0++;
j logic
endAnagram:
# reset stack pointers
lw $a0, 0($sp)
lw $a1, 4($sp)
lw $s0, 8($sp)
lw $ra, 12($sp)
addi $sp, $sp, 16 # adjust stack
jr $ra
printS: # print string and jump to return
jal printString # calls printString function which prints the string
j endAnagram
$t0 is not preserved accross subroutine calls according to convention, and you seem to follow that convention. As such, you have two choices:
you either store i in a register that is preserved, in which case
you need to preserve the register yourself in the prologue/epilogue. You already do this for $s0.
or you save $t0 yourself on the stack, around the subroutine call
In both cases, you will need additional space for your locals, so change addi $sp, $sp, -16 to addi $sp, $sp, -20 (along with the matching code in the epilogue too). If you choose option #1, use for example $s1 to store i. Add code to save and restore $s1 just like you do for $s0. If you choose option #2, add code around the jal anagram that writes $t0 to stack before the jal, and reloads it after.