I need to save a data that contains: name, id and rating.
So the insert command is kind this one:
addi $sp, $sp, -12
li $v0,8 # take in input
la $a0, buffer # load byte space into address
move $t0,$a0 # save string to t0
syscall
sw $t0, 0($sp) # name
sw $t1, 4($sp) # id
sw $t2, 8($sp) # rating
addi $t7,$t7,1 # number of elements
The view command is kind this one:
loop:
beq $t6, $t7, end
lw $a0, 0($sp) # name
li $v0, 4
syscall
lw $a0, 4($sp) # id
li $v0, 1
syscall
lw $a0, 8($sp) # rating
li $v0, 1
syscall
addi $t6,$t6, 1
addi $sp,$sp, 12
j loop
When make two elements the first one: aa, 12, 12 second one : bb, 13, 13
After the view command is this: aa, 12, 12 second one: aa, 13, 13
Can you help me solve this problem?
It looks like you're passing the same address (buffer) to the read_string syscall every time. So each call will overwrite the string from the previous call.
You need to have separate buffers for every string. You could for example pass buffer to syscall 8, then calculate the length of the read string, allocate memory for it (including the NULL-terminator) using syscall 9, copy the string from buffer to your newly allocated block of memory, and finally store the address of the allocated memory on the stack instead of buffer's.
Related
I'm having trouble reading a string that previously was introduced by the user and saved in memory.
The function to read the string:
.data
table: .space 1200
buffer: .space 30
empty: .asciiz "The string is empty\n"
.text
la $t7,table
li $s4,0
li $v0, 8
la $a0,buffer
li $a1, 30
syscall
move $t0,$a0
move $a0,$t0 # parameter
jal insert
Then using the readed string, I pass through $a0 as a parameter to another function that stores the string to a certain memory position (with 60 bytes space within each string).
insert: mul $s6,$s4,60 # padding (index*padding)
add $t7,$t7,$s6 # address of the next free position
sw $a0,0($t7)
addi $s4,$s4,1
Also I've got a function that given an index (introduced by the user as an integer), recovers the string stored in that position (if is not empty):
read_table: move $t5,$a0 # a0 contains the index as a parameter
la $t4, table
mul $t5,$t5,60
add $t4,$t4,$t5
lw $s1,0($t4)
beqz $s1,empty_str
li $v0,4
move $a0,$s1
syscall
empty_str: li $v0,4
la $a0,empty
syscall
My problem comes when regardless of the index I enter, the program always return the last string introduced by the user although it seems to be stored properly. I understand that it might be some misuse of the buffer, but any of the solutions I've thought worked.
[EDIT] Here goes the whole code block to clarify the question, as suggested (supposing that all the data that the user introduces is OK):
):
.data
table: .space 1200
buffer: .space 30
empty: .asciiz "The string is empty\n"
.text
la $t7,table
li $s4,0
main: li $v0,4
la $a0,tira1
syscall
li $v0,5
syscall
beqz $v0,index # If 0, read from table, 1 insert to the table
beq $v0,1,string
string: li $v0, 8
la $a0,buffer
li $a1, 30
syscall
move $t0,$a0
move $a0,$t0 # parameter
jal insert
index: li $v0,5 # function to get index
syscall
move $a0,$v0
jal read_table
insert: mul $s6,$s4,60 # padding (index*padding)
add $t7,$t7,$s6 # address of the next free position
sw $a0,0($t7)
addi $s4,$s4,1
b main
read_table: move $t5,$a0 # a0 contains the index as a parameter
la $t4, table
mul $t5,$t5,60
add $t4,$t4,$t5
lw $s1,0($t4)
beqz $s1,empty_str
li $v0,4
move $a0,$s1
syscall
b main
empty_str: li $v0,4
la $a0,empty
syscall
I am new to MIPS and am trying to figure out returning values in nested functions. I am trying to figure out why in the function test when I load $ra from the stack it takes me to the instruction after I call calcs function in main instead of to the instruction after jal test in calcs function? Is a new stack created for every function?
When I am in the test function I should have 2 $ra values in the stack and when I load the last in $ra value it should be the one to return me to the instruction after jal test in calcs function, but that isn't happening and I can't figure out why.
.data
newline: .asciiz " XXXX "
.text
main:
addi $s0, $0, 39 # val 1
addi $s1, $0, 2 # val 2
addi $s2, $0, 14 # val 3
addi $s3, $0, 11 # val 4
add $a0, $0, $s0 # copy val 1 to $a0
add $a1, $0, $s1 # copy val 2 to $a1
jal calcs
add $s4, $0, $v0 # move returned value to $s4
# Exit program
li $v0, 10 # system call to exit program
syscall
calcs:
addi $sp, $sp, -4 # make space in stack
sw $ra, 0($sp) # add $ra value to stack
add $t8, $s0, $a0 # save arg $a0 to $t8
add $t9, $s0, $a1 # save arg $a1 to $t9
jal test
add $t0, $0, $v0 # move returned value to $t0
add $a0, $0, $v0 # move returned value to $a0
# print
li $v0, 1
syscall
li $v0, 4
la $a0, newline
syscall
# get value from stack
addi $sp, $sp, 4
lw $ra, 0($sp)
add $v0, $0, $t0
jr $ra
test:
addi $sp, $sp, -4 # make space in stack
sw $ra, 0($sp) # push $ra value to stack
add $t0, $0, -989898989
add $v0, $0, $t0
#### If I keep the two lines below then the $ra value jumps to be right after I call the calcs function in main. But if I remove it then it goes to the value right after I call the test function in calcs
lw $ra, 0($sp) # load $ra value from stack
addi $sp, $sp, 4 # pop value off stack
# printing
add $a0, $0, $t0
li $v0, 1
syscall
li $v0, 4
la $a0, newline
syscall
add $v0, $0, $t0 # copy $t0 value to $v0 again
jr $ra
In calcs you have:
addi $sp, $sp, 4
lw $ra, 0($sp)
This (incorrect) code sequence will pop the stack and then try to fetch an $ra value from an empty stack. When I run this, as the empty stack is filled with zeros, so that sequence loads 0 into $ra, which causes the program to crash soon after in doing jr $ra with zero in $ra.
The proper code sequence to pop is to fetch first, then deallocate the stack space.
lw $ra, 0($sp)
addiu $sp, $sp, 4
I am trying to figure out why in the function test when I load $ra from the stack it takes me to the instruction after I call calcs function in main instead of to the instruction after jal test in calcs function?
We don't see this behavior in the code as posted, so it must have happened in some other incarnation you were experimenting with.
For example, if you also had that same two-instruction pop-sequence reversed in test. the code would return directly from test to main (instead of properly returning from test to calcs, and only then crashing by trying to return to the null address as it does as posted).
Is a new stack created for every function?
No, all functions in the thread of the process share the same stack via sharing the stack pointer with each other. The $sp stack pointer is implicitly shared (i.e. as a parameter) with functions as they are called.
When I am in the test function I should have 2 $ra values in the stack and when I load the last in $ra value it should be the one to return me to the instruction after jal test in calcs function, but that isn't happening and I can't figure out why.
I'm not sure what you mean by last, but once you use a proper pop sequence everywhere, there will be two return addresses on the stack, an older one to return from calcs to main and the newer one to return from test to calcs. As is the nature of the (call) stack, the newer one is used first and then later the older one.
As pointers are unsigned, use addiu when you're manipulating them, e.g. when adjusting the $sp.
I'm new to MIPS assembly, I want to write a routine that takes the memory address of a string and the memory address of another callback subroutine. This routine will go through every letter in the string and for each letter call the subroutine (this prints the ASCII value of each letter). The pseudo code would look something like this:
string_for_each(string, subroutine) {
for_each_character_in_string {
subroutine(address_of(character))
}
}
This is what my routine looks like right now:
string_for_each:
addi $sp, $sp, -4 # PUSH return address to caller
sw $ra, 0($sp)
jal loop
lw $ra, 0($sp) # Pop return address to caller
addi $sp, $sp, 4
jr $ra
loop:
lb $t1, 0($a0) # Get current character
beq $t1, $zero, end_for_each # Done when reaching NULL character
jr $a1 # Call callback subroutine
addi $a0, $a0, 1 # Increment to get next character in string
j loop
end_for_each:
jr $ra # Return to caller
The thing is that the register $a0 contains the address to the string, and $a1 contains the address to the callback subroutine, and the address to the current character in the string that will be passed to the callback subroutine should also be in $a0. How can $a0 contain both the starting address of the string and the current character at the same time?
The callback subroutine:
ascii:
.data
STR_the_ascii_value_is:
.asciiz "\nAscii('X') = "
.text
la $t0, STR_the_ascii_value_is
# Replace X with the input character
add $t1, $t0, 8 # Position of X
lb $t2, 0($a0) # Get the Ascii value
sb $t2, 0($t1)
# Print "The Ascii value of..."
add $a0, $t0, $zero
li $v0, 4
syscall
# Append the Ascii value
add $a0, $t2, $zero
li $v0, 1
syscall
jr $ra
You will need to save $a0 and $a1 elsewhere (usually stack) for the duration of the subroutine call. Also, your loop isn't a subroutine, no reason to call it using jal. On the other hand, the callback is a subroutine, you should call it using jalr. Something along these lines should work:
string_for_each:
addiu $sp, $sp, -12 # Need 3 locals for $a0, $a1 and $ra
sw $ra, 0($sp) # Store $ra
sw $a1, 8($sp) # Store $a1
loop:
sw $a0, 4($sp) # Store $a0 as it will be used for argument
lb $t0, 0($a0) # Get current character
beq $t0, $zero, end_for_each # Done when reaching NULL character
jalr $a1 # Call callback subroutine
lw $a0, 4($sp) # Reload $a0
lw $a1, 8($sp) # $a1 could have changed (calling convention)
addi $a0, $a0, 1 # Increment to get next character in string
j loop
end_for_each:
lw $ra, 0($sp) # Reload return address to caller
addiu $sp, $sp, 12 # Free locals
jr $ra
So I'm working on a project in MIPS to check if a string input by the user is a palindrome or not. The part I'm stuck on is reading the string and pushing each character of the string into the stack one by one (the PushLoop part of the code). When I debug it, the program seems to think I haven't entered anything at all. Here's what I have so far:
.text
main:
li $v0, 4 # Prints str1
la $a0, str1
syscall
jal Init # Sets $s0 equal to $sp to compare if the stack is empty later
li $v0, 8 # Read String
la $a0, buffer # Loads memory buffer (100)
li $a1, 100 # Defines length of buffer
syscall
la $t0, buffer # Moves base register to $t0
PushLoop:
lb $a2, ($t0) # Loads current character into $a2
beqz $a2, fin # if $a2 is equal to zero, the loop is terminated
jal Push # Pushes what is stored in $a0 to the stack
add $t0, $t0, -8 # Subtracts from buffer
j PushLoop
fin:
la $t0, buffer # Resets the memory buffer (I think)
PopLoop:
jal IsEmpty # Checks if the stack is empty
lb $a2, ($t0) # Loads current character into $a2
beq $v0, 1, isPal # If stack is empty, jump to isPal
jal Pop # Pops what is stored in the stack to $t1
add $t0, $t0, -8 # Subtracts from buffer
bne $a2, $t1, notPal
j PopLoop
notPal:
li $v0, 4 # Prints str3
la $a0, str3
syscall
li $v0, 0 # loads 0 into $v0
j end
isPal:
li $v0, 4 # Prints str2
la $a0, str2
syscall
li $v0, 1 # loads 1 into $v0
j end
#EXIT
end:
li $v0, 10 # ends the program
syscall
Push:
addi $sp, $sp, -8 # Move stack pointer
sb $a2, ($sp) # Store contents of $a2 at ($sp)
jr $ra
Pop:
lw $t1, ($sp) # Pop char from stack and store in $t1
addi $sp, $sp, 8 # Move stack pointer
jr $ra
Init:
add $s0, $sp, $zero # Sets $s0 equal to $sp
jr $ra
IsEmpty:
beq $sp, $s0, Yes # If $s0 is equal to the initial value of $sp, then stack is empty
li $v0, 0 # Loads 0 into $v0
jr $ra
Yes:
li $v0, 1 # Loads 1 into $v0
jr $ra
.data # Data declaration section
str1: .asciiz "Please enter a String: "
str2: .asciiz "Is a palindrome!"
str3: .asciiz "Is NOT a palindrome"
buffer: .space 100
I'm sure there are more things wrong with the code, but I'm just trying to squash one bug at a time. Thanks so much for helping me out!
You're not using syscall 8 properly:
li $v0, 8 # Read String
la $t0, buffer # Loads memory buffer (100)
syscall
If you read the description of syscall 8, it says "Arguments $a0 = buffer, $a1 = length". So those three lines of code should be changed into something like:
li $v0, 8 # Read String
la $a0, buffer
li $a1, 100
syscall
Then you can do la $t0, buffer after the syscall if you still want to use $t0 as the base register for the memory reads in PushLoop.
I am trying to implement puts in MIPS. I made it work with $a0. It printed the string starting at the address of that register. However, I am now trying to implement puts for an arbitrary amount of strings, meaning that I can't use the registers anymore. I am confused as to how to use the stack efficiently. From my main procedure, I stack an arbitrary amount of strings. I then want to jal to puts, which will print all the strings. I also pass on top of the stack the number of strings to print.
Here is my code for puts :
# puts - prints ASCII to the screen
# prints from $a0, $a1, $a2, $a3 (buffers)
# stops when chracter is NULL
# ASSUME THAT WHEN PUTS IS CALLED, AT LEAST ONE STRING IS PASSED
#
# CALLING CONVENTION
#
# I decided not to use any of the registers $a0-$a4.
# Instead, the caller stacks all the strings it wants to print in the stack.
# On top of it, it adds the number of strings it wants to print.
# puts will loop, printing each string, until it has printed all of them.
#
# Since the caller is using "s" registers, puts must save them to the stack.
# Thus, these will be put on top of the stack.
# puts will have to access the strings below the stacked registers.
#
# ============ STACK DIAGRAM ==============
#
# \ Number of Strings \ TOP
# \ String 1 \ +200
# \ String 2 \ +400
# \ String 3 \ +600
# \ String 4 \ +800
# \ String 5 \ +/000
# \ String 6 \ +...
# \ ... \
#
# on entry:
# $ra -- return address
# 0($sp) -- number of strings to print
# x($sp) -- strings to print
#
# on exit:
# $v0 -- number of character printed
#
.text
puts:
addi $sp, $sp, -20 # make room for 6 registers
sw $ra, 16($sp) # save $ra on the stack
sw $s0, 12($sp) # save $s0 on the stack
sw, $s1, 8($sp) # save $s1 on the stack
sw, $s2, 4($sp) # save $s2 on the stack
sw, $s3, 0($sp) # save $s3 on the stack
lw $s0, 224($sp) # copy address of first string inside $s0
lw $s1, 24($sp) # copy number of strings to print inside $s1
move $s2, $zero # counter for the number of strings printed
move $s3, $zero # counter for the number of characters sent to putchar
putsString:
** lbu $t3, ($s0) # load ccurrent char into #t3
beq $t3, $zero, exitString # exit puts if the current character is the NULL character
move $a0, $t3 # put the character to print inside $a0, accessible by putchar
jal putchar # print char using putchar
addi $s3, $s3, 1 # character count += 1
addi $s0, $s0, 4 # increment string address by 1 word (4 bytes)
j putsString # Loop to print next character
exitString:
addi $s2, $s2, 1 # increment number of strings printed
beq $s1, $s2, exitPuts # if # of strings to print = # of strings printed, then exit puts; else, increment # of strings printed
addi $s0, $s0, 200 # $s0 now points to next string to print
j putsString
exitPuts:
move $v0, $s4 # return number of character printed
lw $s3, 0($sp) # restore stack
lw $s2, 4($sp) # -
lw $s1, 8($sp) # -
lw $s0, 12($sp) # -
lw $ra, 16($sp) # -
addi $sp, $sp, 20 # pop from stack
jr $ra # return
The line with a ** is the faulty one for now. I get Runtime exception at 0x00400124: address out of range 0x00000000.
Here is how I save the strings to the stack (I read 2 (2 is hardcoded) strings for now with gets).
init:
beq $s0,2,continue
move $a0, $s2 # load buffer address into $a0
la $t1, limit # - load limit into $a1
lb $a1, ($t1) # - ...
jal gets # call gets - it will modify buffer
move $t0, $v0 # $t0 = string count returned by gets
moveBufferToStack:
lbu $t1, ($s2) # get first character inside buffer
beqz $t1, endMoveBuffer
sw $t1, ($sp) # copy buffer(i) into array(i)
addi $sp, $sp, 4 # array pointer points a position forward
addi $s2, $s2, 4 # same for array pointer
j moveBufferToStack
endMoveBuffer:
addi $sp, $sp, 200 # step to next array cell
addi $s0, $s0, 1 # increment number of strings read
j init # loop until we have 9 elements
continue:
jal puts
j done
Just focusing on the problem line, I see this:
lw $s0, 224($sp) # copy address of first string inside $s0
...
putsString:
lbu $t3, ($s0) # load ccurrent char into #t3
So you've loaded $s0 with the contents of the memory starting 224 bytes past $sp. If I'm understanding correctly, that's the first four bytes of a string. Meaning, if the string is "Madam, I'm Adam", that $s0 contains 'a' 'd' 'a' 'M' (if I've got my endianness right).
So when you do lbu $t3, ($s0), you're not loading the current character into $t3. You're loading the byte pointed to by $s0, which is going to be a random spot in memory. If your string is blank, $s0 might contain 0x00000000 - and thus would generate the error you're getting.
I think what you want to do, instead of lw, is addi $s0, $sp, 224. Then, $s0 will point to the address at $sp + 224.