I am attempting to reverse a string of ints when given size and string. Here is what I have so far. I know my problem is in the loop section, mainly in figuring out how to get a pointer to point at the back of the string and move what is contained to where it should go
Any help would be appreciated:
.data
Size: .word 9 # Size is 9
Vals: .word 1, 2, 3, 4, 5, 6, 7, 8, 9 # Vals[9]={1,2,..}
.text
main:
la $t1, Size # get the address of variable Size
la $t2, Vals # get the address of variable Vals
lw $t3, 0($t1) # fetch the value of Size to register $t3
sra $t4, $t3, 1 # right shift arithematic, now $t4 contains Size/2
sll $t3, $t3, 2 # left shift logic, now $t3 contains Size*4
loop:
lb $t5, 0($t2)
sb $t0, 36($t2)
sb $t0, ($t5)
addi $t0, $t0, 4
addi $t5, $t5, -4
bne $t5, $t4, end
j loop
end:
la $t0, Vals # get the address of Vals to $t0
la $t1, Size # get the address of Size to $t1
lw $t3, 0($t1) # get Size to $t3
sll $t3, $t3, 2 # left shift logic, now $t3 contains Size*4
add $t1, $t0, $t3 # $t1=Vals+Size*4 => array bound
li $v0, 1 # service 1 is print integer
lab4:
lw $a0, 0($t0) # load desired value into argument register $a0
syscall # print the value in $a0
addi $t0, $t0, 4 # increase array index
bne $t0, $t1, lab4 # check if reach array bound
There are some issues in that loop:
You are reading bytes instead of words (use lw/sw instead of lb/sb
You are mixing contents with addresses (usage of register $t5)
You are adding/subtracting 4 to the contents read instead of the pointers to the array
The way you are trying to reverse the array seems wrong as adding 36 (=9x4) would point to a location after the end of the array
What you should do is use 2 pointers: one which starts pointing to the beginning of the array and another that starts pointing to the end of the array, then read both items and interchange its contents. Now move the the next element in each pointer (one pointer moves ahead and the other move behind), and repeat the process until all the items are processed (that is, when both pointers crosses).
E.g (snip):
addu $t7, $t2, $t3 # $t5 contains address of end of string
loop:
addi $t7, $t7, -4 # moves pointer backwards
lw $t5, 0($t2)
lw $t0, 0($t7)
sw $t5, ($t7) # interchange
sw $t0, ($t2) # contents
addi $t2, $t2, 4 # moves pointer forward
ble $t2, $t7, loop
Related
I hope you're all having a great day. I was hoping i could get some assistance with my project code. Basically a sentence "FADED IN FADED OUT." is type in when prompted and then a search word "FADED" is typed in and the code is executed to see if the word "FADED" is in the sentence and if it is it will say "x Match(es) Found" and if not "No Match(es) Found". Well when I compile and run it gives me a "line 65: Runtime exception at 0x00400098: address out of range 0x00000000" error and there are multiple lines that have this error. Would anyone be able to assist me with this? I have been trying to do it the past 3 days and finally broke for some help... If you have any questions please let me know!
.data
str: .space 100 # Pre Allocate space for the input sentence
input: .space 30 # Pre Allocate space for the input sentence
ins: .asciiz "Please enter a sentence: " # string to print sentence
seek: .asciiz "Please enter a word: " # string to print sentence
nomatch: .asciiz "No Match(es) Found"
found: .asciiz " Match(es) Found"
newline: .asciiz "\n" # string to print newline
.text
li $v0, 4 # syscall to print string
la $a0, ins # move str into a0
syscall # syscall
li $a1, 100 # allocate space for the string
la $a0, str # load address of the input string
li, $v0, 8 # read string input from user
syscall # issue a system call
move $t9, $a0 # move string to t5
li $v0, 4 # syscall to print string
la $a0, seek # move str into a0
syscall # syscall
la $a0, input # load address of the input string
li $a2, 30 # allocate space for the string
li, $v0, 8 # read string input from user
syscall # issue a system call
move $t8, $a0 # move string to t8
la $s5, input # create space for the input word we are looking for in s5
wloop: # loop to allocate space for the word we are looking for to a register
lb $t0, 0($t8) # load first character into t0
beqz $t0, sentence # branch to sentence loop if null character
sb $t0, 0($s5) # store the current character into current address of s5
addi $t8, $t8, 1 # add one to t8 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to the next allocated space
j wloop # jump back to wloop
la $s4, str # create space for the input sentence
sentence: # loop to allocate space for the word we are looking for into a register
lb $t0, 0($t9) # load first character into t0
beqz $t0, resetsen # branch to check loop if null character
sb $t9, 0($s4) # store the current character into current address of s4
addi $t9, $t9, 1 # add one to t9 to move to next character
addi $s4, $s4, 1 # add one to s5 to move to the next allocated space
j sentence # jump back to sentence
resetsen:
li $s4, 0 # reset sentence back to 0 (first character)
resetword:
li $s5, 0 # reset word we are looking for back to 0 (first character)
check:
lb $t1, 0($s4) # load current character of sentence to t1
beq $t1, 46, quit # branch to QUIT if period found
bne $t1, 70, nextword # if t1 != t0 branch to nextword
beq $t1, 70, checkword # branch to found if t1 = f
nextword: # loop to get to the next word
lb $t1, 0($s4) # load current character to t1
beq $t1, 46, quit # branch to quit if period found
bne $t1, 32, increment # if current character is not a spaace branch to increment
beq $t1, 32, plusone # if current character is a space branch to plusone
increment: # increment procedure
addi $s4, $s4, 1 # add one to s4 to move to next character
j nextword # jump to nextword
plusone: # plusone procedure
addi $s4, $s4, 1 # add one to s4 to move to next character
j resetword # jump to check
checkword:
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for a)
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for d)
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for e)
addi $s4, $s4, 1 # add one to s4 to move to next character
addi $s5, $s5, 1 # add one to s5 to move to next character
lb $t1, 0($s4) # load current character of sentence to t1
lb $t0, 0($s5) # load current character of sentence to t0
bne $t1, $t0, increment # if t0 != t1 branch to increment (looking for d)
addi $t2, $t2, 1 # add one to t2 which counts occurences
j resetword
quit:
beqz $t2, exit # if t2 = 0 branch to exit
li $v0, 1 # syscall to print integer
move $a0, $t2 # move str into a0
syscall # syscall
li $v0, 4 # syscall to print string
la $a0, found # move found into a0
syscall # syscall
j endprogram
exit:
li $v0, 4 # syscall to print string
la $a0, nomatch # move nomatch into a0
syscall # syscall
endprogram:
li $v0, 10
syscall
You are given a string, like "hello what is your name?"
You have to reverse the words, using a recursive function.
So the result of the example string is "name? your is what hello"
The language is MIPS assembly.
Here is what I have done so far: (The code doesn't end unfortunately :| and I can't find the issue)
.macro print_int(%arg)
li $v0, 1
add $a0, %arg, $zero
syscall
.end_macro
.macro print_string(%arg)
move $t9, $a0
li $v0, 4
add $a0, %arg, $zero
syscall
move $a0, $t9
.end_macro
.text
la $s0, string
li $s1, 32 # space
la $t8, space
sub $s0, $s0, 1
sb $s1, 0($s0)
# find the length of the string
move $t0, $s0 # $t0 = i = the iterator
L1: lb $t1, 0($t0) # $t1 = i'th char of the string
beq $t1, 0, Exit # if string[i] == null, Exit
addi $t0, $t0, 1 # i++
j L1
Exit:
sub $s3, $t0, $s0 # $s3 is the length of the string
# Set arguements
move $a0, $s0
move $a1, $t0 # endFlag = length of the string
jal reverse # call the function
li $v0, 10
syscall # exit
reverse:
# save registers
sub $sp, $sp, 12
sw $ra, 0($sp)
sw $a0, 4($sp)
sw $a1, 8($sp)
bgt $a1, $s0, L2 # base case
add $sp, $sp, 12
jr $ra
# find a word in the string
L2:
add $t0, $zero, $a1
add $t3, $a0, $s3 # address of last character of the string
Loop:
lb $t4, 0($t3) # chracter from the string
seq $v0, $s1, $t4 # if space
ble $t3, $a0, Exit_Loop # if first of string
beq $v0, 1, Exit_Loop # if character was space
sub $t3, $t3, 1
j Loop
Exit_Loop:
sb $zero, 0($t3)
add $t3, $t3, 1
print_string($t3)
print_string($t8)
#recursive call
move $a1, $t3
jal reverse
# load registers
lw $ra, 0($sp)
lw $a0, 4($sp)
lw $a1, 8($sp)
add $sp, $sp, 12 # release the stack
jr $ra
.data
string: .asciiz "hello what is your name?"
newline: .asciiz "\n"
space: .asciiz " "
Your code does not stop from entering the function recursive even if the string is empty ($t3 == $a0 + 1).
Here's a quick fix: replace your code:
Exit_Loop:
sb $zero, 0($t3)
add $t3, $t3, 1
print_string($t3)
print_string($t8)
#recursive call
move $a1, $t3
jal reverse
with:
Exit_Loop:
sb $zero, 0($t3)
add $t4, $t3, 1
print_string($t4)
print_string($t8)
ble $t3, $a0, Exit_Func
#recursive call
move $a1, $t4
jal reverse
Exit_Func:
Also, please notice that your macro print_int doesn't store/restore the value of $a0, also even in print_string you store/restore $a0 using $t9, that's still dangerous since according to the MIPS32 ABI the values in register $t[0-9] are not guaranteed to be reserved during a syscall (while in $s[0-7] it is guaranteed).
I am trying to implement puts in MIPS. I made it work with $a0. It printed the string starting at the address of that register. However, I am now trying to implement puts for an arbitrary amount of strings, meaning that I can't use the registers anymore. I am confused as to how to use the stack efficiently. From my main procedure, I stack an arbitrary amount of strings. I then want to jal to puts, which will print all the strings. I also pass on top of the stack the number of strings to print.
Here is my code for puts :
# puts - prints ASCII to the screen
# prints from $a0, $a1, $a2, $a3 (buffers)
# stops when chracter is NULL
# ASSUME THAT WHEN PUTS IS CALLED, AT LEAST ONE STRING IS PASSED
#
# CALLING CONVENTION
#
# I decided not to use any of the registers $a0-$a4.
# Instead, the caller stacks all the strings it wants to print in the stack.
# On top of it, it adds the number of strings it wants to print.
# puts will loop, printing each string, until it has printed all of them.
#
# Since the caller is using "s" registers, puts must save them to the stack.
# Thus, these will be put on top of the stack.
# puts will have to access the strings below the stacked registers.
#
# ============ STACK DIAGRAM ==============
#
# \ Number of Strings \ TOP
# \ String 1 \ +200
# \ String 2 \ +400
# \ String 3 \ +600
# \ String 4 \ +800
# \ String 5 \ +/000
# \ String 6 \ +...
# \ ... \
#
# on entry:
# $ra -- return address
# 0($sp) -- number of strings to print
# x($sp) -- strings to print
#
# on exit:
# $v0 -- number of character printed
#
.text
puts:
addi $sp, $sp, -20 # make room for 6 registers
sw $ra, 16($sp) # save $ra on the stack
sw $s0, 12($sp) # save $s0 on the stack
sw, $s1, 8($sp) # save $s1 on the stack
sw, $s2, 4($sp) # save $s2 on the stack
sw, $s3, 0($sp) # save $s3 on the stack
lw $s0, 224($sp) # copy address of first string inside $s0
lw $s1, 24($sp) # copy number of strings to print inside $s1
move $s2, $zero # counter for the number of strings printed
move $s3, $zero # counter for the number of characters sent to putchar
putsString:
** lbu $t3, ($s0) # load ccurrent char into #t3
beq $t3, $zero, exitString # exit puts if the current character is the NULL character
move $a0, $t3 # put the character to print inside $a0, accessible by putchar
jal putchar # print char using putchar
addi $s3, $s3, 1 # character count += 1
addi $s0, $s0, 4 # increment string address by 1 word (4 bytes)
j putsString # Loop to print next character
exitString:
addi $s2, $s2, 1 # increment number of strings printed
beq $s1, $s2, exitPuts # if # of strings to print = # of strings printed, then exit puts; else, increment # of strings printed
addi $s0, $s0, 200 # $s0 now points to next string to print
j putsString
exitPuts:
move $v0, $s4 # return number of character printed
lw $s3, 0($sp) # restore stack
lw $s2, 4($sp) # -
lw $s1, 8($sp) # -
lw $s0, 12($sp) # -
lw $ra, 16($sp) # -
addi $sp, $sp, 20 # pop from stack
jr $ra # return
The line with a ** is the faulty one for now. I get Runtime exception at 0x00400124: address out of range 0x00000000.
Here is how I save the strings to the stack (I read 2 (2 is hardcoded) strings for now with gets).
init:
beq $s0,2,continue
move $a0, $s2 # load buffer address into $a0
la $t1, limit # - load limit into $a1
lb $a1, ($t1) # - ...
jal gets # call gets - it will modify buffer
move $t0, $v0 # $t0 = string count returned by gets
moveBufferToStack:
lbu $t1, ($s2) # get first character inside buffer
beqz $t1, endMoveBuffer
sw $t1, ($sp) # copy buffer(i) into array(i)
addi $sp, $sp, 4 # array pointer points a position forward
addi $s2, $s2, 4 # same for array pointer
j moveBufferToStack
endMoveBuffer:
addi $sp, $sp, 200 # step to next array cell
addi $s0, $s0, 1 # increment number of strings read
j init # loop until we have 9 elements
continue:
jal puts
j done
Just focusing on the problem line, I see this:
lw $s0, 224($sp) # copy address of first string inside $s0
...
putsString:
lbu $t3, ($s0) # load ccurrent char into #t3
So you've loaded $s0 with the contents of the memory starting 224 bytes past $sp. If I'm understanding correctly, that's the first four bytes of a string. Meaning, if the string is "Madam, I'm Adam", that $s0 contains 'a' 'd' 'a' 'M' (if I've got my endianness right).
So when you do lbu $t3, ($s0), you're not loading the current character into $t3. You're loading the byte pointed to by $s0, which is going to be a random spot in memory. If your string is blank, $s0 might contain 0x00000000 - and thus would generate the error you're getting.
I think what you want to do, instead of lw, is addi $s0, $sp, 224. Then, $s0 will point to the address at $sp + 224.
I'm working on implementing a basic checksum algorithm for an inputted string in MIPS assembly as a general introduction to working in the language, and I could use some error checking.
Here's what I have so far, comments included to keep track of what's happening each step:
.data
str1:
.asciiz "This is a short string."
cs1:
.word 0x84a
str2:
.asciiz "This is a much longer string. In fact, it has two sentences in it, and some funny characters (~`)€."
cs2:
.word 0x230a
NonSuccessString:
.asciiz "You have more work to do."
Test1Success:
.asciiz "Test 1 was successful. "
Test2Success:
.asciiz "Test 2 was successful. "
.text
la $a0, str1 #Set $a0 to address of label str1:
jal checksum #Set $ra to address of next instruction, then jump to label checksum:
la $s0, cs1 #Set $s0 to address of label cs1:
lw $s1, 0($s0) #Set $s1 to contents of memory address $s0
beq $v0, $s1, Success1 # If $v0 and $sl are equal, jump to label Success1:
j NonSuccess # Jump to label NonSuccess:
Success1:
la $a0, Test1Success #Set $a0 to address of label Test1Success:
addi $v0, $zero, 4
syscall
la $a0, str2 #Set $a0 to address of label str2:
jal checksum #Set $ra to return address, then jump to label checksum:
la $s0, cs2 #Set $s0 to address of label cs2:
lw $s1, 0($s0) #Set $s1 to contents of memory address $s0
beq $v0, $s1, Success2 # If $v0 and $sl are equal, jump to label Success2:
j NonSuccess # Jump to label NonSuccess:
Success2:
la $a0, Test2Success #Set $a0 to address of label Test2Success:
addi $v0, $zero, 4
syscall
j Quit # Jump to label Quit:
NonSuccess:
la $a0, NonSuccessString #Set $a0 to address of label NonSuccessString:
addi $v0, $zero, 4
syscall
Quit:
addi $v0, $zero, 10
syscall
checksum:
addi $sp, $sp, -4 # adjust stack for 1 item
sw $s0, 0($sp) # save $s0
add $s0, $zero, $zero # i = 0
L1:
add $t1, $s0, $a0 # Locate the character at y[i]
lbu $t2, 0($t1) # Load the unsigned byte value of y[i]
beq $t2, $zero, L2 # exit loop if y[i] == 0
add $v0, $v0, $t2 # Add the byte to the total value
addi $s0, $s0, 1 # i = i + 1
j L1
L2:
jr $ra #jump back to $ra
The simple string in the first test calculates correctly and I get the first "Test 1 was successful." message, but on the second checksum the debug reports a sum of 0x230e, 4 more than the expected value, meaning something wonky happened. I'm guessing the special characters are probably causing the issue, but I don't know for sure. Any ideas?
You're not resetting $v0 prior to calculating the checksum. There should be an add $v0,$zero,$zero before the L1: label.
But I still don't see where the value 0x230a comes from. It seems to me like the correct checksum for the second string is 0x22de.
I have a small problem with a program i'm trying to write. Basically i'm prompting a user for input. I then count the number of spaces in the string, and display the count. I also need to print out the original string with spaces removed.
The bug I'm having is when i'm adding characters to my string without spaces, it only adds the first character. $t2 is the register that is holding the character to be added, and I've checked the registers during run-time to be sure the value there was changing. I don't know where else to look.
.data
str: .space 81 # buffer for input string
strNS: .space 81 # buffer for string w/o spaces
prompt: .asciiz "Enter a string up to 80 characters\n"
head1: .asciiz "\nOriginal String: "
head2: .asciiz "\nNumber of spaces: "
head3: .asciiz "\nWith spaces removed: "
.text
main:
#print the first prompt and get the input string from console
li $v0, 4 #load syscall value to print string into $v0
la $a0, prompt #address of prompt to print
syscall #print prompt to console
li $v0, 8 #load syscall value to read input string
la $a0, str #addr of allocated space for input string is now in $a0
li $a1, 81
syscall
jal countSpace
addi $t1, $v0, 0 #the count of spaces is in $v0, save it into $t1
li $v0, 4 #print header then the count
la $a0, head1
syscall
la $a0, str #print the original string
syscall
la $a0, head2 #print second header before printing count
syscall
li $v0, 1
addi $a0, $t1, 0 #place the count in $a0
syscall #print the count
li $v0, 4
la $a0, head3 #print the third header
syscall
la $a0, strNS #print no spaces string
syscall
End:
li $v0, 10 #load syscall value for exit
syscall #exit
countSpace:
la $s0, strNS
addi $sp, $sp, -12 #adjust the stack pointer for saving
sw $s0, 8($sp) #store addr of nospace string
sw $ra, 4($sp) #store return addr on the stack
sw $a0, 0($sp) #store the count on the stack
#Begin counting spaces
addi $t3, $a0, 0 #$t3 has addr of user input
addi $t5, $s0, 0 #$t5 has addr of string with no spaces
li $t6, 0 #$t6 holds index of string with no spaces
li $t0, 0 #$t0 will hold the count of spaces
li $t4, 0 #$t4 holds the index of the string
loop:
add $t1, $t3, $t4 #$t1 = addr of str[i]
lb $t2, 0($t1) #$t2 = character in str[i]
beq $t2, $zero, exitCS #break from loop if $t2 contains null character
addi $a0, $t2, 0 #place value to be checked in $a0
#save values onto stack from temp registers to preserve them
addi $sp, $sp, -28 #adjust the stack pointer for 5 values
sw $t6, 24($sp) #save index of string with no spaces
sw $t5, 20($sp) #save addr of string with no spaces
sw $t4, 16($sp) #save index of user input
sw $t3, 12($sp) #save the addr of user input
sb $t2, 8($sp) #save the character in str[i]
sw $t1, 4($sp) #save the address of str[i]
sw $t0, 0($sp) #save the count of spaces
jal isSpace #result from this jump and link will be in $v0 after call
#pop saved values from the stack, then reset the pointer
lw $t6, 24($sp)
lw $t5, 20($sp)
lw $t4, 16($sp)
lw $t3, 12($sp)
lb $t2, 8($sp)
lw $t1, 4($sp)
lw $t0, 0($sp)
addi $sp, $sp, 28 #reset stack pointer
beq $v0, $zero, addTo #if not a space, continue to next character
addi $t0, $t0, 1 #if it is a space, increment count
addTo:
bne $v0, $zero, nextChar #If character is a space, branch
sll $t7, $t6, 2 #index if nospaces string stores width of 4
add $t7, $t7, $t5 #now $t7 points at nospaces[i]
sb $t2, 0($t7) #store the character in the nospaces string
addi $t6, $t6, 1 #increment the index of nospaces
nextChar:
addi $t4, $t4, 1 #increment the index value
j loop #jump back to loop and continue processing
exitCS:
addi $v0, $t0, 0 #count of spaces placed into $v0
addi $v1, $t5, 0
lw $ra, 4($sp) #load return addr from the stack
lw $a0, 0($sp) #load value to check from the stack
addi $sp, $sp, 8 #reset stack pointer
jr $ra #return
isSpace:
addi $sp, $sp, -12 #adjust stack pointer to make room
sw $s0, 8($sp)
sw $ra, 4($sp) #store value of return addr onto stack
sw $a0, 0($sp) #store value to check onto stack
#Check to see if the character is a space
li $t0, 32 #ascii value for space character loaded into $t0
li $v0, 0 #Set default return to 0, or "not a space character"
bne $t0, $a0, endSC #if ascii values match, character is a space
li $v0, 1 #$v0 = 1 means it is a space character
endSC:
lw $s0, 8($sp)
lw $ra, 4($sp) #restore return address
lw $a0, 0($sp) #restore addr of str
addi $sp, $sp, 12 #reset the stack pointer
end: jr $ra
Change sll $t7, $t6, 2 to move $t7, $t6. There's no need to align anything manually.
Here's the data segment of your program after a run with the input here's some spaces. Once you see the bug, the fix should be obvious.