MIPS - More than 4 strings as parameters - string

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.

Related

Finding number of character occurrences in string (mips 32)

I'm writing a code that takes a string and a character from user as input, Then finds number of occurrences of the character in the string.
Here is my attempt:
.data
string: .space 100 # allc space for string
.text
main:
la $a0, string # input string
li $a1, 100 # maximum size of string
li $v0, 8 # represents reading string
syscall # call system
la $s0, 0($a0) # $s0 contains address of first element of the string
li $v0, 12 # 12 represents reading character
syscall # call system
move $s2, $v0 # $s2= character
li $s1, 100 # $s1= maximum size of string
li $t4, 0 # count (number of occurrances)
li $t0, 0 # i(index)
loop:
bge $t0, $s1, print # i>= string length, exit
add $t1, $s0, $t0 # &A[i]
lb $t2, 0($t1) # A[i]
bne $t2, $s2, skip # skip increasing the counter if item does not equal
add $t4, $t4, 1 # otherwise increment the counter
skip:
add $t0, $t0, 1 # increment index
j loop # go back to loop
print: # print the result
addi $v0, $t4, 0 # $v0= $v4= number of occurrences
li $v0,1 # 1 represents printing integer
syscall # call system
# Terminate the program
li $v0, 10 # 10 represents exit
syscall # call system
But my code is not working properly. It prints a very large number instead of number of occurrences. I even checked it for string with the same size as the space allocated. But it still gives wrong result. Is this problem related to length of string or something else? Please help me debug it.
Try this code :
.data
string: .space 100 # allc space for string
.text
main:
la $a0, string # input string
li $a1, 100 # maximum size of string
li $v0, 8 # represents reading string
syscall # call system
li $v0, 12 # 12 represents reading character
syscall # call system
move $s2, $v0 # $s2= character
li $s1, 100 # $s1= maximum size of string
li $t4, 0 # count (number of occurrances)
li $t0, 0 # i(index)
bge $t0, $s1, print # i>= string length, exit
loop:
lb $s0,($a0) # the first character of string into $s0
bne $s0, $s2, skip # skip increasing the counter if item does not equal
add $t4, $t4, 1 # otherwise increment the counter
skip:
addiu $a0, $a0, 1 # increment index
beq $s0,0,print # go to print label if $s0=0 so $s0 ='\0'
j loop # go back to loop
print: # print the result
#addi $v0, $t4, 0 # $v0= $v4= number of occurrences
li $v0,1 # 1 represents printing integer
move $a0,$t4 # $a0=$t4 = number of occurances of character
syscall # call system
# Terminate the program
li $v0, 10 # 10 represents exit
syscall # call system

Call a subroutine on each character in string - MIPS

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

Searching for a word in a sentence in MIPS

