How to write a program that applies run length encoding compression to a given string using MIPS - mips

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.

Related

End character of user input string

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.

Program defining if an input integer is divided by 2

I am trying to make a program on QtSpim that constantly takes new integers as input (through the console) and then prints them on the console only when they are even numbers. I set the input 5 as the exit case. The program compiles as it should and when I press the run button there doesn't seem to have a problem. The problem is when I try to type the input number, as the console doesn't respond to that (the number I just typed doesn't even appear on the screen).
Here is my code, I imagine the mistake must be in the first lines where the input number is read, but I can't find it:
.text
.globl __start
__start:
li $v0,5
syscall
move $t0,$v0
add $t1,$t0,$zero
addi $t2,$zero,5
LOOP: div $t0,$t0,2
bne $t0,$zero,LOOP
mfhi $t3
bne $t3,$zero,REPEAT
li $v0,1
move $a0,$t3
syscall
REPEAT:bne $t1,$t2,__start
li $v0,10
syscall
.data
The thing can be done in a much simpler way, using the bitwise and.
Every odd number will have the last bit set, which will make number & 1 equal to 1.
.text
.globl __start
__start:
li $t0, 5
loop:
move $v0, $t0 # set $v0 to 5: read integer
syscall # read in the number
andi $t1, $v0, 1 # check if it's divisible by 2
bnez $t1, check # if no, jump to a check for 5
move $a0, $v0 # if yes, print it
li $v0, 1 # set $v0 to 1: print integer
syscall # do the printing
j loop # continue
check:
bne $t0, $v0, loop # if the integer read is not equal to 5, run again
li $v0, 10
syscall # exit
Your method of checking for even numbers is incorrect. Whenever you enter a number >=1 you'll end up dividing 1 by 2 on the last iteration of your loop. And of course 1 MOD 2 is 1, so your code always thinks the number is odd.
A single division by 2 is sufficient to determine if the value is odd or even. But an AND operation would be even more efficient:
andi $t0,$t0,1 # if the least significant bit is set, the number is odd
bne $t0,$zero,REPEAT
After making that change you'll probably also have to change the printing code, since the value to print no longer is in $t3.

MIPS Error: Not outputting prompts on new line

I'm trying to design a basic calculator with MIPS. I promt the user for the first operand, the operator, and then the second operand. I get and store the first operand, but after the user enters the operator the prompt for the second operand appears on the same line, immediately after the user's input. Here's a sample of what I mean:
Enter first number: 8
Select operator: -Enter second number: 3
Result: 5
-- program is finished running (dropped off bottom) --
The "Enter second number" was printed right after the minus sign. I use read string, with length of string = 2 to get the operator. Here's the relevant code for that:
GetOperator:
la $a0, prompt2 #Load prompt 2
add $v0, $zero, 4 #Load syscall 4
syscall
la $a0, operator
add $a1, $zero, 2
add $v0, $zero, 8 #Load syscall 8
syscall #Store the input string to memory
jr $ra
"operator" is a .word variable I declared under .data. I then use a syscall 4 to print the next prompt. I think there's just some subtle thing going on here that I'm missing. I'm still quite new with MIPS, so any pointers would be awesome.
You could print a carriage return / linefeed sequence before the "Enter second number" string.
# 13=carriage return, 10=linefeed, 0=null terminator
CRLF: .byte 13,10,0
Use syscall 4 to print it.

Exception encountered when trying to display the length of a string given by a user. MIPS

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.

Need help with MIPS program

I'm working on a mips program that will run on pcspim and i need a little help. The description of the program is: Write a program that reads a string (from a keyboard), stores it in the memory, and computes and prints the frequency of each character; and then it reverses the string and prints the reversed string.
so far i have is...
.data # Data declaration section
userString: .space 256
Prompt: .asciiz "\nEnter a word: "
newLine: .asciiz "\n"
.text
main: # Start of code section
li $v0, 4
la $a0, Prompt
syscall
li $v0, 8
la $a0, userString
li $a1, 256
syscall
jr $ra
la $a0, userString
move $t0, $a0
lb $t1, 0($t0)
li $v0, 4
move $a0, $t1
syscall # prints first letter of word
Right now i just wanted to see if i've actually stored the input into the userString array. So at the end i tried to print out the first letter. but it doesnt seem to be printing anything.
Any suggestion?
thank.
I've broken your code down into three parts: prompting, input, display. I assume the first two parts were given to you and the third is what you are focusing on right now. I'll explain what the first to parts are doing then explain what the third is doing right now and what you probably want it to do at this point.
.data # Data declaration section
userString: .space 256
Prompt: .asciiz "\nEnter a word: "
newLine: .asciiz "\n"
.text
# Part I
main: # Start of code section
li $v0, 4
la $a0, Prompt
syscall
# Part II
li $v0, 8
la $a0, userString
li $a1, 256
syscall
jr $ra
# Part III
la $a0, userString
move $t0, $a0
lb $t1, 0($t0)
li $v0, 4
move $a0, $t1
syscall # prints first letter of word
Part I
This is pretty straightforward, when we start executing the program counter will be set to the address of the main label. It loads the value 4 into $v0 (that seems to be the print string system call number), and then loads the address of the Prompt string into the first argument register $a0. The last bit just performs the system call that puts the string on the screen.
Part II
Now that the "Enter a word: " string has been printed on screen, we want to actually read what the user is typing. It looks like here we're using system call #8 (probably read string), so we load that value into $v0 in preparation for the syscall. Then, since we want to read the user string into userString, we load the address of that label into $a0 (the first argument for the read string function) then, since we are savvy programmers, we give the upper bound of how many bytes userString can hold (256) in $a1. Then we perform the system call, you type in a string at the keyboard and hit enter and we return to the next line of code.
That line is jr $ra, which means "jump to the location stored in register $ra (return address)". You probably don't want this, because it marks the end of the main function and likely you program exits back to the command line at this point, probably best to remove it.
Part III
Again, you're loading the address of userString into $a0 (and also moving it into $t0 in the next line). Now it gets weird, you load the first byte 0($t0) of userString into $t1. This is a ASCII character value (like 72 or something). Then you start up the system call stuff again with the print string system call (#4) and the argument of $t1. Which you think will print the first letter of the word, which I disagree with. Here's why. If the user types the string, "Hello, world!" this is what it looks like in memory:
userString: H e l l o , w o r l d !
offset: 0 1 2 3 4 5 6 7 8 9 10 11 12
So, loading 0($t0) moves the letter H into register $t1, then when you perform the system call it tries to print the string starting at H to the screen. However, there is not a string starting at letter H, it starts at the address of userString. So if you just move the address of userString into register $a0, then do system call #4 it should print userString to the screen.
#mjshultz
i've changed it up a little. Didnt think i needed 2 loops. Also i've increment it by four because i thought each character is 4 bytes so to go to the next letter i need to increment the offset by four.
.data # Data declaration section
userString: .space 256
Prompt: .asciiz "\nEnter a word: "
newSpace: .asciiz " "
newLine: .asciiz "\n"
.text
main: # Start of code section
li $v0, 4
la $a0, Prompt
syscall
la $a0, userString
li $a1, 256
li $v0, 8
syscall
la $a0, userString
move $s0, $a0
loop:
lb $t1, 0($s0)
li $v0, 1
move $a0, $t1
syscall
li $v0, 4
la $a0, newSpace
syscall
addi $s0, $s0, 4
blt $s0, 256, loop