C to MIPS trouble - c

So I have been working on this for a few days now, and I have managed to get through all of it except one part is getting me.
int mystery(int a0)
{
if (a0 == 0)
{
return 0;
}
else
{
return mystery(a0 - 1) + a0;
}
}
I have this recursive function and I have some MIPS code. The C code works but I have a problem somewhere in my MIPS code that is making it not come out correctly beyond putting in 2.
.text
main:
li $a0, 2
jal mystery
move $a0, $v0
jal putDec
li $a0, '\n'
li $v0, 11
syscall
li $a0, 3
jal mystery
move $a0, $v0
jal putDec
li $a0, '\n'
li $v0, 11
syscall
li $v0, 10
syscall
putDec:
li $v0, 1
syscall
jr $ra
mystery:
bne $0, $a0, recur
li $v0, 0
jr $ra
recur:
sub $sp, $sp, 8
sw $ra, 4($sp)
sub $a0, $a0, 1
jal mystery
sw $v0, 0($sp)
jal mystery
lw $t0, 0($sp)
addu $v0, $v0, $t0
addu $v0, $v0, 1
add $a0, $a0, 1
lw $ra, 4($sp)
add $sp, $sp, 8
jr $ra
Everything up to the label 'mystery' is fine it is just there as a formality to actually put in arguments and print after. The problem I am having is getting values above 3 to print out the right numbers. Help would be greatly appreciated if someone could see where I am making my mistake. Thank you

Try taking a step back, and comparing the structure of the C and assembly code, without worrying too much about the details.
In the C code, there is a conditional, leading to either a base case (which just returns a value) or the recursive case. The recursive case performs a subtraction, the recursive call to mystery, and an addition.
Now look at the assembly version: this, too, has a conditional leading to either a base case or a recursive case. But look at the structure of the recursive case: there are two recursive calls to mystery there! That's a strong hint that it's unlikely to be doing the same thing...

Whoo after extensive work and tracking the code one step at a time I think I finally got it, had to do quite a bit of changing but actually seems easier now. Here is the code in case you want to look over it again, if you can see a problem that could arise with any certain values I did not test. Thanks for the help all.
New recursive mystery function:
mystery:
bne $0, $a0, recur
li $v0, 0
jr $ra
recur:
sub $sp, $sp, 8
sw $ra, 4($sp)
sw $a0, 0($sp)
sub $a0, $a0, 1
jal mystery
lw $t0, 0($sp) #
addu $v0, $v0, $t0
lw $ra, 4($sp)
add $sp, $sp, 8
jr $ra
Thanks again. :)

you're suppose to return the ASCII symbol, change "li $v0, 1" in putDec to "li $v0, 11" (that was not a mistake)

Related

Mips program infinite loop issue