I hope you're all having a great day. I was hoping i could get some assistance with my project code. Basically a sentence "FADED IN FADED OUT." is type in when prompted and then a search word "FADED" is typed in and the code is executed to see if the word "FADED" is in the sentence and if it is it will say "x Match(es) Found" and if not "No Match(es) Found". Well when I compile and run it gives me a "line 65: Runtime exception at 0x00400098: address out of range 0x00000000" error and there are multiple lines that have this error. Would anyone be able to assist me with this? I have been trying to do it the past 3 days and finally broke for some help... If you have any questions please let me know!
.data
str: .space 100 # Pre Allocate space for the input sentence
input: .space 30 # Pre Allocate space for the input sentence
ins: .asciiz "Please enter a sentence: " # string to print sentence
seek: .asciiz "Please enter a word: " # string to print sentence
nomatch: .asciiz "No Match(es) Found"
found: .asciiz " Match(es) Found"
newline: .asciiz "\n" # string to print newline
.text
li $v0, 4 # syscall to print string
la $a0, ins # move str into a0
syscall # syscall
li $a1, 100 # allocate space for the string
la $a0, str # load address of the input string
li, $v0, 8 # read string input from user
syscall # issue a system call
move $t9, $a0 # move string to t5
li $v0, 4 # syscall to print string
la $a0, seek # move str into a0
syscall # syscall
la $a0, input # load address of the input string
li $a2, 30 # allocate space for the string
li, $v0, 8 # read string input from user
syscall # issue a system call
move $t8, $a0 # move string to t8
la $s5, input # create space for the input word we are looking for in s5
wloop: # loop to allocate space for the word we are looking for to a register
lb $t0, 0($t8) # load first character into t0
beqz $t0, sentence # branch to sentence loop if null character
sb $t0, 0($s5) # store the current character into current address of s5
addi $t8, $t8, 1 # add one to t8 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to the next allocated space
j wloop # jump back to wloop
la $s4, str # create space for the input sentence
sentence: # loop to allocate space for the word we are looking for into a register
lb $t0, 0($t9) # load first character into t0
beqz $t0, resetsen # branch to check loop if null character
sb $t9, 0($s4) # store the current character into current address of s4
addi $t9, $t9, 1 # add one to t9 to move to next character
addi $s4, $s4, 1 # add one to s5 to move to the next allocated space
j sentence # jump back to sentence
resetsen:
li $s4, 0 # reset sentence back to 0 (first character)
resetword:
li $s5, 0 # reset word we are looking for back to 0 (first character)
check:
lb $t1, 0($s4) # load current character of sentence to t1
beq $t1, 46, quit # branch to QUIT if period found
bne $t1, 70, nextword # if t1 != t0 branch to nextword
beq $t1, 70, checkword # branch to found if t1 = f
nextword: # loop to get to the next word
lb $t1, 0($s4) # load current character to t1
beq $t1, 46, quit # branch to quit if period found
bne $t1, 32, increment # if current character is not a spaace branch to increment
beq $t1, 32, plusone # if current character is a space branch to plusone
increment: # increment procedure
addi $s4, $s4, 1 # add one to s4 to move to next character
j nextword # jump to nextword
plusone: # plusone procedure
addi $s4, $s4, 1 # add one to s4 to move to next character
j resetword # jump to check
checkword:
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for a)
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for d)
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for e)
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for d)
addi $t2, $t2, 1 # add one to t2 which counts occurences
j resetword
quit:
beqz $t2, exit # if t2 = 0 branch to exit
li $v0, 1 # syscall to print integer
move $a0, $t2 # move str into a0
syscall # syscall
li $v0, 4 # syscall to print string
la $a0, found # move found into a0
syscall # syscall
j endprogram
exit:
li $v0, 4 # syscall to print string
la $a0, nomatch # move nomatch into a0
syscall # syscall
endprogram:
li $v0, 10
syscall

Checksum assembly algorithm not adding up to correct value

