MIPS (Bare Mode) String Won't Print - string

Recently while starting to learn MIPS in university, I've come across a problem while trying to print 1 string, accept a user input, and then print another string and accept a user input. Both user inputs should be stored to registers a0 and a1 respectively.
The names of each string are promptD for the Dividend input, and enterD for the Divisor input (you might guess this is an unsigned division calculator program).
In my debugging attempts, I have narrowed the problem to a small snippet of the code, posted below.
I think I am incorrectly offsetting my first .data register to reach my 2nd .data register. The problem I am noticing as I've tried QTspim, xspim, PCspim, and MARS is that all 4 of these give the first string in .data a different initial register address.
For example: The string "Enter Dividend" will be in reg address 0x10010000 in MARS but will start in 0x10000000 in PCspim. The following register address for "Enter Divisor" will be in either 0x10010011 in MARS or 0x10000010 in PCspim.
In its current state thru MARS, the program snippet below asks the user to input dividend, and it will store the value. Immediately after storing to a0, the code will fail due to a line 37 (which is just the 3rd syscall) runtime exception at 0x00400024: address out of range 0x00000004. It is not prompting "Enter Divisor" at all.
To really see the problem in action, I think running this in MARS would help make it more clear. Is it a offsetting issue? Am I clobbering a register without seeing it? I haven't found much MIPS help on here that deals with problems without pseudo-instructions. I realize with them, I could load an address directly (la)...but I can't use them here.
Thanks
.globl main
.data #for the data
promptD: .asciiz "Enter Dividend \n"
enterD: .asciiz "Enter Divisor \n"
# result: .asciiz "Result = "
.text #for the instructions
main:
#for Dividend
addi $v0, $0, 4 #store string instr to v0
lui $a0, 0x1001 #address of promptD
syscall #display promptD
addi $v0, $0, 5 #store input instr to v0
syscall # Get dividend
add $a0, $0, $v0 # Dividend to $a0
#for Divisor
addi $v0, $0, 4 #store string instr to v0
lui $a1, 0x1001 #Where I think the problem is...
#Address of first string followed by add offset?
addi $a1, $a1, 33 #Maybe incorrect offset?
syscall #display enterD
addi $v0, $0, 5 #store input instr to v0
syscall # Get divisor
add $a1, $0, $v0 # Divisor to $a1
#end snippet

Here's the problematic code:
lui $a1, 0x1001 #Where I think the problem is...
#Address of first string followed by add offset?
addi $a1, $a1, 33 #Maybe incorrect offset?
You're using the wrong register. The argument for syscall 4 should be placed in $a0, not $a1.
The offset 33 is incorrect. If you look at the Data Segment viewer in Mars you can see that the NUL-terminator byte for promptD is located at 0x10010010, and that the enterD string begins at 0x10010011 (if you have a hard time reading hexadecimal ASCII codes you can tick the "ASCII" checkbox in the Data Segment viewer to view the data as characters). So the offset you should be using is 0x11 (17 decimal).

Related

String input in MIPS omits the first four characters that are inputted

