Related
I'm new to MIPS assembly and I'm trying to learn how to use arrays. I understand that arrays are declared in the .data segment and then you load the address of the array to the register. However, I'm confused on accessing elements of the the array and changing the values. Here is a simple program I wrote in C that I'm trying to convert into assembly. Any explanation/code is greatly appreciated!
void addArray (int arrA[], int arrB[], int i, int j) {
arrB[j] = arrA[i] + arrA[i + 1];
printf("%d", arrB[j]);
}
int main(void) {
int arrA [] = {1,2,3,4,5,6};
int arrB [] = {1,1,1,1,1,1};
int i = 2;
int j = 3;
addArray(arrA, arrB, i, j);
return 0;
}
Try this code
.data
arrA: .word 1,2,3,4,5,6
arrB: .word 1,1,1,1,1,1
.text
.globl main
main:
la $a0,arrA #load address of arrA into $a0 register (first parameter)
la $a1,arrB #load address of arrB into $a1 register (second parameter)
li $a2,4 # load $a2 register with 3 (threeth parameter)
li $a3,3 # load $a3 register with 3 (fourth parameter)
jal addArray # call addArray function
exit: # syscall to exit the programm
li $v0, 10
syscall
addArray:
addi $sp,$sp,-4 #allocate space in stack
sw $ra,0($sp)
move $t0,$a2 #copy a content of $a2 register into $t0
move $t1,$a3 #copy a content of $a3 register into $t1
mulu $t0,$t0,4 # multiplication by 4 with the value of $t0, because each number have 4 bytes as offset
addu $a0,$a0,$t0 #
lw $t3,($a0) # arrA[i]
addi $a0,$a0,4 # i+1
lw $t4,($a0) # arrA[i+1]
add $t5,$t3,$t4 # arrA[i] + arrA[i + 1]
mul $t1,$t1,4 # j
addu $a1,$a1,$t1
sw $t5,($a1) # arrB[j] = arrA[i] + arrA[i + 1];
print_out:
lw $s0,($a1)
li $v0, 1 #print the input number
move $a0, $s0
syscall
lw $ra,0($sp) # free stack and return to main
addi $sp,$sp,4
jr $ra
I have a problem in converting my C code to MIPS code. The original C code is below:
int A[4];
int i;
int diff;
for(i=0; i<3; i++){
diff = A[i+1] - A[i];
if (diff > 0)
A[i] = 5*A[i];
else
A[i+1] = -5*A[i];
}
And here is my MIPS Code below:
### MIPS PROJECT PART 1: ARRAY USING FOR LOOPS
.data
# allocate 16 bytes memory for 4 integer array
A: .space 16
.text
# Store array values in registers
addi $s0,$zero,2
addi $s1,$zero,4
addi $s2,$zero,6
addi $s3,$zero,8
# Index = $t0
addi $t0,$zero,0
# Store the first index and then store others by increasing $t0 by 4 bytes
sw $s0,A($t0)
addi $t0,$t0,4
sw $s1,A($t0)
addi $t0,$t0,4
sw $s2,A($t0)
addi $t0,$t0,4
sw $s3,A($t0)
main:
li $s4,0 # counter i = $s4
li $t2,3 # constant $t2
loop:
beq $s4, $t2, end # if t1 == 3 we are done
# diff = A[i+1] - A[i]
lw $t3,A($zero) # $t3 = A[i]
addi $t6, $zero,4 # next element A[i+1]
lw $t4,A($t6) # $t4 = A[i+1]
sub $t5,$t4,$t3 # dif = $t5 = A[i+1] - A[i]
# if (diff > 0)
bltz $t5, Else
# A[i] = 5*A[i]
add $t5,$zero,$t5
add $t5,$zero,$t5
add $t5,$zero,$t5
add $t5,$zero,$t5
sw $t5 A($zero)
Else:
# else A[i+1] = -5 * A[i];
addi $zero, $zero,4 #next element A[i+1]
#Loop body
addi $s4, $s4, 1 # add 1 to $s4
j loop # jump back to the top
end:
li $v0,10
syscall
The thing is that program does not update array values. Additionally I am restricted to use multiplication commands (mult or mull), that is why I am trying just to add it 5 times in control statement.
Your program has some bugs and is incomplete. I've created two versions. One with annotations for the bugs and a refactored one. Please pardon the gratuitous style cleanup.
Here's your asm program with annotations:
### MIPS PROJECT PART 1: ARRAY USING FOR LOOPS
.data
A: .space 16
# allocate 16 bytes memory for 4 integer array
.text
# Store array values in registers
addi $s0,$zero,2
addi $s1,$zero,4
addi $s2,$zero,6
addi $s3,$zero,8
# Index = $t0
addi $t0,$zero,0
# Store the first index and then store others by increasing $t0 by 4 bytes
sw $s0,A($t0)
addi $t0,$t0,4
sw $s1,A($t0)
addi $t0,$t0,4
sw $s2,A($t0)
addi $t0,$t0,4
sw $s3,A($t0)
# NOTE/BUG: this should go after the .text as the above code may be skipped
main:
li $s4,0 # counter i = $s4
li $t2,3 # constant $t2
loop:
beq $s4,$t2,end # if t1 == 3 we are done
# diff = A[i+1] - A[i]
# NOTE/BUG: this always loads from A[0]
lw $t3,A($zero) # $t3 = A[i]
# NOTE/BUG: this always loads from A[1]
addi $t6,$zero,4 # next element A[i+1]
lw $t4,A($t6) # $t4 = A[i+1]
sub $t5,$t4,$t3 # dif = $t5 = A[i+1] - A[i]
# if (diff > 0)
bltz $t5,Else
# NOTE/BUG: this is _not_ A[i] but is diff
# NOTE/BUG: this is _not_ 5* but higher (see below)
# A[i] = 5*A[i]
add $t5,$zero,$t5 # diff*2
add $t5,$zero,$t5 # diff*4
add $t5,$zero,$t5 # diff*8
add $t5,$zero,$t5 # diff*16
# NOTE/BUG: this always stores to A[0]
sw $t5,A($zero)
# NOTE/BUG: this falls through to the else case
Else:
# else A[i+1] = -5 * A[i];
# NOTE/BUG: this is just a nop because a $zero as destination reg does
# nothing
addi $zero,$zero,4 # next element A[i+1]
# Loop body
addi $s4,$s4,1 # add 1 to $s4
# NOTE/BUG: this stores nothing
j loop # jump back to the top
end:
li $v0,10
syscall
I rewrote the C code slightly to make the asm easier:
int A[4] = { 2, 4, 6, 8 };
void
calc(int *arr,int cnt)
{
int tmp;
int diff;
int *endp;
endp = &arr[cnt];
for (; arr < endp; arr += 1) {
tmp = arr[0];
diff = arr[1] - tmp;
if (diff > 0) {
tmp = 5 * tmp;
arr[0] = tmp;
}
else {
tmp = -5 * tmp;
arr[1] = tmp;
}
}
}
Here's the refactored asm code:
### MIPS PROJECT PART 1: ARRAY USING FOR LOOPS
.data
# allocate 16 bytes memory for 4 integer array
A: .word 2,4,6,8
space: .asciiz " "
nl: .asciiz "\n"
.text
main:
la $a0,A
li $a1,4
jal print
la $a0,A
li $a1,3
jal calc
la $a0,A
li $a1,4
jal print
li $v0,10
syscall
# calc -- calculate results
#
# arguments:
# a0 -- pointer to array
# a1 -- count of array
calc:
# get endp
sll $a1,$a1,2 # convert int count to byte count
addu $a1,$a1,$a0 # add in array base
j calc_start # start loop
calc_loop:
lw $t3,0($a0) # tmp = A[i]
lw $t4,4($a0) # A[i + 1]
sub $t5,$t4,$t3 # get diff
bltz $t5,calc_neg # < 0
add $t5,$t3,$t3 # tmp = A[i]*2
add $t5,$t5,$t5 # tmp = A[i]*4
add $t5,$t5,$t3 # tmp = A[i]*5
sw $t5,0($a0)
b calc_next
calc_neg:
neg $t3,$t3
add $t5,$t3,$t3 # tmp = A[i]*2
add $t5,$t5,$t5 # tmp = A[i]*4
add $t5,$t5,$t3 # tmp = A[i]*5
sw $t5,4($a0)
calc_next:
addiu $a0,$a0,4
calc_start:
bne $a0,$a1,calc_loop # more to do? if yes, fly
jr $ra # return
# print -- print results
#
# arguments:
# a0 -- pointer to array
# a1 -- count of array
#
# temporaries:
# a2 -- array pointer
# a3 -- array count
print:
move $a2,$a0
move $a3,$a1
j print_next
print_loop:
li $v0,4
la $a0,space
syscall
li $v0,1
lw $a0,0($a2) # get current value
syscall
addiu $a2,$a2,4
addi $a3,$a3,-1
print_next:
bnez $a3,print_loop
li $v0,4
la $a0,nl
syscall
jr $ra # return
I was trying to do an assignment translating from C to MIPS but I get a instruction reference error in jal main. Here is what I have to translate:
void swap (int a, int b)
{
int temp=a;
a=b;
b=temp;
}
int distance (int a, int b)
{
if (b > a)
swap (a,b);
return (a-b)
}
And here is what I wrote:
.data #declare the variables
var1: .word 4, 7, 12, 5
var2: .word 15, 3, 6, 14
result: .space 4
.text
main:
la $t0, var1 #load address 'var1' into $t0
la $t1, var2 #load address 'var2' into $t1
la $t2, result #load address 'result' into $t2
li $t3, 0 # load imm (i=0)
for_loop:
bgt $t3, 4, for_done #when i>4 do not meet condition, exit
lw $t4, 0($t4) #result[i] = tmp
jal distance
addi $t3, $t3, 1 #i++
j for_loop
for_done:
la $t2, distance
ori $v0, $0, 4
syscall
distance:
blt $t0, $t1, exit
jal swap
sub $t5, $t0, $t1
jr $t5
swap:
lw $t6, 0($t0)
lw $t7, 0($t1)
sw $t6, 0($t1)
sw $t7, 0($t0)
exit:
I actually don't know what am I doing, just the basics of Assembly. I hope some of you could help me. :)
I'm sorry but your asm code had [at least] 15 bugs.
I've created two versions: one with the bugs annotated and a second with the bugs fixed and a working program
Note that due to the vagueness of the C code, I had to guess the true intent of the program and take [considerable] poetic license.
Here's the unchanged version with the bugs annotated [please pardon the gratuitous style cleanup]:
.data # declare the variables
var1: .word 4, 7, 12, 5
var2: .word 15, 3, 6, 14
# NOTE/BUG: this only reserves 4 bytes instead of the 16 we need to hold all
# four values
result: .space 4
.text
main:
la $t0,var1 # load address 'var1' into $t0
la $t1,var2 # load address 'var2' into $t1
la $t2,result # load address 'result' into $t2
li $t3,0 # load imm (i=0)
for_loop:
# NOTE/BUG: this goes one too far (i.e. we want i>=4)
bgt $t3,4,for_done # when i>4 do not meet condition, exit
# NOTE/BUG: $t4 is never intialized to anything, so this instruction will fault
# (e.g. equivalent to dererencing a null pointer in C)
lw $t4,0($t4) # result[i] = tmp
jal distance
# NOTE/BUG: the index variable 'i' is incremented, but distance does _not_ use
# (i.e.) distance will always use var1[0] and var2[0] on each iteration
addi $t3,$t3,1 # i++
j for_loop
# NOTE/BUG: what do we want to do here? -- print the result vector presumably
# NOTE/BUG: syscall 4 is to print a string -- it would require setting up $a0
# and _not_ $t2 but, even then, using 'distance' is wrong as distance is the
# function name and _not_ a string so we'd get garbage
# NOTE/BUG: we probably wouldn't even get that far because QtSpim would
# probably fault because distance is in the .text segment and not the .data
# segment
for_done:
la $t2,distance
ori $v0,$0,4
syscall
distance:
# NOTE/BUG: this is comparing _addresses_ instead of _values_ (i.e.) this
# compares (&var1[i] > &var2[i]) instead of var1[i] > var2[i])
# NOTE/BUG: this test is _reversed_, because this guarantees negative numbers
blt $t0,$t1,exit
# NOTE/BUG: jal is calling swap as a function, but swap is merely a label here
jal swap
# NOTE/BUG: based on the mips ABI, return values go into $v0
sub $t5,$t0,$t1
# NOTE/BUG: when 'jal distance' is called, the return address goes into $ra
# and to return to the place in main that called us, we want to do 'jr $ra'
# NOTE/BUG: this 'jr' should be at exit:
jr $t5
# NOTE/BUG: this actually swaps var1[i] and var2[i] -- would this be correct to
# modify the original arrays???
swap:
lw $t6,0($t0)
lw $t7,0($t1)
sw $t6,0($t1)
sw $t7,0($t0)
# NOTE/BUG: this is where the 'jr' should go
exit:
Here's the cleaned up and working version. I decided that it should store the distance in the result vector and then show all three vectors:
.data
# NOTE: lw/sw must be four byte aligned so keep these first
var1: .word 4, 7, 12, 5
var2: .word 15, 3, 6, 14
result: .space 16
msg_var1: .asciiz "var1:"
msg_var2: .asciiz "var2:"
msg_result: .asciiz "dist:"
msg_space: .asciiz " "
msg_nl: .asciiz "\n"
.text
main:
la $s0,var1 # load address of 'var1'
la $s1,var2 # load address of 'var2'
la $s2,result # load address of 'result'
li $s3,4 # number of elements in a given vector
li $s4,0 # load imm (i=0)
for_loop:
bge $s4,$s3,for_done # i <= count? if no, fly
jal distance
addi $s4,$s4,1 # i++
j for_loop
for_done:
la $a0,msg_var1
la $a1,var1
jal show
la $a0,msg_var2
la $a1,var2
jal show
la $a0,msg_result
la $a1,result
jal show
# exit program
li $v0,10
syscall
# distance -- calculate distance between two numbers in two vectors
#
# RETURNS:
# stores into 'result' vector
#
# global registers:
# s0 -- pointer to var1
# s1 -- pointer to var2
# s2 -- pointer to result
# s4 -- array index
#
# registers:
# t0 -- address and value of var1[i]
# t1 -- address and value of var2[i]
# t2 -- temp value
# t7 -- byte offset corresponding to index 'i'
distance:
sll $t7,$s4,2 # convert index to byte offset
addu $t0,$s0,$t7 # get &var1[i]
lw $t0,0($t0) # fetch var1[i]
addu $t1,$s1,$t7 # get &var2[i]
lw $t1,0($t1) # fetch var2[i]
bge $t0,$t1,distance_done # swap a/b to get abs val? if no, fly
# swap a/b
move $t2,$t0 # temp = a
move $t0,$t1 # a = b
move $t1,$t2 # b = temp
distance_done:
sub $v0,$t0,$t1 # get distance (i.e.) abs(a-b)
addu $t2,$s2,$t7 # get &result[i]
sw $v0,0($t2) # result[i] = distance
jr $ra # return
# show -- show vector
#
# arguments:
# a0 -- vector name
# a1 -- pointer to vector
#
# registers:
# t3 -- array remaining count
#
# clobbers:
# v0
show:
li $v0,4 # syscall to print string
syscall
move $t3,$s3 # get number of elements in vector
show_loop:
blez $t3,show_done # more to do? if no, fly
li $v0,4
la $a0,msg_space # output a space
syscall
# output vector[i]
li $v0,1 # syscall to output value
lw $a0,0($a1) # get vector value
syscall
addiu $a1,$a1,4 # advance pointer to next array element
addi $t3,$t3,-1 # bump down count
j show_loop
show_done:
# output newline
la $v0,4
la $a0,msg_nl
syscall
jr $ra # return
I am trying to call a function declared in a C file from an assembly file, but I am getting a "Illegal Instruction" error.
My C code:
#include <stdio.h>
int BubbleSort(int *v){
return 13;
}
int Run(int *, int *);
int main(){
int vetor[] = {1, 3, 5, 4, 10}, numTrocas = 0, res = 0;
numTrocas = Run(vetor, &res);
printf("numTrocas= %d\nf= %d\n", numTrocas, res);
return 0;
}
My Assembly code:
.data
.text
.globl Run
Run:
addi $sp,$sp,-8
sw $ra,0($sp)
sw $a0,4($sp)
move $t4, $a1 #$t4 = $a1; address of variable res in C file
move $t6, $a0 #$t6 = address of the first vector element
lw $a0, ($t6)
add $t6, $t6, 4
lw $a1, ($t6)
add $t6, $t6, 4
lw $a2, ($t6)
add $t6, $t6, 4
lw $a3, ($t6)
add $t6, $t6, 4
lw $t3, ($t6)
jal PolyCalc
lw $a0,4($sp)
jal BubbleSort #-> Illegal Instruction
lw $ra, 0($sp)
addi $sp, $sp, 8
jr $ra
PolyCalc: #This function calculates the result of the expression 5(V[0] + V[1])(V[2] − 3xV[3]xV[4])
li $s0,5 #$s0 = 5
li $s1,3 #$s1 = 3
add $t1,$a0,$a1 #t1=(x1+x2)
mul $t1,$t1,$s0 #t1=5*(x1+x2)
mul $t2,$a3,$t3 #$t2 = x4*x5
mul $t2,$t2,$s1 #$t2 = 3*x4*x5
sub $t2,$a2,$t2 #$t2 = (x3-3x4*x5)
mul $t1,$t1,$t2
sw $t1, ($t4) #Save the result on the address of $t4
jr $ra
When I comment the line jal BubbleSort and add a random value to $v0 as a return of the BubbleSort function I stop getting that error and the program works fine.
Can someone find the error in my code?
Thanks
For good measure, in your asm file, you should probably add:
.extern BubbleSort
Then, the jal BubbleSort should get relocated correctly.
But, the linker may place it too far away for the [limited] range of jal, so you may have to replace it with:
la $t0,BubbleSort
jalr $t0
The error you are experiencing is because the assembly code is not able to find the label called BubbleSort to jump. I think you should be using a different approach. You can embed assembly code in C using the asm function.
Something like:
#include <stdio.h>
int BubbleSort(int *v){
asm("lw $a0, ($t6)"
"lw $a1, ($t6)");
}
int Run(int *, int *);
int main(){
int vetor[] = {1, 3, 5, 4, 10}, numTrocas = 0, res = 0;
numTrocas = Run(vetor, &res);
printf("numTrocas= %d\nf= %d\n", numTrocas, res);
return 0;
}
You can find more information about this function in the GCC documentation: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
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.