I'm working on implementing a basic checksum algorithm for an inputted string in MIPS assembly as a general introduction to working in the language, and I could use some error checking.
Here's what I have so far, comments included to keep track of what's happening each step:
.data
str1:
.asciiz "This is a short string."
cs1:
.word 0x84a
str2:
.asciiz "This is a much longer string. In fact, it has two sentences in it, and some funny characters (~`)€."
cs2:
.word 0x230a
NonSuccessString:
.asciiz "You have more work to do."
Test1Success:
.asciiz "Test 1 was successful. "
Test2Success:
.asciiz "Test 2 was successful. "
.text
la $a0, str1 #Set $a0 to address of label str1:
jal checksum #Set $ra to address of next instruction, then jump to label checksum:
la $s0, cs1 #Set $s0 to address of label cs1:
lw $s1, 0($s0) #Set $s1 to contents of memory address $s0
beq $v0, $s1, Success1 # If $v0 and $sl are equal, jump to label Success1:
j NonSuccess # Jump to label NonSuccess:
Success1:
la $a0, Test1Success #Set $a0 to address of label Test1Success:
addi $v0, $zero, 4
syscall
la $a0, str2 #Set $a0 to address of label str2:
jal checksum #Set $ra to return address, then jump to label checksum:
la $s0, cs2 #Set $s0 to address of label cs2:
lw $s1, 0($s0) #Set $s1 to contents of memory address $s0
beq $v0, $s1, Success2 # If $v0 and $sl are equal, jump to label Success2:
j NonSuccess # Jump to label NonSuccess:
Success2:
la $a0, Test2Success #Set $a0 to address of label Test2Success:
addi $v0, $zero, 4
syscall
j Quit # Jump to label Quit:
NonSuccess:
la $a0, NonSuccessString #Set $a0 to address of label NonSuccessString:
addi $v0, $zero, 4
syscall
Quit:
addi $v0, $zero, 10
syscall
checksum:
addi $sp, $sp, -4 # adjust stack for 1 item
sw $s0, 0($sp) # save $s0
add $s0, $zero, $zero # i = 0
L1:
add $t1, $s0, $a0 # Locate the character at y[i]
lbu $t2, 0($t1) # Load the unsigned byte value of y[i]
beq $t2, $zero, L2 # exit loop if y[i] == 0
add $v0, $v0, $t2 # Add the byte to the total value
addi $s0, $s0, 1 # i = i + 1
j L1
L2:
jr $ra #jump back to $ra
The simple string in the first test calculates correctly and I get the first "Test 1 was successful." message, but on the second checksum the debug reports a sum of 0x230e, 4 more than the expected value, meaning something wonky happened. I'm guessing the special characters are probably causing the issue, but I don't know for sure. Any ideas?
You're not resetting $v0 prior to calculating the checksum. There should be an add $v0,$zero,$zero before the L1: label.
But I still don't see where the value 0x230a comes from. It seems to me like the correct checksum for the second string is 0x22de.

Reversing an int string in MIPS

I am attempting to reverse a string of ints when given size and string. Here is what I have so far. I know my problem is in the loop section, mainly in figuring out how to get a pointer to point at the back of the string and move what is contained to where it should go
Any help would be appreciated:
.data
Size: .word 9 # Size is 9
Vals: .word 1, 2, 3, 4, 5, 6, 7, 8, 9 # Vals[9]={1,2,..}
.text
main:
la $t1, Size # get the address of variable Size
la $t2, Vals # get the address of variable Vals
lw $t3, 0($t1) # fetch the value of Size to register $t3
sra $t4, $t3, 1 # right shift arithematic, now $t4 contains Size/2
sll $t3, $t3, 2 # left shift logic, now $t3 contains Size*4
loop:
lb $t5, 0($t2)
sb $t0, 36($t2)
sb $t0, ($t5)
addi $t0, $t0, 4
addi $t5, $t5, -4
bne $t5, $t4, end
j loop
end:
la $t0, Vals # get the address of Vals to $t0
la $t1, Size # get the address of Size to $t1
lw $t3, 0($t1) # get Size to $t3
sll $t3, $t3, 2 # left shift logic, now $t3 contains Size*4
add $t1, $t0, $t3 # $t1=Vals+Size*4 => array bound
li $v0, 1 # service 1 is print integer
lab4:
lw $a0, 0($t0) # load desired value into argument register $a0
syscall # print the value in $a0
addi $t0, $t0, 4 # increase array index
bne $t0, $t1, lab4 # check if reach array bound
There are some issues in that loop:
You are reading bytes instead of words (use lw/sw instead of lb/sb
You are mixing contents with addresses (usage of register $t5)
You are adding/subtracting 4 to the contents read instead of the pointers to the array
The way you are trying to reverse the array seems wrong as adding 36 (=9x4) would point to a location after the end of the array
What you should do is use 2 pointers: one which starts pointing to the beginning of the array and another that starts pointing to the end of the array, then read both items and interchange its contents. Now move the the next element in each pointer (one pointer moves ahead and the other move behind), and repeat the process until all the items are processed (that is, when both pointers crosses).
E.g (snip):
addu $t7, $t2, $t3 # $t5 contains address of end of string
loop:
addi $t7, $t7, -4 # moves pointer backwards
lw $t5, 0($t2)
lw $t0, 0($t7)
sw $t5, ($t7) # interchange
sw $t0, ($t2) # contents
addi $t2, $t2, 4 # moves pointer forward
ble $t2, $t7, loop

Resources