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.
Related
So I have a program that takes an input from the user (integer above 0) and adds up all even numbers below it to achieve a return answer (Ex: input: 7; ans: 2 + 4 + 6 = 12).
The issue with this program is that it's meant to break out of the loop based on if my 'active even variable' ($t1) > the input. Although my program never seems to properly interpret the branch and loops indefinitely until $t1 overflows (I have checked the debugger and know that the program does run the branch line every time). Below is my code:
.data
N: .word 0
Result: .word 0
.text
.globl main
initialize:
li $v0, 5 #getting arg1 from user
syscall
la $t0, N
sw $v0, 0($t0)
li $t1, 2
li $t2, 0
main:
blt $t0, $t1, fin2
fori:
add $t2, $t2, $t1 #t2 += t1
add $t1, $t1, 2 #t1 += 2
slt $t5, $t1, $t0
bne $t5, $zero, fori
fin:
li $v0,1 #prints return value
move $a0, $t2
syscall
li $v0, 10
syscall
fin2:
li $v0,1 #prints return value
move $a0, $zero
syscall
li $v0, 10
syscall
So I don't know if you NEED to be using word storage and such, but really you were just over complicating it in doing so. All you needed was a simple loop which has a counter that increments by 2, checks if it is larger than the initial value, and then add that overall value to the result
.text
.globl main
initialize:
li $v0, 5 # Getting arg1 from user
syscall # System call
move $t0, $v0 # Store the input value in $t0
li $t1, 0 # Initializing the result register
li $t2, 0 # Initializing the addition/counter register
main:
loop:
add $t2, $t2, 2 # Increase the value to be added by 2 (next even value)
bge $t2, $t0, fin # Check if the increment is larger than or equal to the initial input, if so break to finish
add $t1, $t1, $t2 # Increment the result by adding the even value
j loop # jump bak to the top of the loop
fin:
li $v0,1 # let the system know an integer is going to be printed
move $a0, $t1 # Load the result into the $a0 register (the register that prints values)
syscall # System Call
li $v0, 10 # Let the system know the program is going to exit
syscall # System Call
So as you can see $t2 increments by 2 each time. After each increment it is compared to the input value. If the input ($t0) than $t2 then add the value of $t2 to the result ($t1). This way there is an increment counter which is also used to add the necessary even value to the result.
Edit:
Not sure if this is entirely what you meant but I just tossed in some loads and saves, using the s registers as those are the register that are supposed to be used when saving values.
.data
N: .word 0
Result: .word 0
.text
.globl main
initialize:
li $v0, 5 # Getting arg1 from user
syscall # System Call
la $s0, N # Load the address of N into $s0
sw $v0, 0($s0) # Store the input value in 0 index of N
li $t2, 0 # Initializing the addition/counter register
la $s1, Result # Load the address of Result into $s1
main:
sw $t2, 0($s1) # Setting the 0 index of Result to 0
loop:
add $t2, $t2, 2 # Increase the value to be added by 2 (next even value)
lw $t4, 0($s0) # Loading the input value into the $t4 register
bge $t2, $t4, fin # Check if the increment is larger than or equal to the initial input, if so break to finish
lw $t4, 0($s1) # Loading the current result into the $t4 register
add $t4, $t4, $t2 # Increment the result by adding the even value
sw $t4, 0($s1) # Saving the new current result into the $t4 register
j loop # jump bak to the top of the loop
fin:
li $v0,1 # let the system know an integer is going to be printed
lw $a0, 0($s1) # Load the result into the $a0 register (the register that prints values)
syscall # System Call
li $v0, 10 # Let the system know the program is going to exit
syscall # System Call
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
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!
I have an unknown output error after entering 2nd value which is after entering base number.
Hopefully,some of you could identify my error:
ERROR:Instruction references undefined symbol at 0x00400060
[0x00400060] 0x102a0000 beq $1, $10, 0 [hex-0x0040005c]
PROGRESS:Currently stucked at Step 2.
What i want to do is,
1)User enter a decimal value
2)User enter type of conversion
3)Go to desired subroutine depending on type of conversion chosen earlier
4)Display output
.data
prompt: .asciiz "Enter the decimal number to convert: "
base: .asciiz "Select type of base (2 for binary,16 for hexadecimal or 8 for octal): "
ans1: .asciiz "\nBinary Output is equivalent:"
ans2: .asciiz "\nOctal Output is equivalent:"
ans3: .asciiz "\nHexadecimal Output equivalent:0x"
result1: .space 8
.text
.globl main
main:
la $a0, prompt #Display message
li $v0, 4
syscall
li $v0, 5
syscall
beq $v0, $zero, Exit #Exit if 0 decimal is entered
move $t0, $v0 #Else copy value entered into temporaries
askbase:
li $v0, 4
la $a0, base #Display message
syscall
li $v0, 5
syscall
add $t1,$zero,$v0 #Add desired value/base entered into t1
beq $t2,16,hex #if base 16 is entered,goto hex subroutine
beq $t2,8,oct
beq $t2,2,bin
la $a0, ans3
li $v0, 4
syscall
li $t0, 8 # counter
la $t3, result1 # where answer will be stored
Hex:
beqz $t0, Exit # branch to exit if counter is equal to zero
rol $t2, $t2, 4 # rotate 4 bits to the left
and $t4, $t2, 0xf # mask with 1111
ble $t4, 9, Sum # if less than or equal to nine, branch to sum
addi $t4, $t4, 55 # if greater than nine, add 55
b End
Sum:
addi $t4, $t4, 48 # add 48 to result
End:
sb $t4, 0($t3) # store hex digit into result
addi $t3, $t3, 1 # increment address counter
addi $t0, $t0, -1 # decrement loop counter
j Loop
Exit: la $a0, result1
li $v0, 4
syscall
la $v0, 10
syscall
It looks like you have a typo at line 29 beq $t2,16,hex should be beq $t2,16,Hex. Note the capital letter on Hex. You also have a number of undefined labels: Loop, oct, bin... Without those labels you are going to have issues. It is a good idea to just set some up as place holders (until you have their subroutines defined). Maybe have them all branch to Exit:. The assembler cannot resolve your branch instructions without having the actual labels to go to.
You haven't stored anything in $t7, so there's no particular reason to expect that $t7 would equal 16.
What you probably wanted to write is:
beq $t1,16,hex
Since $t1 is the register that you stored the base in.
However, I really don't see why you would want that jump with the way the code currently is structured. The hex subroutine relies on a few registers (like $t0 and $t3) to have been initialized to certain values, and that initialization would be skipped if that beq is taken.
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