Swapping every two characters in a string and fixing exception error - mips

I'm getting an exception error and I'm not sure why. I need some help with the fix for the exception and how to swap chars in the user inputted string?
The input and output should look something like this:
Input : Apples
Output : pAlpse
.text
main:
#Prompt user for string
la $a0, promptStr
li $v0, 4
syscall
#Get String
li $v0,8
la $a0, buffer
li $a1, 20
syscall
move $t0, $a0
#Initialize pointer to start of string (specify register)
la $t0, buffer
#Get a char from the string (register)
loop: lb $t1, ($t0)
I'm getting Instruction references undefined here and I have no idea why because I've used it in similar problems to branch out of a loop when at the end of a string.
Here's the error message
Instruction references undefined symbol at 0x00400058
[0x00400058] 0x11200000 beq $9, $0, 0 [strEnd-0x00400058]; 48: beqz $t1, strEnd
#IF at end of the string branch to endStr
beqz $t0, strEnd
#Add 1 to the string pointer
add $t0, 1#Fixed
#get the next char in the string
lb $t2, ($t0)
I don't know if this code is right. I just need help understanding
how to swap chars and what the code will look like.
#Swap the 2 chars by writing them back to the original string
sb $t2, ($t0)
sb $t1, ($t0)
#Add 1 to the string pointer
add $t0, 1 #Fixed
#Jump back to loopStart
j loop
#Display modified string
endStr: la $a0,ans
li $v0, 4
syscall
move $a0,$t2
li $v0, 4
syscall
#Blankline
la $a0, end
li $v0, 4
syscall
#Exit porgram
li $v0, 10
syscall
.data
buffer: .space 20
promptStr: .asciiz "Input a string : "
blankLine : .asciiz "\n"

The issue with your error beqz $t0, strEnd is a typo: the label you meant is endStr. The error itself tells you this, highlighting [strEnd-0x00400058] as undefined. Additionally, spim warns me on load:
The following symbols are undefined:
end
ans
After fixing the missing labels, the logic is not quite correct, although it looks like you're on the right track. The idea is to step over the string in character pairs, swapping elements and exiting when hitting a null character (and, optionally, a newline depending on how you want to handle that--stripping/chomping it is probably best).
For starters and a rather minor point, there is a dead instruction near the top of your program:
move $t0, $a0 # $t0 will be overwritten by the next la
#Initialize pointer to start of string (specify register)
la $t0, buffer
Stepping into the loop and the main logic, the code beqz $t0, strEnd uses the address of the string which will never be 0 as the branch condition rather than the byte at that address, $t1. This gives an infinite loop.
Next, there is a problem with $t0, the pointer that walks the string. The logic
sb $t2, ($t0)
sb $t1, ($t0)
doesn't work because $t0 was already incremented so the code loses track of the previous byte address after
#Add 1 to the string pointer
add $t0, 1#Fixed
A solution is to store $t0 in a temporary register before any add $0, 1 operations. Something like:
move $t3 $t0 # save the address of buf for swap
# ... later on, after incrementing `$t0` ...
sb $t2, ($t3)
sb $t1, ($t0)
You could also use an indexing strategy here by adding/subtracting offsets or using an index to walk the string.
Lastly, I'm not sure what move $a0,$t2 should do towards the end of the program when you're printing.
Here's one possible solution that addresses these issues and generally cleans up the logic:
.text
main:
# prompt user for string
la $a0 prompt
li $v0 4
syscall
# get string
la $a0 buffer
li $a1 20
li $v0 8
syscall
move $s0 $a0 # incrementable pointer to buf
loop:
# t1 = *(buf++) and exit if '\0' or '\n'
move $t0 $s0 # save the address of buf for swap
lb $t1 ($t0) # t1 = *buf
beqz $t1 end # break if '\0'
beq $t1 10 end # break if '\n'
add $s0 1 # buf++
# t2 = *buf and exit if '\0' or '\n'
lb $t2 ($s0) # t2 = *buf
beqz $t2 end # break if '\0'
beq $t2 10 end # break if '\n'
# swap the chars
sb $t2 ($t0) # *prev_buf = curr_buff_char
sb $t1 ($s0) # *buf = prev_buff_char
# increment pointer and continue to the next pair
add $s0 1 # buf++
j loop
end:
# display modified string
la $a0 buffer
li $v0 4
syscall
# exit program
li $v0 10
syscall
.data
buffer: .space 20
prompt: .asciiz "Input a string : "
Sample runs:
$ spim -f swap_alternating_chars.s
Input a string : Apple
pAlpe
$ spim -f swap_alternating_chars.s
Input a string : Apples
pAlpse