.data
EntryReq:
.asciiz "Please enter an 8 digit hexadecimal MIPS instruction: \n"
InputLongError:
.asciiz "\nYour input was too long, make sure it is 8 digits. "
InputShortError:
.asciiz "\nYour input was too short, make sure it is 8 digits. "
CharInvalidError:
.asciiz "\nYour input contains an invalid character. "
ValidChars:
.asciiz "0123456789abcdef\n\b\0"
.align 4
input:
.space 20
.text
main:
#Print input request
la $a0, EntryReq #loads input into arg. reg.
li $v0, 4 #op code for print string
syscall
#take input for input (stored)
li $v0, 8 #op code for take user input
la $a0, input #provide address for syscall
li $a1, 20 # tell syscall the byte space required for the string
syscall
#move to input(stored)
sw $v0, input #move inputted into from $v0 to input(stored)
#check validity of input
la $a0, input #load address of input to arg. reg. for method call
la $a1, ValidChars #load address of string of valid chars
jal verifyInput #call the verifyInput method which does as expected
#test if string length count works
addi $a0, $v0, 0 #load from $v0 to arg. reg.
li $v0, 1 #op code for print int
syscall
terminate:
li $v0, 10
syscall
verifyInput:
li $v0, -1 #start length count at 0
verifyLoop:
lb $t0, ($a0) #load current
li $a2, 0 #loop for char check, loops up to length of validChar string
la $a1, ValidChars
j checkChar
charVerified: #ignore this, is entry point back into verifyLoop for checkChar
addi $a0, $a0, 1 #increment
addi $v0, $v0, 1
bgt $v0, 8, printTooLongError #if result bigger than 8, error
bne $t0, 10, verifyLoop #10 is string end, so check if string is end
blt $v0, 8, printTooShortError #if result less than 8, error
jr $ra #if here string input was confirmed okay
checkChar: # loops through valid chars for each char in $a0 | Valid Chars: 0123456789abcdef\n |
lb $t1, ($a1) #loads in byte from char string
addi $a1, $a1, 1 #increment address, for the next char
addi $a2, $a2, 1 #increment until length of valid char string is reached
beq $t0, $t1, charVerified
bne $a2, 19, checkChar #if length of valid chars changes, change second argument here
j charNotValidError
charNotValidError:
la $a0, CharInvalidError #loads input into arg. reg.
li $v0, 4 #op code for print string
syscall
j terminate
printTooLongError:
la $a0, InputLongError #loads input into arg. reg.
li $v0, 4 #op code for print string
syscall
j terminate
printTooShortError:
la $a0, InputShortError #loads input into arg. reg.
li $v0, 4 #op code for print string
syscall
j terminate
The general gist of this code is for the user to input an 8 digit hexadecimal string, and then the program checks whether it is a valid hexadecimal string (i.e. includes only 0-9 and a-f). However, whenever I run it, the string that I input is missing the first four characters. So if I place invalid characters in the first four digits, like wwww1abc, then the code runs fine, which it shouldn't. But if I do 1abcwwww, it outputs an invalid character error, which it should. I'm genuinely confused as to why this is the case, nor have I seen anyone else experience this issue. Any help is greatly appreciated.
The problem is this line:
#move to input(stored)
sw $v0, input #move inputted into from $v0 to input(stored)
Unlike read integer syscall, read string puts the result in the input buffer, in your case input. So you don't need to read out the value in $v0 and by storing it in input you're overwriting the first 4 bytes of the buffer with the value of $v0, which is still 0x00000008, which conveniently is the same as the string "\b\0\0\0" for little endian machines, all of which are in your validity list. Removing that line should fix your program (though I didn't look over all the rest of the code for errors).

How can I replace only the first character of a string in MIPS?

I have written a MIPS assembly language code using sw instruction so that I can only replace the 1st character of a string with a character of my choice.
But, what happens is, the instead of only changing one character, the code changes the 1st character plus destroys characters in next three bytes.
How can I get it right?
I have written the following code:
# replace 1st character of a string
.data
string: .asciiz "ABCDEFGH"
.text
main:
# load string's 1st address into the memory
la $a0, string
li $t0, 'X'
#addi $t0,$t0, 48
sw $t0, ($a0)
# print string
la $a0, string # load 1st address of the string
li $v0, 4 # syscall for string print
syscall
# exit program
li $v0, 10
syscall
Input: ABCDEFGH
Expected result: XBCDEFGH
Actual result: X
You incorrectly use sw that stores a word, ie a 4-bte data.
In your algorithm, after the instruction
li $t0, 'X'
you write 'X' as a 32 bits word in your t0 register. Probably your machine is configured as little endian and $t0, that is a 32 bits register holds the value 0x00000058 (0x58 is the ascii code of X).
When you write it to memory with sw $t0, ($a0), all the 32 bits are written and the content of your memory, that was originally "ABCDEFGH" becomes "X\0\0\0EFGH".
When you ask to print it, the '\0' at position string+1 is considered as an end-of-string terminator and you have just 'X' displayed.
The fix is just to replace the line with
sw $t0, ($a0)
with
sb $t0, ($a0)
and only the least significant byte of your register (ie 'X') is written to memory.

MIPS - How to perform a shift on a string?

I'm very new to MIPS programming, and I've been stuck on a problem I've been attempting to program. I realize what I'm trying to do may be silly, but bear with me please! Here's a description of what I'm attempting to do.
Let's say that I have this string: "~~Hello World!". I want to obtain the string "Hello World!" by shifting this string left by two characters. So far, my closest attempt at performing such an operation is this:
Let the register $t0 contain the string "~~Hello World!". I want to perform a left shift of 2 bits on this string and store in the register $t1.
.data
output1: .asciiz "The value in $t1 is: "
.text
sll $t1, $t0, 2 # attempt at shifting left by 2 bits
li $v0, 4
la $a0, output1
syscall # print "The value in $t1 is: "
li $v0, 4
move $a0, $t1
syscall # print the contents of the register $t1
However, when I assemble these instructions, I'm met with an address out of range error. Can anybody point out where I'm going wrong, and perhaps what I should do to achieve this?
I've figured it out! Here is an updated code snippet, which now contains the working instructions. I'll leave this post up in case it helps anybody else.
.data
output1: .asciiz "The value in $t1 is: "
.text
add $t0, $t0, 2 # shifts the string left by 2 bits (CORRECT)
li $v0, 4
la $a0, output1
syscall # print "The value in $t1 is: "
li $v0, 4
move $a0, $t1
syscall # print the contents of the register $t1

MIPS A String to Integer Conversion

Basically, I will read a string from console, which is no problem. The first and third characters in this string will be a 0-9 number and I want these numbers to store as ıntegers in memory to reuse later. I get "Exception occured at PC=0x0040004c" and when clicking abort I get "Unaligned address in store:0x100100c9".
What is the problem? Please, help!
EDIT:When I run step by step, error occurs in line 24.
.data
exp: .space 201 #allocate 200 bytes for logic expression to be read from stdin. +1 is for null char.
dimension: .space 8 #allocate 8 bytes for dimensions of environment
.text
main:
li $v0, 8 # load appropriate system call code into register $v0;
# code for reading string is 8
la $a0, exp # load address of string to be read into $a0
li $a1, 201 # load length of string to be read into $a1
syscall # call operating system to perform read operation
la $t0, exp
la $t1, dimension
add $t2,$zero,$zero
lb $t2, 0($t0)
addi $t2, $t2, -48
sw $t2, 0($t1)
li $v0, 10
syscall
You have to align the data at word boundary when storing a word.
For that you would have to use .align directive with parameter 2.
In your example dimension is not aligned because exp is 201 bytes length (not a multiple of 4). So you would have to use:
.data
exp: .space 201 #allocate 200 bytes for logic expression to be read from stdin. +1 is for null char.
.align 2 # Align data
dimension: .space 8 #allocate 8 bytes for dimensions of environment
.text

MIPS Assembly: Immediate value is too large for field error

When trying to store a user's inputed string, for part of a project, I receive the following error in spim when I simply load the file:
Immediate value is too large for field: [0x0040009c]
Below is my code:
.globl main
.data
prompt: .asciiz "0: exit, 1: enter string, 2: convert, 3: mean, 4: median, 5: display string, 6: display array: " #94 char long
enter: .asciiz "Enter string: "
.text
main:
display: addi $v0, $v0, 4 #print prompt
lui $a0, 0x1000 #grabbing prompt
syscall
addi $v0, $0, 5 #get integer
syscall
beq $v0, 0, rtn #if user type's 0, exit program
nor $0, $0, $0 #nop
beq $v0, 1, enterString #if user type's 1, enterString
nor $0, $0, $0 #nop
enterString:
addi $v0, $0, 4 #printing string
lui $a0, 0x1000 #grabbing prompt
addi $a0, $a0, 95 #grabbing enter
syscall
addi $v0, $0, 8 #grabbing input
sw $a0, 0x10000100 #storing inpuit - this specific address is a requirement
syscall
rtn: jr $ra
Now, when I run this I get the above mentioned error. However, I'm not quite sure why. It may be due to a string being 32 bit? Any explanations as to why would be appreciated. Thanks again!
I see a couple of problems in your code:
This is way longer than 94 chars:
prompt: .asciiz "0: exit, 1: enter string, 2: convert, 3: mean, 4: median, 5: display string, 6: display array: " #94 char long
Even if you remove those extra spaces, I still count 95 chars.
Don't assume that registers start out with a certain value:
addi $v0, $v0, 4 #print prompt
This should be addi $v0, $zero, 4.
This should probably be 0x1001, since the data section starts at 0x10010000:
lui $a0, 0x1000
Same goes for all other places where you're trying to access the data section.
I don't know if SPIM translates this into a valid instruction:
sw $a0, 0x10000100
If not, you should load the address into a register first (e.g. $a1), and access memory through that register (e.g. sw $a0, ($a1)).

Resources