I am writing a program that takes a Roman Numeral (up to 12 characters) and converts it to a decimal value. I am able to do this conversion successfully and read each value character by character, however my value is always 1000 greater than actual when the inputted string is not 12 characters, which I've determined is due to an extra loop through the evaluate subroutine (if no matches are made, it enters the process for converting M = 1000).
I assume it has something to do with the way I reading the user's string when less than 12 characters. It is my understanding that the read string system call code proceeds until it reaches a '\n' character, and converts that character and the rest of its empty space to 0 characters, so checking if the next character is not equal to 0 should work as I do not expect 0 to be in the user's input. I do realize I can fix my code by jumping to tailLoop if no match is made during "evaluate", but I would like to understand why the code I have currently isn't working.
The following code does not contain much of my program (would be quite long to post in full), only enough to understand the logic processes I am trying to use. This is homework in case it flavors how you want to answer the question.
# Program to translate Roman Numerals to decimal values
.data
numeralString:
.space 13
# End Strings
.text # Begin Program
.globl main
main:
li $v0, 8 # read string from user at next syscall
la $a0, numeralString
li $a1, 13
syscall
la $t0, numeralString # take input string and store in $t0
move $t1, $t0 # creating a copy of base register
lb $t2, 0($t0) # loads first byte (character) into $t2
jal evaluate # calls evaluate subroutine. String passed via $t0 (first byte/character specified in $t2)
# interger value returned via $v1
li $v0, 1 # print result value from $v1 at next syscall
move $a0, $v1
syscall
exit: # Exit Program in next syscall
li $v0, 10 # exit program in next syscall
syscall
evaluate: # matches numeral in string to correct subroutine
lb $t3, 1($t0)
beq $t2, 'M', mChar
mChar:
addi $v1, $v1, 1000
# beq $t3, 'C', cSlot
j tailLoop
tailLoop:
move $t2, $t3 # $t2 now holds next character of string
addi $t0, $t0, 1
bnez $t2, evaluate # Go back to evaluate if next character exists
jr $ra
It is my understanding that the read string system call code proceeds until it reaches a '\n' character, and converts that character and the rest of its empty space to 0 characters
No.
If you look at the spim source code, you'll see that the read_input function which is called in case of a syscall
Simulate[s] the semantics of fgets (not gets) on Unix file.
According to CPlusPlus.com:
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
This is confirmed by examing the spim code.
Related
Dynamic memory allocation and strings
Write a program that applies run length encoding compression to a given string. Follow the below steps:
Dynamically allocate memory from heap for two strings (an input string and an output string).
Read an input string (a-z, A-Z) of size less than 40 characters in this dynamic memory.
Iterate over the string looking for consecutive occurrences of the same character and replace
them with the character and a count.
Example, if the input string is AAAAAA, the output string should be A6. If you see
BBCCCCCCCCCCCCC, the output should be B2C13.
Note that the number 13 is represented as two separate characters ‘1’ and ‘3’ in the output string.
Single character occurrences do not need a count.
The output string should also be stored in the dynamic memory before it is displayed on the
console.
Implement a loop back to the main function. See the prompts below:
“Enter a string:”
“Compressed string is:”
“Do you want to apply run length encoding again?”
Test the program using the following data:
a. Input: AAAAaaabbcde Compressed string: A4a3b2cde
b. Input: XXXXXXXXXXYYZZZWERTT Compressed string: X10Y2Z3WERT2
.data
Prompt1: .asciiz "\n Enter a string."
.text
main:
li $v0, 4
la $a0, Prompt1
syscall
li $v0, 9
li $a0, 80
syscall
move $s0, $v0
li $v0, 8
la $a0, 0($s0)
li $a1, 40
syscall
move $t0, $a0
li $t4, 0
Loop:
lb $t1, 0($t0)
sb $t1, 40($s0)
addi $t0, $t0, 1
addi $t4, $t4, 1
addi $s0, $s0, 1
beq $t1, 0, End
j Loop
End:
neg $t4, $t4
add $s0, $s0, $t4
li $v0, 4
la $a0, 40($s0)
syscall
Generally, the algorithm should look something like this:
Get the pointers to your input string and your output buffer.
Loop:
Read the nth letter.
If it's the null terminator, exit.
Read the (n+1) th letter. If it's the null terminator, GOTO 5. If it's the same as the nth letter, GOTO 6.
Otherwise, add 1 to your index into the input string and GOTO 2.
Compress:
Create a counter variable for how many times you've seen the same letter in a row. It should start at two since you've seen two letters that are the same.
Add 2 to your index.
Compress_Loop:
Read the nth letter (remember, we added 2 to n in step 6.)
If it's not the same letter we've saw in Step 4, GOTO 11. Otherwise, continue.
Add 1 to your index into the input string, and add 1 to the counter variable, then GOTO 8.
Store the letter we saw in Step 4 into the output string.
Convert the counter variable to a string, and store it in the output string.
Adjust your index into the output string past what you just wrote.
GOTO 2.
I am having a hard time figuring out where to start with this project. I am needing to write code in PLP that is a palindrome checker.
the task is to write a program that recieves a string of characters via UART, checks if this string is a palindrome, then uses a print function to print either"yes" of "no". I have been given a template that I am to follow when creating the program.
The template project file contains six function stubs that need to be implemented. five are called from the main loop and the sixth is called from "period_check: in the template file it contains descriptions of what each function needs to do and how it should be implemented. I have attempted to fill in some, however I do not think I am on the right track. Please help.
***** I have gotten this much code in, but it does not print out the right output****
it prints no for everything vs no for non palindromes and yes for palindrome.
.org 0x10000000
# Initializations
# NOTE: You may add initializations after line 10, but please do not
# remove or change the initializations to $sp, $s0, $s1, or $s2
li $sp, 0x10fffffc # Starting address of empty stack
li $s0, 0xf0000000 # UART base address
li $s1, array_ptr # Array head pointer
li $s2, array_ptr # Array tail pointer
####################################################################
# Do not make changes to the jump to main, the allocation of
# memory for the array, or the main loop
####################################################################
j main
nop
array_ptr: # Label pointing to 100 word array
.space 100
main:
jal poll_UART
nop
jal period_check
nop
jal space_check
nop
jal case_check
nop
jal array_push
nop
j main
nop
####################################################################
# ******************************************************************
####################################################################
# The "poll_UART" function should poll the status register of the UART.
# If the 2^1 bit position (ready bit) is set to 1 then it
# should copy the receive buffer's value into $v0 and send
# a clear status command (2^1) to the command register before
# returning (a return statement is already included). In order to
# receive full credit, $s0 must contain the base address of the UART
# and must be used with the appropriate offsets to access UART
# registers and buffers
poll_UART:
lw $t1, 4($s0)
li $t2, 0b10
and $t3, $t1, $t2
beq $t3, $0, main
nop
lw $v0, 8($s0)
sw $t2, 0($s0)
jr $ra
nop
# The "period_check" function should check if the current character ($v0)
# is a period ("."). If it is a period then the function should go to the
# label, "palindrome_check". If the character is not a period then it
# should use the included return.
period_check:
li $t0, 0x2E
beq $v0, $t0, palindrome_check
nop
# The "space_check" function should check if the current character ($v0)
# is a space (" "). If it is then it should jump to "main" so
# that it skips saving the space character. If not it should
# use the included return.
space_check:
li $t4, 0x20
beq $t4, $v0, main
jr $ra
nop
# The "case_check" function should perform a single inequality check.
# If the current character ($v0) is greater than the ASCII value of 'Z',
# which indicates the current character is lowercase, then it should convert
# the value of $v0 to the uppercase equivalent and then return. If the
# current character ($v0) is already uppercase (meaning the inequality
# mentioned before was not true) then the function should return without
# performing a conversion.
case_check:
li $t5, 0x5A
slt $t6, $v0, $t5
li $t7, 1
beq $t6, $t7, convert
convert:
addiu $v0, $v0, -32
jr $ra
nop
# The "array_push" function should save the current character ($v0) to the
# current location of the tail pointer, $s2. Then it should increment the
# tail pointer so that it points to the next element of the array. Last
# it should use the included return statement.
array_push:
sw $v0, 0($s2)
addiu, $s2, $s2, 4
jr $ra
nop
# The "palindrome_check" subroutine should be jumped to by the period
# check function if a period is encountered. This subroutine should contain
# a loop that traverses the array from the front towards the back (using the
# head pointer, $s1) and from the back towards the front(using the tail
# pointer, $s2). If the string is a palindrome then as the array is traversed
# the characters pointed to should be equal. If the characters are not equal
# then the string is not a palindrome and the print function should be used
# to print "No". If the pointers cross (i.e. the head pointer's address is
# greater than or equal to the tail pointer's address) and the compared
# characters are equal then the string is a palindrome and "Yes" should be
# printed.
#
# Remember to restore the head and tail pointers to the first element
# of the array before the subroutine jumps back to main to begin processing the
# next string. Also, keep in mind that because the tail pointer is updated at
# the end of "array_push" it technically points one element past the last
# character in the array. You will need to compensate for this by either
# decrementing the pointer once at the start of the array or using an offset
# from this pointer's address.
palindrome_check:
addiu $s2, $s2, -8
move $s3, $s1
subu $s6, $s2, $s3
beq $s6, $0, palindrome
nop
check_loop:
lw $s4, 0($s3)
lw $s5, 0($s2)
bne $s5, $t0, not_palindrome
nop
adjust_pointers:
addiu $s2, $s2, -4
addiu $s3, $s3, 4
slt $t8, $s3, $s2
bne $t8, $t0, check_loop
nop
j palindrome
nop
palindrome:
li $a0, 1
call project3_print
move $s2, $s1
j main
not_palindrome:
li $a0, 0
call project3_print
move $s2, $s1
j main
nop
Ok, this is just my opinion, but you are definitely not on the right track.
The control flow you're showing is problematic.
To see one reason why, try writing this same in C or any other language that you know. You won't be able to do it because of the non-local goto's that's using, where one procedure jumps (without calling) to another procedure.
Further, finding whether an input is a palindrome is not a fixed sequence of one-time steps that are executed on each input character.
You will (1) need to store the characters for later comparison, and (2) need a decision point where you can determine (and print) yes it is, or no it isn't. You don't have any control structure for that.
that recieves a string of characters via UART, checks if this string is a palindrome, then uses a print function to print either"yes" of "no".
Yes, your main should reflect the above description you've been given:
receive a string of characters
checks if this string is a palindrome
print either "yes" of "no"
In other words you might have something like:
int len = input_string();
if ( check_palindrome(len) ) {
print "yes";
else
print "no"
Suggest you write it in C or other language you know, then translate that to assembly.
Also consider that we some things we program are functions returning a value rather than procedures that don't return values. Returning a value so that main can take a different course of action (e.g. print yes vs. no) is much better than using non-local goto's to alter the flow of control from within a subroutine.
If your instruction/coursework has given you that main, and is recommending non-local goto's that would be very sad.
I feel for you and your classmates, as this is one of the worst examples of teaching assembly I've seen in a long long time.
array_ptr: # Label pointing to 100 word array
.space 100
The label name is misleading. This space is used as an array of words, not a pointer to an array. The storage reserved is 25 words, since .space operates in terms of bytes and words are 4 bytes each. So, the comment is just plain wrong.
The various "functions" called using jal are single use function, so there's really no need for functions in this assignment at all. The "functions" also are going to each other and back to main instead of returning properly like they would in structured programming. So, this is what we call spaghetti code — such code is difficult to reason over and one of the reasons that other languages don't even bother to offer this kind of flow control.
The array being used is storing whole words, when the input elements are only characters, so that's harmless but unnecessary.
beq $t6, $t7, convert
convert:
This control structure will never choose between two options, it will always convert. Why? Because in the case $t6 is true it will branch to convert: and in the case that $t6 is not true it will fall through to convert:, so same location, will run same code in either case.
You should be able to observe this during debugging.
Debugging Tips
Get to know your data. You should know the address of the array as you debug. You can find this during execution, e.g. look at a register after li ... array_ptr (btw, that opcode should be la, but no matter if it works). Otherwise you can observe the data section and its layout to find that out before running the first instruction.
Single step each line like one would to debug code in any other language, verifying program state between each line. In MIPS assembly, not much program state changes between lines so usually this is pretty simple — usually each instruction only changes one register or one memory location — but you must verify that such change is as you're expecting. Once the first part of the program is properly storing characters into the array, you can use the break point feature to stop at the palindrome check routine and single step only from there on.
Use the smallest possible input first, (in the most degenerate case that would be an empty string, but you may not be handling those so instead) might try a single letter input (should be a palindrome). Once that is working, try two letter input. As I said, first make sure that the character values are being placed into the array properly, and only when you've verified that's working, go on to debug the palindrome check code.
I would like to print out "HEL" from the string "HELLO!". I am aware that I can load individual byte using lb in MIPS, however, how do I load the first 3 characters?
.data
string: .asciiz "HELLO!"
.text
lw $t6, string
lb $a0, 0-2($t6) # pseudo-code, returns "HEL"
li $v0, 4 # as opposed to 11, printing character
syscall
This has little to do with MIPS, and everything to do with C style nul-terminated strings, if you consider printing a character and whole string are your only options.
So, you can print the first 3 characters one a a time using syscall #11, or, copy them to another string and print it using syscall #4.
Otherwise, you can modify the string in place by smashing the 2nd L with a nul character, and print it.
Hello everybody friends :) I coding an exercise but I have found a problem and in two days I haven't found the solution... I explain better, this is my code:
main:
#first message
la $a0, m1
li $v0, 4
syscall
#space in the stack
addi $sp, $sp, -41
#read the input
move $a0, $sp
li $v0,8
li $a1, 41
syscall
#bge $a0, 41, error
...
My code, read 40 characters (41 with string terminator)to count the vocal letters inside a string and, if I insert 41 characters send me an error message and repeat the stamp of the menu and the relative input, where is the problem? If I use this code without the control --> bge $a0, 41, error --> everything works fine, I can insert 1-40 characters with and without a space, when I insert this control give me the error that I have set when the string is 40+ character (see below to understand better)
Example with control:
input: hello(click return) ERROR: the string is bigger then 40 character
input: h ello(click return) ERROR: the string is bigger then 40 character
Any help or advice is the welcome :)
Thank you all, have a nice day!
P.s.
Sorry for my bad english
bge $a0, 41, error makes no sense here. System call 8 doesn't return a result, it just stores the characters into the buffer you supplied it with. If you want to find out the number of characters that were written to the buffer, loop over the buffer until you find a byte with the value 0 (or possibly 13 [carriage return]).
Friend maybe I have solved my problem... this is my solution I hope that it is good
#Create two copies of the string, one for nectCh and another for contaVocali
move $t0, $a0
move $t2, $a0
nextCh:
# extract a character from the string
lb $t1, ($a0)
# check the end of the string, so if it is more than 40+1
# give me the error message and repeat the prompt and the input
beq $t1, $zero, error
#check the carriage return and go to the function
beq $t1, 10, contaVocali
#next character
addi $a0, $a0, 1
# repeat
j nextCh
Is acceptable like solution?
Thank you
P.s.
The output is accordant with the output proposed on the exercise ;)
I am trying to compute the length of a string given by a user. Every time I try to run the code, I get the message "Exception occurred at PC=(a certain address) followed by the message :"Bad address in Data/stack read: (another address). I know that it has something to do with the stack but I can't figure out the problem. The code in MIPS is bello and I am using QtSpim. Your help will be very appreciated.
sentence: .space 6
Prompt: .asciiz "Enter the sentence. Max 6 characters, plus a terminator .\n"
.text # Start of code section
main: # The prompt is displayed.
li $v0, 4 # system call code for printing string = 4
la $a0, Prompt # load address of string to be printed into $a0
syscall # call operating system to perform operation;
# $v0 specifies the system function called;
# syscall takes $v0 (and opt arguments)
##read the string, plus a terminator, into the sentence
la $t0, sentence
li $t0, 6
li $v0, 8
add $v0, $zero, $zero #initialize length to zero
loop:
lbu $s0, 0($t0) #load one character of string
addi $t0,$t0,1 #point to next character
addi $v0,$v0,1 #increment length by 1
bne $s0,$zero, loop #repeat if not null yet
end_loop:
addi $v0, $v0, -1 #don't count the null terminator
li $v0, 4 #display the actual length
syscall
exit: #exit the program
li $v0, 10
syscall
##read the string, plus a terminator, into the sentence
la $t0, sentence
li $t0, 6
Here you're loading the address of sentence into $t0, and then immediately overwrite $t0 with the value 6. This is probably the root cause of the exception, since the following lbu will attempt to read from address 0x00000006. I suggest that you remove the li.
li $v0, 8
add $v0, $zero, $zero #initialize length to zero
This li is pointless since you're setting $v0 to zero on the very next line, so this li can also be removed.
sentence: .space 6
Prompt: .asciiz "Enter the sentence. Max 6 characters, plus a terminator .\n"
You say that the user is allowed to enter up to 6 characters. But you only allocate space for 6 bytes, meaning that the NULL terminator wouldn't fit if the user actually enters 6 characters.