First of all, I have already checked related questions to this one, yet I am still not able to overcome the problem I have with this program.
What I am trying to do is, basically, take a byte[] input and duplicate it to another byte[], and print the duplicate array. My code is as above:
.data
hello: .asciiz "hello"
inp: .byte 5
dup: .byte 5
.text
main:
la $a0, inp #get input
li $v0, 8
syscall
la $s0, dup #load arrays on s0 and s1
la $s1, inp
li $t0, 0 #instantiate offsets as 0
li $t2, 0
Load:
lb $t1, 0($s1) #load first byte
sub $t1, $t1, 48 #test if it is <0
bltz, $t1, exit #if so go to exit
add $t1, $t1, 48
sb $t1, 0($s0) #else save the byte
add $s1, $s1, 1 #increment offsets
add $s0, $s0, 1
j Load
la $a0, hello
li $v0, 4
syscall
exit:
li $t1, 0
add $s0, $s0, 1
sb $t1, 0($s0) #add null to the end of dup
la $a0, dup
li $v0, 4
syscall
jr $ra
I am new to MIPS and, I am not able to recognize what the problem is.
By the way, I am passing 123 as an input and I am getting countless of 1s as output, which tells me that I am stuck in the loop and never getting any further in $s1 (inp).
There are a couple of problems with your code:
First, .byte 5 doesn't reserve space for 5 bytes, it declares a single byte with the value 5. If you want 5 bytes you should say .space 5 (the bytes will be initialized with the value 0 IIRC).
Second, syscall 8 takes one more argument; $a1 = maximum number of characters to read, which you haven't specified. If you have room for 5 bytes in your buffer you should set $a1 to 5. Note that "maximum number of characters to read" actually means "maximum number of characters to read including the terminating null-character".
Related
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.
So I am using MIPS trying to read in several strings entered by a user and then print them, but I am not getting the behavior I expect.
I want to take in 4 bytes of user input (4 characters essentially). In my loop I use the letter 'D' as a signal to exit. The problem is, no matter what I input, when I try to print the very first input later (or even the second, or the third), all I get printed out is the letter 'D' that was used to exit (which should be the last value of the array no?).
.data
mem: .space 256 #256 bytes of space for input
inst .space 5
.text
la $s1, mem #s1 used to take input
la $s2, 0($s1) #Pointer to base address of memory
jal readLoop #Read input loop
lw $a0, 0($s2) #Attempt to read very first saved input
li $v0, 4
syscall
li $v0, 10 #End program
syscall
readLoop:
li $v0, 8 #read string
la $a0, inst #location of input memory
addi $a1, $zero, 5 #length of buffer
syscall
lb $t2,($a0) #used to exit loop
sw $a0, 0($s1) #store input into memory
addi $s1, $s1, 4 #increment memory by 4 bytes
li $t1, 'D'
bne $t2, $t1, readLoop #exit loop on input of a 'D'
jr $ra
I've checked my input as it comes in and even when in the array after saving. It seems my print is the problem, but I could easily be wrong. Any ideas?
The fix was rather easy. Instead of storing the user input into a separate memory location and then putting that location in my array, I simply had the user input be stored directly into my array. See code changes below.
.data
mem: .space 256 #256 bytes of space for input
.text
la $s1, mem #s1 used to take input
jal readLoop #Read input loop
la $a0, mem #Attempt to read very first saved input
li $v0, 4
syscall
li $v0, 10 #End program
syscall
readLoop:
li $v0, 8 #read string
la $a0, mem #set user input as memory location
addi $a1, $zero, 5 #length of buffer
syscall
lb $t2,($a0) #used to exit loop
addi $s1, $s1, 4 #increment memory by 4 bytes
li $t1, 'D'
bne $t2, $t1, readLoop #exit loop on input of a 'D'
jr $ra
I am trying to make a loop that will add user inputted integers into an array until it fills the array. Every time I typed in a value, QTSPIM spits out 268501016 which I assume to be some random value stored in an register.
To test if my program was going through the whole loop, I added a call to an ascii line when the program reached the branch portion of my beq. The program seemed to be branching even if the values were not (at least to my understanding) equal.
.data
array1: .space 24
str1: .ascii "Type in numbers:"
str2: .ascii "Reached Terminate"
.text
main:
li $t2, 5
li $t3, 0
loop1:
beq $t3, $t2, terminate #branch if equal
la $a0, str1
syscall
ori $v0, $0, 5 #instruction to store user input in v0
syscall #get user input and store it in v0
la $t4, array1 #load the address of the array
addu $t0, $0, $v0 #add v0 (our user input) to $t0
sw 0($t4), t0 #stores the value in $t4 to our array
addi $t3, $t3, 1 #add 1 to t3 (incrementing the counter)
addi $t4, $t4, 4 $add 4 to increment the array 4 bits to the next array slot
jal loop1
terminate:
la $a2, str2 #load the string to check when the program reaches terminate
syscall
ori $v0, $0, 10 # end the program
syscall
The only thing I can think is that my jump call is not going back to loop1, but if this is the case I am unaware how to fix that.
This is 32 bit MIPS code.
You're not setting up the registers properly before the syscalls.
Here there should be an li $v0, 4 before syscall:
la $a0, str1
syscall
If we assume that you're trying to print str2 here, there should be an li $v0, 4 before the syscall, and $a0 should be used instead of $a2:
la $a2, str2 #load the string to check when the program reaches terminate
syscall
This should be sw $t0, 0($t4), not the other way around:
sw 0($t4), t0
This should be j, not jal (jal is used for function calls):
jal loop1
.text
main: la $a0, A #loads address of A into $a0
li $t0, 1 #loads 1 into $t0
sw $t0, ($a0) #stores word from $a0 into $t0
li $t0, 2 #loads 2 into $t0
sw $t0, 4($a0)
li $t0, 3
sw $t0, 8($a0)
li $t0, 4
sw $t0, 12($a0)
la $a1, A
li $v0, 1
lw $a0, ($a1)
syscall
lw $a0, 4($a1)
syscall
lw $a0, 8($a1)
syscall
lw $a0, 12($a1)
syscall
la $a0, tab
li $v0, 4
syscall
li $v0, 10
syscall
.data
A: .space 16 #array size 16
tab: .asciiz "\t" #makes line indentation
#stores 1 - 4 into $t0
From what I can see, it loads the address of A into a0, then it loads '1' into $t0, then it overrides?!? $t0, by storing the array into it., then loads 2 into $t0, then I'm unsure what the 4 is supposed to do(doesn't do anything as far as I can see, then loads $t0 into 3, then same as '4' don't don't see what 8 does, then loads 4 into $t0, then does something with 12, and then it loads address of A, into $a1, and prints it?!? I'm completely lost about the rest of the program. The program is correct,(loads "1234"), but I am supposed to learn MIPS, by understanding what each program the class lecturer gives. This isn't homework or anything, just learning how MIPS programs work.
The program first stores the constants 1, 2, 3, 4 starting at the address of A, and then it just prints the contents of those four words.
Instruction sw reg1, offset(reg2) stores the contents of the register reg1 into the address pointed by reg2 plus the optional offset specified.
So, in your first example sw $t0, ($a0) will store the contents of $t0 in the address pointed by $a0, which in your case is the address of label A.
It then uses multiples of 4 as the offset because you are storing words and each word occupies exactly 4 bytes, therefore to store the numbers 1, 2, 3, 4 at consecutive words you have to add 4 to each previous address.
It appears to be an unrolled loop. The first loop sets the content of the array and the second loop prints out the contents of the array followed by a tab. Here's what I would assume the pseudo code would look like:
function main()
{
i = 1
while( i <= 4 )
{
A[i-1] = i
i = i + 1
}
i = 1
while( i <= 4 )
{
printToConsole( A[i-1] )
i = i + 1
}
printToConsole( tab )
}
When the code was assembled, the assembler possibly unrolled the loop as a performance improvement.
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