Related

How to count the number of words and print the result in mips?

So I am fairly new to mips and am trying to do a hmwk.
The exercise is to create a block of words with 0 as the last word , to count them knowing we don't consider the last element 0 and print out the result.
Here is my code for the moment
.data
blockOfWords: .word 12,43,549,7,60,0
.text
la $a0, blockOfWords #putting address of blockOfWords in $a0
loopStart: sll $t1, $s3,2 #iterates thru blockOfWords by jumping by 4 bytes, $t1=4*i
add $t1,$t1,$a0 #we increment the address by four so as to advance in the array after every iteration
lw $a1, 0($t1) #$a1=blockOfWOrds[i]
beq $a1,$zero, Exit #if the value in $a1[i]==0, exit the loop
addi $s0,$s0,1 #else, increment by one the cnt=$s0
j loopStart #and continue looping thru blockOfWords
Exit:
#how do i print the result?
So instead of doing a shift left logical it is a lot easier to up the memory address in the array
In this way the lw $a1, 0($a0) is just grabbing the beginning of the word
it is then incremented by 4 after a check for the 0. This makes the function much simpler and easier to read.
.data
blockOfWords: .word 12,43,549,7,60,0
.text
main:
la $a0, blockOfWords # Load beginning address into $a0
loopStart:
lw $a1, 0($a0) # load the value at the beginning of $a0 into $a1
beq $a1,$zero, exit # Check if $a1 is 0, if so jump to exit
addi $s0,$s0,1 # Add one to the count
addi $a0, $a0, 4 # Up the intial index of the array by 4, up one word
j loopStart # Re-loop
exit:
li $v0, 1 # Load the value of 1 into the $v0 register
la $a0, ($s0) # Load the counter into the $a0 address
syscall
li $v0, 10 # A value 10 syscall to indicate end of program
syscall

Understanding how string is stored in MIPS and how to count the number of characters in the string

I am a beginner in MIPS. To my understanding string is stored using directive .asciiz in MIPS, and each character in the string is stored in a byte. In order to obtain a specific character code (decimal) in the MIPS program, I will have to use lb (load byte) instruction, and specify the byte position of the string to get back the character decimal stored in that byte.
.text
main:
la $t0, str
move $a0, $t0
li $v0, 4 #print string
syscall
# ---------------------------
lb $a0, 0(t0) #to obtain the 1st character
li $v0, 1 #print it
syscall
# ---------------------------
li $v0, 10
syscall
.data
str: .asciiz "abcde"
If I need a program to count the number of characters in a string, the addi instruction is used which I don't seem to get it, shown in the program bellow :
.text
main:
la $t0, str
la $t1, 0 #counter
lb $a0, 0($t0) #set $a0 to 1st character in str
# ------------------------------
loop: beqz $a0, breakout #if character is empty = 0 (decimal) breakout
addi $t0, $t0, 1 # why?
addi $t1, $t1, 1 # to increment counter
lb $a0, 0($t0) #loads the first character in $t0
j loop
# ------------------------------
breakout: move $a0, $t1
li $v0, 1 #print counter
syscall
li $v0, 10 #system exit
syscall
.data
str: .asciiz "abcde"
I assume that "addi $t0, $t0, 1" will increment the address of $t0 by 1 from 268500992 to 268500993. Does that mean that an increment in the address by one will allow me to find the next character in the string, and that the increment of 1 in the address is equivalent to an increment of a byte in the address containing the string?
Many thanks!

Override string MIPS

Have to copy the second string in the first, when the strings are the same size right but when the second is less complete with characters from the first in that space over. Follows the code. I am grateful if someone help.
.data
string1: .asciiz "Hello"
string2: .asciiz "Word"
finalStr: .space 256 # A 256 bytes buffer
buffer: .space 100
.text
main:
la $s1, finalStr
la $s2, string1
la $s3, string2
copySecondString:
lb $t0, ($s3) # get character at address
beqz $t0, exit
sb $t0, ($s2) # else store current character in the buffer
addi $s3, $s3, 1 # string1 pointer points a position forward
addi $s2, $s2, 1 # same for finalStr pointer
j copySecondString # loop
exit:
la $a0, string1
li $v0,4
syscall
li $v0, 10
syscall
The problem here is that you are not null terminating the string and hence it is bleeding over into the characters left over when the first string is longer than the second.
To fix this, move the check for null after the store instruction:
lb $t0, ($s3) # get character at address
sb $t0, ($s2) # store current character in the buffer
beqz $t0, exit # exit if null