I am currently trying to figure out why my mars program for creating an array is getting stuck in an infinite loop. I am trying to create an array using different functions and in the create array function the program is supposed to jump into a get number function and return the value to store it into the array but the counter seems to not be decrementing and it just keeps asking for an element for the array. Here is the code:
.data
str5: .asciiz "Please enter a number of elements for the array between 0 and 20: "
str6: .asciiz "Please enter an element: "
array: .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.text
main:
begin:
li $v0, 4
la $a0, str5
syscall
jal readnum
add $a0, $v0, $0
jal verify
add $a0, $v0, $0
jal createarray
la $s1, array
jal printarray
li $v0, 10
syscall
printarray:
createarray:
add $t0, $ra, $0
add $s0, $a0, $0
add $t9, $0, $0
la $s1, array
loop:
beq $s0, 0, done
li $v0, 4
la $a0, str6
syscall
jal readnum
add $t1, $v0, $0
sw $t1, 0($s1)
addi $s1, $s1, 4
addi $s0, $s0, -1
addi $t9, $t9, 1
j loop
done:
add $ra, $t0, $0
jr $ra
verify:
add $s0, $a0, $0
bge $s0, 20, begin
ble $s0, 0, begin
add $v0, $s0, $0
jr $ra
readnum:
li $v0, 5
syscall
jr $ra
EDIT: After re-reading the code, what is causing the error is actually the following code:
jal printarray
You never did the following code in printarray:
jr $ra
So, it would go back down to the other functions.
Use the $sp (the proper way to handle the $ra when calling functions within functions)
The following segment is the cause of the problem:
syscall
jal readnum
add $t1, $v0, $0
Moreso specifically the jal call. Calling a function within a function requires some more steps to be taken place.
First, understand what the jal instruction is doing. The jal instruction, put simply, marks where you currently are and stores it in the $ra register. Then, it will jump to that function.
But there's a problem: you are already wishing for the $ra register to remember where you once were because you, in fact, called createarray!
So, you called createarray, making $ra store where you once were, then within that function you called readnum, making $ra store where you are in createarray. **But now, you have lost where you once were before called createarray. So, it will keep looping back to what $ra used to be, which is inside your createarray function.
Fortunately, the $sp register is just what you need
How to use the $sp register
To store where we are, we push to the stack:
addi $sp, $sp, -4 # by convention, we use negative numbers when pushing
sw $ra, ($sp)
To get where we once were after we called the function (which replaced our $ra in the first place), we pop the stack:
lw $ra, ($sp)
addi $sp, $sp, 4 # by convention, we use positive numbers when popping
So in the grand scope of things, here is what you do:
# push to the stack
addi $sp, $sp, -4
sw $ra, ($sp)
jal theFunctionWeWantToCallInOurFunction
# pop the stack, get back our $ra
lw $ra, ($sp)
addi $sp, $sp, 4
Let's apply this solution to your codebase:
.data
str5: .asciiz "Please enter a number of elements for the array between 0 and 20: "
str6: .asciiz "Please enter an element: "
array: .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.text
main:
begin:
li $v0, 4
la $a0, str5
syscall
jal readnum
add $a0, $v0, $0
jal verify
add $a0, $v0, $0
jal createarray
la $s1, array
jal printarray # comment off this line and you won't have an infinite loop
li $v0, 10
syscall
printarray:
createarray:
add $t0, $ra, $0
add $s0, $a0, $0
add $t9, $0, $0
la $s1, array
loop:
beq $s0, 0, done
li $v0, 4
la $a0, str6
syscall
# store where we wish to come back to so that $ra can be overriden without losing data.
addi $sp, $sp, -4
sw $ra, ($sp)
# call the function. jal will replace our current $ra
jal readnum
# retrieve what our $ra once was
lw $ra, ($sp)
addi $sp, $sp, 4
add $t1, $v0, $0
sw $t1, 0($s1)
addi $s1, $s1, 4
addi $s0, $s0, -1
addi $t9, $t9, 1
j loop
done:
add $ra, $t0, $0
jr $ra
verify:
add $s0, $a0, $0
bge $s0, 20, begin
ble $s0, 0, begin
add $v0, $s0, $0
jr $ra
readnum:
li $v0, 5
syscall
jr $ra

C to MIPS Decimal to Binary Converter Using Recursion

