Hey guys i want to write a simple Caesar Shift in MIPS. My program takes in the string to be encrypted/decrypted. Then the program asks for the key (the number of letters to shift) and then asks to encode or decode.
---EDIT ---
Sorry i forgot to clarify my question. The output of my program is "estuvwL" for a shift of 1 on the string "abcde". Obviously, this is not correct. Any thoughts on where i am going wrong?
Here is my code:
.data # Data declaration section
prompt: .asciiz "Enter a message: " #prompt to enter message
key_prompt: .asciiz "Enter a key for this message: " # enter number to shift letters
enc_dec: .asciiz "(e)ncrypt or (d)ecrypt? " # encrypt or decrypt prompt
encode_bytes: .byte 1 #bytes for encoding
key: .word 0 # the variable for encoding key
String: .space 255 # the string to be encrypted/decrypted
encode: .space 1 #the character (e) or (d) to ask encrypt or decrypt
e: .ascii "e"
d: .ascii "d"
.text
main: # Start of code section
__Start:
li $v0, 4 #print string
la $a0, prompt # print the first prompt
syscall
li $v0, 8 # read in string to be encrpyted
la $a0, String # stores string in variable
li $a1, 255 # the string allocates room for up to 255 chars
syscall
li $v0, 4 #print string
la $a0, key_prompt
syscall
li $v0, 5 #code to read in int
syscall # reading in the integer
move $t5, $a0 #moving key to $t5
li $v0, 4 #print string
la $a0, enc_dec #print the encode string
syscall
li $v0, 12 #read character code
#la $a0, encode #load address of encode to register $a0
#li $a1, 2 #limit input to 1 char
syscall
li $s7, 101
li $s6, 100
beq $v0, $s7, __encrypt_Loop
beq $v0, $s6, __decrypt_Loop
__encrypt_Loop:
la $t0, String #holding address of String
__encode_Loop:
lb $t2, ($t0) #loading the byte to be manipulated into $t2
beqz $t2, __print_String # if string is equal to zero, jump to print new string
__continue:
add $t2, $t2, $t5 #adding the key to the string (manipulating chars)
li $t3, 'z' #loading the value of z, for checking purposes
bgt $t3, $t2, __encrypt_Check #if the char is "bigger" than z jump to fix
sb $t2, ($t0) #store the value
addi $t0, 1 #incrementing to next char
j __encode_Loop
__encrypt_Check:
li $s4, 25
sub $t2, $t2, $s4
sb $t2, ($t0) #store char in string
addi $t0, 1 #incrementing to next char
j __continue
__decrypt_Loop:
la $t0, String #holding address of String
# la $t5, key #loading the key into $t5
__decode_Loop:
lb $t2, ($t0) #making $t2 the address of the string
beqz $t2, __print_String # if string is equal to zero, jump to print new string
___continue:
sub $t2, $t2, $t5 #subtracting the key to the string (manipulating chars)
li $t3, 'a' #loading the value of z, for checking purposes
blt $t3, $t2, __decode_Check #if the char is "smaller" than a jump to fix
sb $t2, ($t0) #store the value
addi $t0, 1 #incfementing to next char
j __decode_Loop
__decode_Check:
li $s4, 25
add $t2, $t2, $s4
sb $t2, ($t0) #store char in string
addi $t0, 1 #incrementing to next char
j __continue
__print_String:
li $v0, 4
#move $a0, $t0
la $a0, String
syscall
__end_program:
li $v0, 10
syscall
A few problems here:
First is the line move $t5, $a0 #moving key to $t5 which should read move $t5, $v0 as the syscall returns the key in $v0 not $a0.
Secondly, I'm not sure what the purpose of the "check" functions is, so I commented out the branch to them and everything worked as I expected.
At any rate, the check for them is clearly wrong. You wrote:
blt $t3, $t2, __decode_Check #if the char is "smaller" than a jump to fix
But the arguments are reversed from the comment. It should read
blt $t2, $t3, __decode_Check #if the char is "smaller" than a jump to fix
Likewise for the __encrypt_Check function.
Related
I have to write a program to count the number of vowels in a string in MIPS. My current code is giving me a memory out of bounds error in QtSPim.
I'm at the beginner level in MIPS, so any help would be appreciated.
Code so far:
.data
str: .space 20
my_chars: .space 20
vow1: .byte 'a'
vow2: .byte 'e'
vow3: .byte 'i'
vow4: .byte 'o'
vow5: .byte 'u'
.text
main:
li $s0,0 #initilaze loop var1
li $t0,20 #initialize loop var2
li $s1,0 #initialize counter
la $t1, my_chars # base address of array
li $a1,20 #max input to be read
li $a0,8
syscall
loop:
beq $s0, $t0, exit
la $t2, str #string into t2
lb $v0, 0($t2) #access first index
lb $t9, vow1
beq $v0, $t9, then #comparing to a
then:
addi $s1, $s1, 1
lb $t8, vow2
beq $v0, $t8, then1 #comparing to e
then1:
addi $s1, $s1, 1
lb $t7, vow3
beq $v0, $t7, then2 #comparing to i
then2:
addi $s1, $s1, 1
lb $t6, vow4
beq $v0, $t6, then3 #comparing to o
then3:
addi $s1, $s1, 1
lb $t5, vow5
beq $v0, $t5, then4 #comparing to u
then4:
addi $s1, $s1, 1
addi $t1, $t1,1 #increment base address
addi $s0, $s0,1 #increment loop variable
j L1
syscall
Because your posted code had missing labels, etc. I couldn't run it to look for the runtime error.
From visual inspection, the read from user input code had a few issues. li $a0,8 should be li $v0,8 [the syscall number to read a string]. $a0 should contain the address of the buffer to read into. In your code, this was 8 and [probably] not a valid address. So, you'd probably want something like la $a0,my_chars or la $a0,str. One of them should be the input buffer and the other seems unnecessary.
As I was trying to add labels [based on educated guesswork], I realized that your program could/would be much simpler if the vowels were in an array, so I refactored the code.
I also changed the loop termination to look for EOS (0x00) instead of decrementing a count, which may have been another potential source of an out-of-bounds issue. This also reduces the number of registers needed (i.e. reduces complexity)
I added the missing boilerplate/syscalls [please pardon the gratuitous style cleanup]:
.data
vowel: .asciiz "aeiou"
msg_prompt: .asciiz "Enter string: "
msg_out: .asciiz "Number of vowels is: "
msg_nl: .asciiz "\n"
str: .space 80
.text
.globl main
main:
# print user prompt
li $v0,4
la $a0,msg_prompt
syscall
# get string to scan
li $v0,8
la $a0,str
li $a1,80
syscall
li $s2,0 # initialize vowel count
la $s0,str # point to string
# registers:
# s0 -- pointer to string character
# s1 -- pointer to vowel character
# s2 -- count of vowels
#
# t0 -- current string character
# t1 -- current vowel character
string_loop:
lb $t0,0($s0) # get string char
addiu $s0,$s0,1 # point to next string char
beqz $t0,string_done # at end of string? if yes, fly
la $s1,vowel # point to vowels
vowel_loop:
lb $t1,0($s1) # get the vowel we wish to test for
beqz $t1,string_loop # any more vowels? if not, fly
addiu $s1,$s1,1 # point to next vowel
bne $t0,$t1,vowel_loop # is string char a vowel? -- if no, loop
addi $s2,$s2,1 # yes, increment vowel count
j string_loop # do next string char
string_done:
# print count message
li $v0,4
la $a0,msg_out
syscall
# print vowel count
li $v0,1
move $a0,$s2
syscall
# print a newline
li $v0,4
la $a0,msg_nl
syscall
# exit program
li $v0,10
syscall
I'm trying to read a string from a user in order to evaluate an expression. For example I want to compute ((2-(8*9))1-4)). To accomplish this, I convert the characters of the numbers read to the actual number they represent while saving them back into the string with the code below. My algorithm works great when I input digits of (0-9) in between operation commands ' - ( ) / '. But I need to make it work when inputting multi-digit numbers in my string. For example, ((20-(8*91))*1-43)). I know the algorithm to convert, lets say 3 1 4 into 314, but my question is, how can I insert it back into my byte string while maintaining the same order on the following characters? I've tried many ways, but can't get it to work. thanks in advance
.data
expression: .space 110 # to store string
stack: .space 110 # to help with operations
.text
#______reading string from user
li $v0, 8
li $a1, 100
la $a0, expression
syscall
#_____________________________
#___count number of characters in string
la $s0, expression #s0 = adress of string
li $t1, 0 #t1 = # characters
loopcounter:
lb $t0, ($s0)
beq $t0, '\n', outcounter
addi $t1, $t1, 1
addi $s0, $s0, 1
j loopcounter
outcounter: #if you want to print the amount of characters in the string
#li $v0, 1
#move $a0, $t1
#syscall
#_______________________________________
#__convert number characters into integers
la $s0, expression #s0 = adress of string
li $t0, 1 #t0 loop counter
loopStringtoNum:
lb $t2, ($s0) #t2 = ASCII of a digit
blt $t2, '0', skipCharConversion
bgt $t2, '9', skipCharConversion
subi $t2, $t2, 0x30 #t1 = the digit
sb $t2, ($s0)
skipCharConversion:
addi $s0, $s0, 1
addi $t0, $t0, 1
ble $t0, $t1, loopStringtoNum #skip string to num loop
#_________________________________________
I am trying to write a program that gets a user string input and reverse that string in MIPS.
However, I must be doing something horribly wrong as it doesn't just display the user input in reverse but, it also reverses the prompt to the user. It seems that the user input is not identified with a null(zero?) character in the end.
.data
prompt: .asciiz "Please enter your name. You're only permitted 20 characters. \n"
userInput: .space 20 #user is permitted to enter 20 characters
.globl main
.text
main:
# user prompt
li $v0, 4
la $a0, prompt
syscall
# getting the name of the user
li $v0, 8
la $a0, userInput
li $a1, 20
syscall
add $t0, $a0, $0 # loading t0 with address of array
strLength:
lbu $t2, 0($t0)
beq $t2, $zero, Exit # if reach the end of array, Exit
addiu $t0, $t0, 1 # add 1 to count the length
j strLength
Exit:
add $t1, $t0, $0 # t1 = string length
li $t2, 0 # counter i = 0
li $v0, 11
reverseString:
slt $t3, $t2, $t1 # if i < stringlength
beq $t3, $0, Exit2 # if t3 reaches he end of the array
addi $t0, $t0, -1 # decrement the array
lbu $a0, 0($t0) # load the array from the end
syscall
j reverseString
Exit2:
li $v0, 10
syscall
Problem number 1:
add $t1, $t0, $0 #t1 = string length
What you're assigning to $t1 here isn't the length of the string; it's the address of the first byte past the end of the string.
Problem number 2 is that you never increment $t2 (or decrement $t1) within the reverseString loop.
I suggest that you make use of the debugging features in SPIM/MARS (like the ability to set breakpoints and single-step through the code), as that would've made finding these problems yourself fairly simple.
The following is my code for a MIPS assembly program that is intended to remove vowels from an input string and then print the new string. As it stands, the program simply does not remove the vowels and just reprints the same string that was input.
.text
.globl main
main:
# display prompt
li $v0, 4
la $a0, prompt
syscall
# accept input string
li $v0, 8
la $a0, str
li $a1, 82
syscall
li $t0, 0 # add a null to the bottom of the stack
subu $sp, $sp, 4
sw $t0, ($sp)
li $t1, 0 # initiate index
pushloop:
lbu $t0, str($t1) # (I think the program is placing a zero in $t0 here, thus causing it to branch immediately to poploop, which then branches to the end since only the null has been pushed onto the stack)
beqz $t0, poploop # once we reach null char, finish
subu $sp, $sp, 4
sw $t0, ($sp)
addiu $t1, $t1, 1
j pushloop
nop
# $t1 is not reset
poploop:
lw $t0, ($sp)
addu $sp, $sp, 4
beqz $t0, done # once we reach null char, finish
nop
# check if vowel
li $t2, 0x61 # a
beq $t0, $t2, vowel
nop
li $t2, 0x65 # e
beq $t0, $t2, vowel
nop
li $t2, 0x69 # i
beq $t0, $t2, vowel
nop
li $t2, 0x6F # o
beq $t0, $t2, vowel
nop
li $t2, 0x75 # u
beq $t0, $t2, vowel
nop
# if not a vowel, store it at current index in string
sb $t0, str($t1)
j decrement
nop
vowel: # if vowel, remove character
li $t0, 0
sb $t0, str($t1)
decrement:
addiu $t1, $t1, -1 # decrement index
j poploop
nop
done:
li $v0, 4
la $a0, str
syscall
li $v0, 10 # exit program
syscall
nop
.data
str: .space 82
prompt: .asciiz "Input a string:\n"
So. I took a look at what you've writen and I've fixed it.
My first thought is I don't know what you are doing with the stack and stack pointer ($sp). It didn't seem necessary so I took it out.
Next is that the approach is wrong. Your approach is to search the string and replace every lower-case vowel (or at least 'a', 'e', 'i', 'o' and 'u') with a 0. This will not work.
If you think about a typical C string, they are delimeted by a 0, so if you take the string My name is Jeoff and apply your algorithm you will get My n\0m\0 \0s J\0\0ff which of course will print as My n.
So instead, I chose a separate algthm that chooses to not store anything in the case of a vowel and instead shift all following characters over by 1. In so doing we can easily remove all vowels from a string without requiring a secondary buffer.
Take a look below:
.text
.globl main
main:
# display prompt
li $v0, 4
la $a0, prompt
syscall
# accept input string
li $v0, 8
la $a0, str
li $a1, 82
syscall
li $t1, 0 # initiate index
li $t3, 0 # vowel count
poploop:
lb $t0 str($t1)
# check if vowel
li $t2, 'a' # a
beq $t0, $t2, vowel
nop
li $t2, 'e' # e
beq $t0, $t2, vowel
nop
li $t2, 'i' # i
beq $t0, $t2, vowel
nop
li $t2, 'o' # o
beq $t0, $t2, vowel
nop
li $t2, 'u' # u
beq $t0, $t2, vowel
nop
# if not a vowel, store it at current index in string less vowel count
sub $t2, $t1, $t3
sb $t0, str($t2)
j next
nop
vowel: # if vowel, inc count
addi $t3, $t3, 1
next:
addi $t1, $t1, 1
beqz $t0, done # once we reach null char, finish
nop
j poploop
nop
done:
li $v0, 4
la $a0, str
syscall
li $v0, 10 # exit program
syscall
nop
.data
str: .space 82
prompt: .asciiz "Input a string:\n"
I am trying to write code that uses the MARS (my MIPS simulator) pseudorandom number generator to pick a random char in the string, take it out of memory and into a register, and replace that char in memory with an asterisk, '*'.
So far, it only scrambles part of the word, and it's driving me insane. I can't find what in this code isn't working. I don't even need a direct answer, just hints/tips would be SO helpful.
Here is the code:
#this loop extracts a char at random from a string in memory, stores it in a register, and replaces the char in the string with an asterisk '*'
.data
.align 2
string0: .ascii "Tyler\n"
.align 2
endString: .asciiz "Loop completed!\n"
.align 2
scrambleString: .asciiz
.text
#counter
li $t0, 5
#pointer to string0
la $s0, string0
loop2:
#is counter = 0? go to loop3 if so
beq $t0, $0, loop3
#seed & prepare randomized number generator
li $v0, 30
syscall
li $v0, 40 #sets seed
syscall
#generates random number in $a0, with the coUnter $t0 being the upper bound
addi $a1, $t0, 1
li $v0, 42
syscall
#add STRING POINTER by random number in $a0, store this new address in $t1
#addi $a0, $a0, 1
add $t1, $s0, $a0
#srlv $t1, $s0, $a0
#isolates that bytesized char, puts it into $t2
lbu $t2, ($t1)
#beq $t2, 0x5c, loop2
#replaces char in original string with "*"
li $t3, 0x2a
sb $t3, ($t1)
beq $t1, $t3, loop2
#decrement counter
addi $t0, $t0, -1
#loop return
j loop2
loop3:
la $a0, string0
li $v0, 4
syscall
li $v0, 10
syscall
You are resetting the random number seed on every iteration of
your loop (loop2:).
Syscalls 40 and 42 each take 2 parameters which should be in $a0 and
$a1. See here.
The temporary registers $t0,...,$t9 get altered by each
syscall. You should be using the callee saved registers
$s0,...$s8 instead.