I'm trying to do exponentiation with recursion, I'm struggling with the recursive part as it's getting stuck in an infinite loop. I'm just confused as to why it's getting stuck in the loop with power and even. Odd hasn't been started I was planning on reusing some of the code from even to get me started on odd.
C code
/* Prompt user for two non-negative integers. Raised the first to the
power of the second and print it. */
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
/* Is the parameter even? */
int is_even (int n);
/* Return a*b. */
int multiply (int a, int b);
/* Return a^b. */
int power (int base, int exponent);
int
main ()
{
int base, exponent;
printf ("Please enter non-negative base: ");
scanf ("%d", &base);
printf ("Please enter non-negative exponent: ");
scanf ("%d", &exponent);
if (base == 0 && exponent == 0)
{
printf ("Can't raise 0 to zero power.\n");
exit (0);
}
int result = power (base, exponent);
printf ("%d^%d=%d\n", base, exponent, result);
}
/* Determine if a non-negative number is even by inspecting
the least-significant bit. A leaf procedure. */
int
is_even (int n)
{
if ((n & 0x1) == 0)
{
return TRUE;
}
return FALSE;
}
/* Multiply the parameters together by repeated addition. A leaf
procedure. */
int
multiply (int a, int b)
{
int result = 0;
while (a > 0)
{
result += b;
a--;
}
return result;
}
/* Calculate b raised to the power of a recursively. */
int
power (int b, int e)
{
int even_exponent, temp, result;
/* Base case of the recursion: exponent is 0. */
if (e == 0)
{
return 1;
}
/* There are two recursive cases. First, determine whether
the exponent is even or odd. */
even_exponent = is_even (e);
if (even_exponent)
{
/* Even case. */
temp = power (b, e / 2);
result = multiply (temp, temp);
}
else
{
/* Odd case. */
temp = power (b, (e - 1) / 2);
result = multiply (b, temp);
result = multiply (result, temp);
}
return result;
}
MIPS code
power:
#If exponent $s1 is equal to zero returns 1 as result
beq $s1,$zero,expoZero
addi $t0,$t0,1
jal checkEven
#if base is even go to even
beq $s2,$zero,even
beq $s2,$t1,odd
#if base is odd go to odd
addi $sp, $sp, 4
lw $ra, 0($sp)
checkEven:
#Check if exponent is even or odd
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $s1, 0($sp)
andi $s2,$s1,0x1
lw $s1, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp 8
jr $ra
expoZero:
#returns 1
li $s3, 1
jr $ra
even:
addi $sp, $sp, -4
sw $ra,0($sp)
#if the base is even it performs a^(b/2)*a^(b/2)
srl $s1,$s1,1 #$s1/2
jal power
move $s4,$v0
move $a0,$s4
move $a1,$s4
jal multiply
#returns result
addi $s3, $s3, 1
addi $sp, $sp, 4
lw $ra,0($sp)
jr $ra
odd:
#if the base is odd it performs a*a^((b-1)/2)*a^((b-1)/2)
#returns result
addi $s3, $s3, 1
jr $ra
multiply:
addi $sp, $sp -24
sw $ra, 20($sp)
sw $s0, 16($sp)
sw $s1, 12($sp)
sw $s2, 8($sp)
sw $s3, 4($sp)
sw $s4, 0($sp)
#set result equal to zero
move $s3,$zero
#test for a > 0
slt $t2,$a0,$zero #a < 0
loop:
#while (a<0)
beq $t2,$zero,exit
add $s3,$s3,$a1 #result += b
addi $a0,$a0,-1 #a--
j loop
exit:
lw $s4, 0($sp)
lw $s3, 4($sp)
lw $s2, 8($sp)
lw $s1, 12($sp)
lw $s0, 16($sp)
lw $ra, 20($sp)
addi $sp, $sp, 24
j power
I'd try something like this:
int pow(int const x, unsigned const e)
{
int const y(e % 2 ? x : 1);
//int const y(((x ^ 1) & -(e % 2)) ^ 1);
return !e ? 1 : 1 == e ? x : pow(x * x, e / 2) * y;
}
Shouldn't be too hard to convert to MIPS.
Godbolt made this:
pow(int, unsigned int):
b $L4
li $2,1 # 0x1
$L9:
beq $5,$0,$L6
nop
beq $5,$3,$L3
li $6,1 # 0x1
$L10:
mult $2,$6
srl $5,$5,1
mflo $2
nop
nop
mult $4,$4
mflo $4
$L4:
andi $3,$5,0x1
beq $3,$0,$L9
li $3,1 # 0x1
bne $5,$3,$L10
move $6,$4
$L3:
mult $4,$2
mflo $2
j $31
nop
$L6:
li $4,1 # 0x1
mult $4,$2
mflo $2
j $31
nop
See? Recursion was optimized away by the compiler.
Related
int fact (int n)
{
if (n < 1) return (1);
else return (n * fact(n – 1));
}
fact:
addi $sp, $sp, –8 # adjust stack for 2 items
sw $ra, 4($sp) # save the return address
sw $a0, 0($sp) # save the argument n
slti $t0,$a0,1 # test for n < 1
beq $t0,$zero,L1 # if n >= 1, go to L1
addi $v0,$zero,1 # return 1
addi $sp,$sp,8 # pop 2 items off stack
jr $ra # return to caller
L1: addi $a0,$a0,–1 # n >= 1: argument gets (n – 1)
jal fact # call fact with (n –1)
lw $a0, 0($sp) # return from jal: restore argument n
lw $ra, 4($sp) # restore the return address
addi $sp, $sp, 8 # adjust stack pointer to pop 2 items
mul $v0,$a0,$v0 # return n * fact (n – 1)
jr $ra # return to the caller
That's example on the book,I tried to know how it operates but still no idea
1.What happened if I didn't restore return address and argument a0 after L1?
2.What happened if I didn't pop the stack after L1?
3.What happened if I swap restore step and popping step?
I am writing a MIPS move vowels function, now I am stuck in a infinite loop because something wrong with the return address. Can someone help here? The issue is only for function "moveVowelsToBeginning", functions countvowels and isVowels work fine.
C code:
char myName[8] = "example";
int main()
{
vowels=countvowels(0,6); //count the vowels using recursion
moveVowelsToBeginning(0,6); //move vowels to the beginning
return 0;
}
int countvowels(int low, int high)
{
omit...
}
int isVowel(int low) // whether the indexed character is a vowel
{
if vowel ("omit code here")
return 1;
else
return 0;
}
int moveVowelsToBeginning(int low, int high)
{
int i, j; //two indexes
char temp;
j=low; //this index used as head of detected vowels
for(i=low;i<=high;i++)
{
if(isVowel(i))
{
//swap characters
temp = myName[j];
myName[j]= myName[i];
myName[i] = temp;
j++;
}
}
}
MIPS code for function moveVowelsToBeginning:
moveVowelsToBeginning:
addiu $sp,$sp,-16 #stack grows by 16 bytes
sw $ra,12($sp) #save return address
sw $a0,0($sp) #save the arguments into stack
sw $s0,4($sp) #$s0 is used to hold the variable i
sw $s1,8($sp) #$s1 is used to hold the variable j
add $s0,$zero,$a0 #i==0
add $s1,$zero,$a0 #j==0
addiu $t3,$zero,6 #high==6
la $t7, myName #gets base address of myName
for_loop:
bgt $s0, $t3, for_loop_done #if i>high, go to for_loop_done to exit the loop
jal isVowel #jump to procedure isVowel
bne $v0, $zero, swap #compare the return value to 0, if not 0, call swap procedure.
addi $s0, $s0, 1 #i++
move $a0, $s0
j for_loop #loop again
swap:
addiu $sp,$sp,-8 #stack grows by 24 bytes
sw $ra,4($sp) #save return address
sw $s2,0($sp) #$s2 is used to hold the variable temp
add $t4,$t7,$s1 # myName[j] = myName + j
lbu $s2,0($t4) # temp=myName[j]
add $t5,$t7,$s0 # myName[i] = myName + i
lbu $t6,0($t5) # myName[i]
lbu $s7,0($t4) # myName[j]
move $s7,$t6 # myName[j]= myName[i]
move $t6,$s2 # myName[i] = temp
sb $s7,0($t4)
sb $t6,0($t5)
addi $s0,$s0,1 # i++
addi $s1,$s1,1 # j++
sw $s2,0($sp) #restore
sw $ra,4($sp)
addiu $sp,$sp,8
jr $ra
for_loop_done:
lw $s1,8($sp)
lw $s0,4($sp)
lw $a0,0($sp)
lw $ra,12($sp)
nop # put in breakpoint here to show memory
I know how to make a function in MIPS that takes in 4 arguments because I know I can just put the arguments into the registers $a0-$a3. But suppose you want more than 4 arguments, how do you get around the fact that there is only 4 argument registers in MIPS ($a0-$a3)?
There are a few different ways to do this, each with its own tradeoffs: (1) using the mips ABI, (2) using your own internal register conventions, or (3) using a C-like struct
The mips ABI:
The mips ABI [like most other arches], when you run out of argument registers, the remaining arguments are pushed onto the stack.
Consider the following C program:
void
callee(int a,int b,int c,int d,int e,int f)
{
}
void
caller(void)
{
int a;
int b;
int c;
int d;
int e;
int f;
callee(a,b,c,d,e,f);
}
The mips ABI equivalent would look like:
caller:
addiu $sp,$sp,-8 # space for e,f
lw $t0,f
sw $t0,4($sp)
lw $t0,e
sw $t0,0($sp)
lw $a3,d
lw $a2,c
lw $a1,b
lw $a0,a
jal callee
addiu $sp,$sp,8 # remove space for e,f
jr $ra
callee:
addiu $sp,$sp,-4
sw $ra,0($sp)
move $t0,$a0 # get a
move $t1,$a1 # get b
move $t2,$a2 # get c
move $t3,$a3 # get d
lw $t4,4($sp) # get e
lw $t5,8($sp) # get f
# ...
lw $ra,0($sp)
addiu $sp,$sp,4
jr $ra
Self contained functions:
If your functions are self contained within the program [i.e. you aren't calling external ABI conformant functions], you can pass arguments in just about any register you wish:
caller:
lw $s7,f
lw $s6,e
lw $a3,d
lw $a2,c
lw $a1,b
lw $a0,a
jal callee
jr $ra
callee:
addiu $sp,$sp,-4
sw $ra,0($sp)
move $t0,$a0 # get a
move $t1,$a1 # get b
move $t2,$a2 # get c
move $t3,$a3 # get d
move $t4,$s6 # get e
move $t5,$s7 # get f
# ...
lw $ra,0($sp)
addiu $sp,$sp,4
jr $ra
Using a C struct:
Like in C, you can pass many things around in a struct:
struct args {
int a;
int b;
int c;
int d;
int e;
int f;
};
void
callee(struct args *av)
{
int tmp;
tmp = av->a;
tmp = av->b;
tmp = av->c;
tmp = av->d;
tmp = av->e;
tmp = av->f;
}
void
caller(void)
{
struct args args;
args.a = 1;
args.b = 2;
args.c = 3;
args.d = 4;
args.e = 5;
args.f = 6;
callee(&args);
}
The asm equivalent of a C struct is to use equates to offsets from a base register:
# offsets within "struct"
a = 0
b = 4
c = 8
d = 12
e = 16
f = 20
caller:
la $a0,args
li $t0,1
sw $t0,a($a0)
li $t0,2
sw $t0,b($a0)
li $t0,3
sw $t0,c($a0)
li $t0,4
sw $t0,d($a0)
li $t0,5
sw $t0,e($a0)
li $t0,3
sw $t0,f($a0)
jal callee
jr $ra
callee:
addiu $sp,$sp,-4
sw $ra,0($sp)
lw $t0,a($a0) # get a
lw $t1,b($a0) # get b
lw $t2,c($a0) # get c
lw $t3,d($a0) # get d
lw $t4,e($a0) # get e
lw $t5,f($a0) # get f
# ...
lw $ra,0($sp)
addiu $sp,$sp,4
jr $ra
A small project that we have to do in MIPS using mars linux is to make 4 different versions using this basic code that the teachers gave us but the problem is that in school it works when i try it home it gives me
Runtime exception at 0x00400028: address out of range 0x00000000
this would be located at B0_7
integer_to_string_v0: # ($a0, $a1, $a2) = (n, base, buf)
move $t0, $a2 # char *p = buff
# for (int i = n; i > 0; i = i / base) {
move $t1, $a0 # int i = n
B0_3:
blez $t1, B0_7 # si i <= 0 salta el bucle
div $t1, $a1 # i / base
mflo $t1 # i = i / base
mfhi $t2 # d = i % base
addiu $t2, $t2, '0' # d + '0'
sb $t2, 0($t0) # *p = $t2
addiu $t0, $t0, 1 # ++p
j B0_3 # sigue el bucle
# }
B0_7:
sb $zero, 0($t0) # *p = '\0'
B0_10:
jr $ra
Solution: Settings->Initialize Program Counter to global "main" if defined Found this over the internet if it helps anyone else
I'm having some trouble implementing a division algorithm with MIPS, I'm assuming it has something to do with the way that I'm shifting and setting the least significant bit, but I'm not entirely sure.
The algorithm goes like this:
1) Subtract the divisor register from the remainder register and place the result in the remainder register.
2a) If the remainder >= 0, shift the quotient register to the left, setting the new rightmost bit to 1
2b) If the remainder <0, Add the divisor register to the remainder register and store the result in the remainder register (to restore the remainders previous value). Shifht the quotient register to the right, setting the rightmost bit to 0.
3) Shift the divisor register right one bit
4) Do this n+1 repititions. Since we are doing unsigned 8-bit division, i would assume this to mean 9 repitions
Edit: To make this simpler, I just set $t0 = 12 and $t1 = 5. However for the quotient i get 63 and the remainder I get 0
.data
quotient_a: .asciiz "Quotient (a/b): "
remainder_a: .asciiz "Remainder (a/b):"
quotient_b: .asciiz "Quotient (b/a): "
remainder_b: .asciiz "Remainder (b/a):"
error: .asciiz "Sorry, divide-by-zero not allowed"
new_line: .asciiz "\n"
.text
main:
li $s0, 8 #8 bit
li $t0, 12
li $t1, 5
## Quotient A message
la $a0, quotient_a # load the addr of display_sum into $a0.
li $v0, 4 # 4 is the print_string syscall.
syscall # do the syscall.
##Computer the Quotient and remainder
## $t0 = a $t1 = b
## $t0 = dividend $t1 = divisor
li $t2, 0 ##Quotient
li $t3, 0 ##Remainder
li $t9, 0 ##i
li $s0, 9 ##count
loop:
sub $t3, $t3, $t1
blt $t3, 0, less_than
## >= 0
sll $t2, $t2, 1
addi $t2, $t2, 1
j cont
less_than:
add $t3, $t3, $t1
sll $t2, $t2, 1
cont:
srl $t1, $t1, 1
addi $t9, $t9, 1
bne $t9, $s0, loop
## print the quotient
move $a0, $t2
li $v0, 1 # load syscall print_int into $v0.
syscall # make the syscall.
## new line
la $a0, new_line # load the addr of new_line into $a0.
li $v0, 4 # 4 is the print_string syscall.
syscall
## Remainder A message
la $a0, remainder_a # load the addr of display_sum into $a0.
li $v0, 4 # 4 is the print_string syscall.
syscall # do the syscall.
## print the remainder
move $a0, $t3
li $v0, 1 # load syscall print_int into $v0.
syscall # make the syscall.
#Exit Program
li $v0, 10 # syscall code 10 is for exit.
syscall # make the syscall.
The description of the algorithm looks a bit off to me. This is what a working 8-bit division algorithm in C looks like:
unsigned char Q,R;
unsigned char N=12,D=5;
int i;
Q = 0;
R = 0;
for (i = 0; i < 8; i++) {
R <<= 1;
R |= (N & 0x80) >> 7;
N <<= 1;
Q <<= 1;
if (R >= D) {
R -= D;
Q |= 1;
}
}
Transcribing that into MIPS assembly shouldn't be too difficult.