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
Related
I'm implementing the following code from c to mips assembly
/*
Given an array arr terminated by entry -1, transform the array by adding
to each entry the sum of the remainder of the array after that entry.
That is, if initially arr == [a_0, a_1, ..., a_n, -1], then finally
arr == [b_0, b_1, ..., b_n, -1], where b_j = sum(a_i for i in j..n-1).
*/
uint32_t reverse_prefix_sum(uint32_t *arr) {
uint32_t r;
if(*arr == -1) return 0;
r = reverse_prefix_sum(arr+1) + (uint32_t)*arr;
*arr = r; /* may discard MSB */
return(r);
}
My current program is the following:
reverse_prefix_sum: # Let the address of array arr be in $a0
# adjust the stack pointer to save $a0 and $ra
addi $sp,$sp,-8
sw $a0,0($sp)
sw $ra,4($sp)
# Load *arr
lw $t0,0($a0)
beq $t0,-1,exit1 # r is in $v0
# *arr = *arr + 1
addu $a0,$a0,4 # recursive call
jal reverse_prefix_sum
addu $v0,$v0,$t0
# restore $a0
lw $a0,0($sp)
#restore $ra
lw $ra,4($sp)
add $sp,$sp,8
sw $v0,0($a0)
jr $ra
exit1:
# restore $a0
lw $a0,0($sp)
#restore $ra
lw $ra,4($sp)
addi $sp,$sp,8
# return 0
li $v0,0
jr $ra
With input 1 2 3 4 -1, I'm getting output -4 -3 -2.
I'm wondering where this could be improved to fix the output being incorrect.
The problem is that t0 isn’t preserved across the function call. Reload it from 0(a0) after restoring a0 after the function returns.
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
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.
I have a doubt on one exercise that my professor did during a seminar. It's basically a translation from C to MIPS. The C code is as follows:
void subr1(char *a, int v[]) {
int i;
char c[12];
for (i=11; i >= 0; i--) {
v[i] = subr2(c[i], *a);
}
}
And the translation of my professor was the following (I've written comments about what each instruction does and what I don't understand):
addiu $sp,$sp,-28 #Activation block
sw $ra, 24($sp) #Space for $ra (4 bytes)
sw $s0, 20($sp) #Space for *a (32-bit address = 4 bytes)
sw $s1, 16($sp) #Space for c (Q1: Shouldn't this be 12 bytes? c is an array of 12 chars = 12*1Byte=12Bytes)
sw $s2 12($sp) #Space for i (4 bytes)
(Q2: Why is he stopping at 12? Doesn't he need to save space for int v[]?)
move $s0,$a0 #Save 'a' in stack
move $s1,$a1 #Save 'c' in stack
li $s2, 11 #i=11
For:
blt $s2, $zero, End #if i<0, then go to 'End'
addu $t0, $s2, $sp (Q3: I'm not sure, but I think he's trying to access v[i] by summing $sp and 'i'. Can you do that, without initialising any register at the beginning like we did for $s0 and $s1?)
lb $a0, 0($t0) #$a0 = v[i] (Q4: Isn't this wrong? It should access c[i], not v[i])
lb $a1, 0($s0) #$a1 = *a
jal sub2 #Jump to sub2
sll $t1, $s2, 2 #$t1 = 4*i
addu $t1,$t1,$sp #$t1 = 4*i + $sp (By this, I guess he's accessing v[i])
sw $v0,0($t1) #v[i] = $v0 (returned value from sub2)
addiu $s2, $s2, -1 #i--
b For
end:
#Close the activation block
lw $s2,12($sp)
lw $s1,16($sp)
lw $s0,20($sp)
lw $ra,24($sp)
addiu $sp,$sp,28
jr $ra #Jump to register address
Could somebody help me?
Thank you in advance.
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.