I am new to MIPS and I wrote a basic format of what I think the code from the C file I wrote is the equivalent to of the MIPS.
My assignment is to convert the following C file which I wrote into a direct translation of what the MIPS is supposed to be. My current C code is :
#include <stdio.h>
int d2b(int d)
{
if(d == 0)
{
return;
}
else
{
return (d %2 + 10 * d2b(d/2));
}
}
int main()
{
int d = 99;
int b;
b = d2b(d);
printf("Input => %d \n", d);
printf("Output => %d ", b);
return;
}
So far I have the following :
.data
msg1 .asciiz “Number is “
msg2 .asciiz “\nConverted to \n“
.text
.globl main
main:
li $v0, 4
la $a0, msg1
syscall
li $v0, 5 #Exit syscall
syscall
add $a0, $v0, $zero
jal fact
add $a0, $v0, $zero
li $v0, 1
syscall
li $v0, 10
la $a0, msg2
syscall
fact:
li $t0 0 #load 0
beq $a0, $t0, skip #test n
li $v0 0
jr $ra
skip:
subu $sp, $sp, 32
sw $ra 20($sp)
sw $fp, 16($sp)
addiu $fp, $sp, 28
sw $a0, 0($fp) #save n
li $t1 2 #load 2
divu $a0 $t1 #n / 2
mfhi $t2 #remainder
mflo $t3 #quotient
move $a0, $t3 #n = quotient
addi $v0, $a1, 10
jal fact
lw $a0, 0($fp) #restore n
multu $v0, $a0
lw $ra, 20($sp)
lw $fp, 16($sp)
addiu $sp, $sp, 32
jr $ra
My main problem is not knowing how to use syscall and not really understand a recursive function in MIPS. Please point out my mistakes and errors!
your MIPS syscalls are in this section
li $v0, 4
la $a0, msg1
syscall
li $v0, 5 #Exit syscall
syscall
add $a0, $v0, $zero
jal fact
add $a0, $v0, $zero
li $v0, 1
syscall
li $v0, 10
la $a0, msg2
syscall
They are commented incorrectly
In a MIPS syscall $v0 holds the "syscall function" or in English, the service you want the operating system to perform. There's a table of them here.
$a0 will hold the first parameter passed to the call. To set this parameter, one technique is to add the input value to zero storing the result into $a0 That's why you have so many lines like this
add $a0, $v0, $zero # this adds $v0 to the number zero and storing in $a0
Finally, the syscalls you are using are (4 => print String, 5 => read integer, 1 => print integer, and 10 => exit)
So a properly commented example of your code would be
la $a0, msg1 # load string as parameter
li $v0, 4 # load operation "print string"
syscall # request "print string" for msg1
li $v0, 5 # load operation "read integer"
syscall # request "read integer"
add $a0, $v0, $zero # load the read integer into $a0
jal fact
add $a0, $v0, $zero # load the value of $v0 into $a0
li $v0, 1 # load operation "print integer"
syscall # request "print integer"
As you can see, my confusion comes not from your ability to use syscalls, but from your description of what you think you are using the syscalls to do.
You state you want to print a binary number like 01001010 from a decimal input. This typically involves breaking the decimal number down, in a loop, printing out a zero or one in each of the binary number places. As this would require a loop for each placeholder in the binary number, it doesn't seem that a single call to "print integer" would be possible (unless the input was limited to only the decimal '1' and '0', or the input is limited to such a small number that it's binary representation, represented in decimal format is less than max_int).
So for an input of 5, the desired output would be 101, and that would be 3 calls to print, in the order of '1', '0', '1'. I believe this approach of printing the digits in a loop will give you greater success, and will permit you to print every positive decimal number inputted.
In short, I think your command of syscall is fine, but you're still struggling with how to do loops and solve problems in assembly. Try to figure out how you would determine the digits in the needed order by hand, using a pencil and paper, and then attempt to code that into your program.

Unaligned memory reference when reading and storing multiple strings

I'm trying to read multiple strings from a file and store them all together. The way the input is designed, the following preconditions are guaranteed:
The number of string I have to read in is pre-determined
The length of all strings (including a newline and a NUL-byte) is the same
I have the following code, however I've been running into an unaligned memory word reference error the second time the loop is entered.
.data
.align 4
aLine:
.space 128
arr:
.space 1024
.text
//...
//...
build:
addi $sp,$sp,-24
sw $ra, 20($sp)
sw $s4, 16($sp)
sw $s3, 12($sp)
sw $s2, 8($sp)
sw $s1, 4($sp)
sw $s0, 0($sp)
li $t0,0 #incrementing value for index
move $s0,$a0 #$a0 is the number of strings
addi $s1,$a1,2 #$a1 is the length of each string, $s1 makes room for the newline character and the null
la $a2,arr
build_loop:
beq $t0,$s0,build_done
li $v0, READ_STRING #READ_STRING is set to 8 earlier in the program
la $a0,aLine
move $a1,$s1
syscall
mul $t1,$t0,$s1
add $t2,$t1,$a2
sw $v0,($t2)
addi $t0,$t0,1
jal build_loop
build_done:
lw $ra, 20($sp)
lw $s4, 16($sp)
lw $s3, 12($sp)
lw $s2, 8($sp)
lw $s1, 4($sp)
lw $s0, 0($sp)
addi $sp,$sp,24
jr $ra
I got this mostly from an online resource on creating an array of integers, tweaking the values to work with strings of a constant length.
What exactly am I missing here?
You are using jal build_loop, which is effectively recursion, not iteration.Unfortunately, jal build_loop stores the return address to a register, (load-store-architecture, remember?) which happens to be $ra (GPR 31). This clashes badly with the rest of build_loop, which neglects to save $ra on the stack.The result is, of course, that $ra is clobbered.

