I keep getting the bad address exception, any knows why? - mips

I'm trying to do a simple program that counts the number of characters in a string . It seems I follow the structure but I keep getting the same bad address error, anyone knows why?
.data
array: .space 100
prompt1: .asciiz " Enter the string that you would like to reverse and calculate character: "
prompt2: .asciiz " "
.text
main:
la $a1, array # allocate the array space
ask:
li $v0, 4 # print String
la $a0, prompt1 # load prompt1
syscall
li $v0, 8 # read the string
syscall
move $a1, $v0 # move input to $a1
li $t0 ,0 # $t0 will hold the actual numbers of character in the string
loopCount:
lbu $t1, 0($a1) # load the next character into t1
beqz $t1, print # check for the null character
addi $a1, $a1, 1 # increment the string pointer
addi $t0, $t0, 1 # increment the count
b loopCount # return to the top of the loop
print:
li $v0, 1
move $a0, $t0
syscall

You should load the address of your buffer in $a0 and the size of the buffer in $a1 before the syscall to read the string.

Related

Swapping every two characters in a string and fixing exception error

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

Understanding how string is stored in MIPS and how to count the number of characters in the string

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!

MIPS Printing Values of a Given Array

I am trying to print on the console the values of the array, which are already given as intA in .data. I.e., trying to print values of an array without user's prompt.
My code:
.data
prompt: .asciiz "The values in the array are:\n"
finished: .asciiz "\nNo more values to present"
space: .asciiz " "
intA: .word 11, 2, 3, 4, 5, 34, 0
.text
.globl main
main:
#Prints the prompt string
li $v0, 4
la $a0, prompt
syscall
# initialization of a0, a1, and t3 (i, counter)
la $a0, intA # loading starting address (base) of array in register a0
addi $a1, $zero, 6 # array size - 1
addi $t3, $zero, 0 # i initialized to 0
j loop
loop:
lw $t1, 0($a0) # loading integer (value of array) in the current address to register t1, I use lw because integer is a word (4 bytes)
# printing current value of array
li $v0, 4
la $a2, ($t1)
syscall
# spacing between values
li $v0, 4
la $a2, space
syscall
# checking that next address is not outside of the array
addi $t3, $t3, 1
slti $t2, $t3, 6
bne $t2, 1, done
# accessing next integer and jumping back to print it
addi $a0, $a0, 4
j loop
done:
# indicating program is done
li $v0, 4
la $a0, finished
syscall
The output that I am getting: output
Any idea why it doesn't print the values of the array, and also what are these squares that are printed instead?
Edit:
I changed
# printing current value of array
li $v0, 4
la $a2, ($t1)
syscall
to
# printing current value of array
li $v0, 1
lw $a2, ($t1)
syscall
Because, from what I understand, I print an integer so $v0 should be fed 1, and I should lw and not la (because it is an integer, i.e, a word)
However, now I get a runtime error in line 31: lw $a2, ($t1) Telling me that
fetch address not aligned on word boundary 0x0000000b
Solution: Instead of lw $a0, ($t1) for printing the value of $t1, I need to do add $a0, $t1, $zero, because I am trying to use the value and not to access an address.

MIPS Calculator