MIPS: Selective copying from one string to another error

I have tried to write a program in MIPS that takes a string and prints a result with only characters (assuming that the string is alphanumerical). It works perfectly for strings of size < 4, but when a 4th character occurs, it goes into infinite loop.
.data
string: .word 10
.data
result: .word 10
.data
message1: .asciiz "number\n"
.data
message2: .asciiz "letter "
.data
message3: .asciiz "finished loop\n"
.text
main:
li $v0 8 # take input as string
la $a0 string # store it in "string"
la $a1 10 # size of "string" is at most 10
syscall
la $s0 string # save address of "string" to s0
la $s1 result # save address of "result" to s1
Loop:
li $t0 10 # set t0 as '/n'
lb $t1 ($s0) # load character that we are currently checking
beq $t0 $t1 Exit # check if we are at the end of the string
li $t0 64 # set t0 to 64 to check if it is a letter (character that we are now checking must be greater than 64)
slt $t2 $t0 $t1 # t2 will store the result of comparison ($t1 - character)
li $t0 0 # set t0 to 0 to check the result of comparison
beq $t2 $t0 Increment # if 64 > ch, then we must just proceed
li $v0 4
la $a0 message2 # print message that it is a character
syscall
sb $t1 ($s1) # copy this character into our "result"
addi $s1 $s1 1 # increment the address of "result"
Increment:
li $v0 4
la $a0 message1 # print message that it is a number
syscall
addi $s0 $s0 1 # increment the address of "string" to proceed in loop
j Loop
Exit:
li $t0 10
sb $t0 ($s1) # add endline character to "result"
addi $s1 $s1 1
li $t0 0
sb $t0 ($s1) # add null character to "result"
li $v0 4
la $a0 message3 # print message that the loop has finished
syscall
li $v0 4
la $a0 result # print result
syscall
jr $ra
Thanks in advance.
Hard to tell without stepping through myself, but here's an idea: use the power of "i", that is, Immediate mode. That way, you don't have to worry about whether a register value isn't quite what you expected. Also, use the special $zero register instead of loading a register with zero. So change this:
li $t0 64 # set t0 to 64 to check if it is a letter (character that we are now checking must be greater than 64)
slt $t2 $t0 $t1 # t2 will store the result of comparison ($t1 - character)
li $t0 0 # set t0 to 0 to check the result of comparison
beq $t2 $t0 Increment # if 64 > ch, then we must just proceed
to this:
slti $t2, $t1, 'A' # A little different: set $t2 if $t1 < 65 ('A')
bne $t2, $zero Increment # branch if $t1 < 'A'
Maybe by eliminating registers in favor of immediates, you can find the glitch that's causing you to go into a loop.
Also, a couple of other possible glitch sources:
You're looking for '\n' to terminate the string, but I'm not sure you can guarantee that the syscall will add \n if the input is a full 10 characters. Perhaps you should also/instead check for the zero-terminator byte value.
You've defined string and result as 10 words, but you're actually storing bytes in them. That shouldn't be a problem - you've allocated 40 bytes instead of 10. But if you ever go back and change those to byte, be sure to allow for that trailing \0 (and \n).
Sorry I can't be more specific, but hope that points you in the right direction.

I keep getting the bad address exception, any knows why?

I'm trying to do a simple program that counts the number of characters in a string . It seems I follow the structure but I keep getting the same bad address error, anyone knows why?
.data
array: .space 100
prompt1: .asciiz " Enter the string that you would like to reverse and calculate character: "
prompt2: .asciiz " "
.text
main:
la $a1, array # allocate the array space
ask:
li $v0, 4 # print String
la $a0, prompt1 # load prompt1
syscall
li $v0, 8 # read the string
syscall
move $a1, $v0 # move input to $a1
li $t0 ,0 # $t0 will hold the actual numbers of character in the string
loopCount:
lbu $t1, 0($a1) # load the next character into t1
beqz $t1, print # check for the null character
addi $a1, $a1, 1 # increment the string pointer
addi $t0, $t0, 1 # increment the count
b loopCount # return to the top of the loop
print:
li $v0, 1
move $a0, $t0
syscall
You should load the address of your buffer in $a0 and the size of the buffer in $a1 before the syscall to read the string.