Delay Loop in MIPS

I am having trouble writing a delay loop that will return a constant 0x80000. The output should be like Hello, world! 0
Hello, world! 1
Hello, world! 2
...
but when I run my program, the terminal doesn't show anything even though I believe one Hello, world! should be showing up. I tried to figure out what is wrong by debugging the code, but that does not seem to be helping me. Any suggestions on how to fix this?
.ent getDelay
.text
.global getDelay
getDelay:
addi $sp, $sp, -1
sw $ra, 0($sp)
la $a0, helloStr
lw $a1, counter
jal printf
nop
lw $ra, 0($sp)
addi $sp, $sp, -1
lw $t0, ($a1)
addiu $t0, $t0,1
la $t1, counter
sw $t1, ($a1)
$v0 = 0x80000
jr $ra
.end getDelay
.data
helloStr: .asciiz "Hello, world %d\n"
counter: .word 100
You should only ever adjust $sp in multiples of 4 (the word size).
You should use addiu $sp, $sp, -4 and addiu $sp, $sp, 4.
You are incrementing $t0 but then storing $t1. You don't need
the la $t1, counter and instead of sw $t1, ($a1) you should use
sw $t0, ($a1).
$v0 = 0x80000 is not an instruction, you probably want li $v0,
0x80000.
If this function itself is supposed to be some delay, then you need
a loop in it.

MIPS Functions and arrays

I can understand and use Java/c++ to a good extent, but for the life of me assembly just confuses me there are 2 functions I'm having trouble with. First:
One function that receives a string and prints it on the terminal
And another one that receives a string and converts it to integers (Strings given all made of numbers).
Any idea on where to start?
Update
On the second function, so far I got this:
main:
atoi:
li $v0, 8
la $a0, tstr
li $a1, 64
syscall
sub $sp, $sp,4
sw $ra, 0($sp)
move $t0, $a0
li $v0, 0
next:
lb $t1, ($t0)
beqz $t1, endloop
mul $v0, $v0, 10
add $v0, $v0, $t1
sub $v0, $v0, 48
add $t0, $t0, 1
b next
endloop:
lw $ra, 0($sp)
add $sp, $sp, 4
Updated code, still getting the error on 10 being an invalid operand. And about sub $v0, $v0, 48 should I just do it as sub $t1, $t1, 48?
For input-output stuff, you have to use system calls. For writing (zero-terminated) strings you'll use syscall #4, which wants the address of the buffer in $a0. Now just place the umber of the syscall in $v0 and execute it. For example, this snippet reads a string:
li $v0, 8 # syscall number
la $a0, theString # buffer
li $a1, 64 # length of buffer
syscall # read!
Here you can find some syscalls numbers.
For the second exercise, here's the C++ code, try to translate it :P
int result = 0;
while (*digit >= '0' && *digit <='9') { // digit is char *
result = (result * 10) + (*digit - '0');
digit++;
}
EDIT:
Ok, there are a couple of errors. First, you're not checking whether you've reached the end of the string (simply compare $t1 with 0 at the beginning). And you should first subtract '0' from $t1, then add it to $v0.
next:
lb $t1, ($t0)
# are there other character or this is the last one?
mul $v0, $v0, 10
add $v0, $v0, $t1
sub $v0, $v0, 48 # subtract 48 only from $t1, not from whole result
add $t0, $t0, 1
b next

Resources