I'm trying to finish up this MIPS calculator, super basic, my first mips program. It doesn't have to handle overflow or anything like that, just has to work on small, positive numbers.
I've not checked my algorithms for multiply and divide, because I am just trying to get add working.
I cannot for the life of me figure out why the ints will not read in and also I'm getting a memory out of bounds when I call lb $a0, op to display the operator for output and don't understand why.
I'm new to this so anything is probably helpful. Thanks in advance.
.data
# const string for welcome
welc: .asciiz "Welcome to SPIM Calculator 1.0!\n"
p_int: .asciiz "\nPlease give an integer: "
p_op: .asciiz "\nPlease give an operator: "
i_err: .asciiz "\nInput Incorrect, bad operator!\n"
again_str: .asciiz "Another calculation? (y/n)"
rmndr: .asciiz " r: "
new_line: .asciiz "\n"
int1: .word 1 # space to hold int 1
int2: .word 1 # space to hold int 2
raw_in: .space 1 # space to hold raw input
op: .space 1 # space to hold operator char
a_char: .space 1 # space to hold again char
out: .word 1 # space to hold output
remain: .word 1 # space to hold remainder
#operator constants
c_plus: .ascii "+" # const for +
c_min: .asciiz "-" # const for -
c_mult: .asciiz "*" # const for *
c_divi: .asciiz "/" # const for /
c_eq: .asciiz "=" # const for =
c_no: .asciiz "n" # const for n
.text
.globl main
main: li $v0, 4 # syscall 4, print string
la $a0, welc # give argument: string
syscall # actually print string
calc: la $t6, remain # load remainder variable
move $t6, $zero # store 0 in remainder (reset)
li $v0, 4 # syscall 4, print string
la $a0, p_int # give argument: string
syscall # actually print string
li $v0, 5 # tell syscall we want to read int 1
syscall # actually read in int 1
la $s1, int1 # load int1 into $s1
move $s1, $v0 # copy the integer from $v0 to int1
li $v0, 4 # syscall 4, print string
la $a0, p_int # give argument: string
syscall # actually print string
li $v0, 5 # tell syscall we want to read int 2
syscall # actually read in int 2
la $s2, int2 # give $s2 the address to hold int 2
move $s2, $v0 # copy the integer from $v0 to $s2
li $v0, 4 # syscall 4, print string
la $a0, p_op # give argument: string
syscall # actually print string
li $v0, 8 # tell syscall we want to read operator
la $a0, op # give $a0 the address to hold the operator
syscall # actually read in operator
lb $t0, op # load the first byte of op
li $t1, '+' # load const for plus
li $t2, '-' # load const for minus
li $t3, '*' # load const for multiplying
li $t4, '/' # load const for dividing
la $s0, out # load out to $s0
beq $t0, $t1, plus # we're adding
beq $t0, $t2, minus # we're subtracting
beq $t0, $t3, multi # we're multiplying
beq $t0, $t4, divi # we're dividing
# else
j error # incorrect input
plus: add $s0, $s1, $s2 # add our ints, store in $t0
j print
minus: sub $s0, $s1, $s2 # subtract our ints, store in $t0
j print
multi: slt $t1, $t2, $s2 # if our counter is less than int2, set $t1 to 1
beq $t1, $zero, print # if we've reached int2, we're done
add $s0, $s1, $s1 # add int1 and int1, store in out
j multi # loop
divi: la $t0 remain # load remainder into $t0
move $t0, $s1 # set remainder to dividend
add $s0, $zero, $zero # set out to 0, just in case
loop: slt $t1, $t0, $s2 # if remainder is less than divisor, set 1
beq $t1, $zero, print # if we're done branch to done
sub $t0, $t0, $s2 # sub divisor from remainder, store in remainder
addi $s0, $s0, 1 # increment quotient by 1
j loop # loop
print: li $v0, 1 # tell syscall we want to print int
la $a0, int1 # give syscall int1 to print
syscall # actually print int
li $v0, 4 # tell syscall we want to print string
lb $a0, op # tell syscall we want to print operator
syscall # actually print string
li $v0, 1 # tell syscall we want to print int
la $a0, int2 # give syscall int2 to print
syscall # actually print int
li $v0, 4 # tell syscall we want to print string
la $a0, c_eq # tell syscall we want to print operator
syscall # actually print string
li $v0, 1 # tell syscall we want to print integer
la $a0, out # give syscall our output
syscall # actually print int
la $t0, remain # load remainder
beq $t0, $zero, again # if we have no remainder, finish printing
li $v0, 4 # tell syscall we want to print string
la $a0, rmndr # tell syscall we want to print remainder string
syscall # print "r: "
li $v0, 1 # tell syscall we want to print int
la $a0, remain # give syscall our remainder to print
syscall # print remainder
again: li $v0, 4 # tell syscall we want to print string
la $a0, new_line # tell syscall to print new line
syscall
la $a0, again_str # load prompt for again string for syscall
syscall
li $v0, 8 # tell syscall we want to read string
la $a0, a_char # tell syscall to put it in $a0
syscall
lb $t0, a_char
li $t1, 'n' # get n char so we can compare
beq $t0, $t1, exit # if we are done, exit
#else loop
j calc # jump to beginning
error: li $v0, 4 # tell syscall we want to print string
la $a0, i_err # give syscall what to print
syscall # actually print
j again # go to prompt for retry
exit: li $v0, 10 # exit code
syscall #exit!
screenshot
The problem is that you don't use the appropriate instruction to handle memory.
Instead of move you should use sw (store word). This code will not store the int into int1:
la $s1, int1 # load int1 into $s1
move $s1, $v0 # copy the integer from $v0 to int1
instead, you should write:
la $s1, int1 # load address of int1 into $s1
sw $v0, 0($s1) # copy the integer from $v0 to int1
Like storing, loading from memory require two instructions:
la $s1, p_op # or whatever register you choose to use
lb $a0, 0($s1) # load byte from the address stored in $s0 (in index 0)
if you want to load the address of p_op into $a0, you should use la $a0, p_op, not lb

Override string MIPS

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