I working on a mips code as extra credit for my Computer Organization and assembly class. The instructions are:
Write a program) that will define the following string manipulation functions. Each is analogous to the
corresponding C++ string function.
STRLEN( Str &, Len &) Sent the address of a string, it returns the length in a call-by-reference param.
STRCPY( Dest &, Source & ) Sent the address of the Source string, it places a copy of it in the Destination
string.
STRCAT( Dest &, Source & )Sent the address of the Source String, a copy of it is attached to the end of
the Destination string.
The test program will do at least the following:
S1 = “I ”
S2 = “love “
S3 = “assembly “
S4 = S1 + S2 + S3 (using STRCAT)
S5 = S3 (using STRCPY)
S6 = S4 (using STRCPY )
L1 = STRLEN( S1)
L4 = STRLEN( S4)

However, when I attempted to get the string length for S1, instead of getting 1, I get 0 raised to the first power. So, I stopped on that one and attemped to copy S3 into S5, but when I did, I got errors. What am I doing wrong? Also, how would I go about S4? I've looked online and seen how to combine two strings, but I don't know how to concatenate three strings. Any help will be appreciated, as I have to submit this for bonus points by midnight on sunday.

    .data
    S1: .asciiz "I "
    S2: .asciiz "love "
    S3: .asciiz "assembly"
    string: .asciiz "Our three strings are:\n"
    S4: .asciiz "\nThe three strings combined are: "
    S5: .asciiz "\nWhen string 3 is copied into string 5, string 5 is: "
    S6: .asciiz "\nWhen string 4 is copied into string 6, string 6 is: "
    L1: .asciiz "\nThe length of the first string is: "
    L2: .asciiz "\nThe length of string 4 is: "
    .text
    main:
    #display all three strings first
    li $v0, 4
    la $a0, string
    syscall
    la $a0, S1
    syscall
    la $a0, S2
    syscall
    la $a0, S3
    syscall

    la $a0, S3
    jal StrCpy
    la $v0, 4
    la $a0, S5
    la $a0, S1 #load address of string
    jal strlen #call string length procedure
    jal print
    addi $a1, $a0, 0 #move address of string to $a1
    addi $v1, $v0, 0 #move length of string to $v1
    addi $v0, $0, 11 #syscall code for message
    la $a0, L1 #address of message
    syscall
    li $v0, 10
    syscall

StrCpy:
    add $t0, $zero, $zero
loop: add $t1, $a1, $t0
    lb $t2, 0($t1)
    beq $t2, $zero, out
    add $t3, $a0, $t0
    sb $t0, 0($t3)
    addi $t0, $t0, 1
    j loop
out:
    add $t3, $a0, $t0
    sb $zero, 0($t3)
    jr $ra



     strlen:
    addi $t0, $t0, 1 #initialize count to start with 1 for first character

       loop1:
    lb $t0, 0($a0) #load the next character to t0
    addi $a0, $a0, 1 #load increment string pointer
    addi $t0, $t0, 1 #increment count
    beqz $t1, exit1 #end loop if null character is reached
    j loop1 # return to top of loop

    exit1: jr $ra

    print:
    li $v0, 4
    la $a0, L1
    syscall
    li $v0, 1
    move $a0, $t1
    syscall
    jr $ra

I have made changes, to where now I am able to get the length of S1. However, whenever I try to copy S3 into S5, I get 'When String 3 is copied into String 5, String 5 is: 1M', 'when it should be assembly, I posted the question on another forum, and they told me it was similar to obtaining the string length. What exactly am I doing wrong? Do I need to increase the stack pointer in print 2

.data
S1: .asciiz "I "
S2: .asciiz "love "
S3: .asciiz "assembly"
string: .asciiz "\nOur three strings are:\n"
S4: .asciiz "\nThe three strings combined are: "
S5: .asciiz "\nWhen string 3 is copied into string 5, string 5 is: "
S6: .asciiz "\nWhen string 4 is copied into string 6, string 6 is: "
L1: .asciiz "\nThe length of the first string is: "
L2: .asciiz "\nThe length of string 4 is: "
.text
main:

#display all three strings first
    li $v0, 4
    la $a0, string
    syscall
    la $a0, S1
    syscall 
    la $a0, S2
    syscall
    la $a0, S3
    syscall
    la $a0, S1 #load address of string
    jal strlen1 #call string length procedure
    move $a0, $v0
    jal print1
    addi $a1, $a0, 0 #move address of string to $a1
    addi $v1, $v0, 0 #move length of string to $v1
    addi $v0, $0, 11 #syscall code for message
    la $a0, L1 #address of message
    syscall
    la $a0, S3
    jal strcopy1
    jal print2
    addi $a1, $a0, 0 #move address of string to $a1
    addi $v1, $v0, 0 #move length of string to $v1
    addi $v0, $0, 11 #syscall code for message
    la $a0, S5 #address of message
    syscall
    li $v0, 10
    syscall

    strlen1:
    addi $t0, $0, 1 #initialize count to start with 1 for first character
    j strlen1.test

strlen1.loop:
    addi $a0, $a0, 1 #load increment string pointer
    addi $t0, $t0, 1 #increment count
strlen1.test:
    lb $t1, 0($a0)
    beqz $t1, strlen1.loop #end loop if null character is reached
    move $v0, $t0

    jr $ra
print1: 
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)

    move $s0, $a0 #store argument in a save register
    li $v0, 4
    la $a0, L1
    syscall

    li $v0, 1
    move $a0, $s0 # get argument from $s0
    syscall

    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12

    jr $ra

strcopy1:
    addi $t0, $0, 1 #initialize count to start with 1 for first character, to copy over
    j strcopy1.test

strcopy1.loop:
    addi $a0, $a0, 1 #load increment string pointer
    addi $t0, $t0, 1 #increment count
strcopy1.test:
    lb $t1, 0($a0)
    beqz $t1, strcopy1.loop #end loop if null character is reached
    move $v0, $t0

    jr $ra

print2: 
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)

    move $s0, $a0 #store argument in a save register
    li $v0, 4
    la $a0, S5
    syscall

    li $v0, 1
    move $a0, $s0 # get argument from $s0
    syscall

    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12

    jr $ra

You are using beqz for the tests, but you want to use bnez (which is actually a macro for bne x, $zero, label, but both SPIM and MARS should support it). You want to loop when the value is not equal to zero; right now, you are looping only if it is zero.

That changed it, to where now it displays 'When String 3 is copied into String 5, String 5 is: 9M'. But I'm trying to get it to display 'When String 3 is copied into String 5, String 5 is: assembly"

OK, where is the S1 string being copied to again? Think about it for a bit, and consider what, if anything, is already there.

Edited 2 Years Ago by Schol-R-LEA

I'm going to try working on the strcat for S4 right now, because when I try to do the copy, I'm now getting it to display 'When string 3 is copied into string 5, string 5 is: 268501009M'. By working on strcat, copying it and getting its length, I may be able to work out copying S3 into S5. If I still have problems, I'll post them.

To elucidate my earlier post: right now, you have S3 overwriting S5 when you run strcpy1(). You want to declare S5 as a .space of at least 9 bytes (8 plus the zero delimiter), or else have it as a .word (a pointer, in effect) and use system call 9 (memory allocation) to allocate 9 bytes for it. The same is true for S4 and S6 as well, though the sizes will be different. The messages now held by S4, S5, and S6 should be moved to their own locations.

.data
S1: .asciiz "I "
S2: .asciiz "love "
S3: .asciiz "assembly"
S4: .space S4 - S1  # set aside space to hold the three strings concatenated
S5: .space S4 - S3  # set aside enough space for a copy of S3
S6: .space S5 - S4  # set aside enough space for a copy of S4
mesg1: .asciiz "\nOur three strings are:\n"
mesg2: .asciiz "\nThe three strings combined are: "
mesg3: .asciiz "\nWhen string 3 is copied into string 5, string 5 is: "
mesg4: .asciiz "\nWhen string 4 is copied into string 6, string 6 is: "
L1: .asciiz "\nThe length of the first string is: "
L2: .asciiz "\nThe length of string 4 is: "

The sizes may need to be explicitly entered in MARS:

S4: .space 16  # set aside space to hold the three strings concatenated
S5: .space 9   # set aside enough space for a copy of S3
S6: .space 16  # set aside enough space for a copy of S4

Alternately, you use them as pointers:

S4: .word 0
S5: .word 0
S6: .word 0

# later...

li $v0, 9
li $a0, 16
syscall
la $t0, S4
sw $v0, 0($t0)   # save the pointer to the allocated memory

That is probably more work than you want to do, however.

Edited 2 Years Ago by Schol-R-LEA

Comments
I didn't see this post earlier, as I've been working on it. Thanks, I'll try this method.

I've gone back to try copying S3 into S5, and I am getting a wierd display. I've tried the method you gave, and several other methods, using these links http://www.daniweb.c.../strcat-in-mips, http://cs.lamar.edu/...ams/StringEx.s, http://stackoverflow...e-mips-strings. I think it is copying it correctly, the only problem is in the display. Because now, instead of displaying 'When string 3 is copied into string 5, string 5 is: assembly", my display is 'Our three strings are: I love assemblyOur three strings are: I love assembly268501002L The length of the first string is: 2'. I think something is wrong in my main method, or my print method, which is creating this wierd output.

.data
.align 4
S1: .asciiz "I "
S2: .asciiz "love "
S3: .asciiz "assembly"
string: .asciiz "Our three strings are:\n"
S4: .asciiz "\nThe three strings combined are: "
S5: .asciiz "\nWhen string 3 is copied into string 5, string 5 is: "
S6: .asciiz "\nWhen string 4 is copied into string 6, string 6 is: "
L1: .asciiz "\nThe length of the first string is: "
L2: .asciiz "\nThe length of string 4 is: "

.text

main:
    #display all three strings first
    li $v0, 4
    la $a0, string
    syscall
    la $a0, S1
    syscall
    la $a0, S2
    syscall
    la $a0, S3
    syscall
    #display the three string when concatenated 
    #display string 5 when string 3 is copied into it
    la $a0, S3
    la $a1, S5
    jal strcpy
    jal print2
    addi $a2, $a1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $a0, S5
    syscall
    #display string 6, when string 4 is copied into it after concatenation
    #display length of string 1 
    la $a0, S1 #load address of string
    jal strlen #call string length procedure
    move $a0, $v0
    jal print4
    addi $a1, $a0, 0 #move address of string to $a1
    addi $v1, $v0, 0 #move length of string to $v1
    addi $v0, $0, 11 #syscall code for message
    la $a0, L1 #address of message
    #display length of string 4, the concatenated string
    syscall
    li $v0, 10
    syscall

strcpy:
    lbu $t0, 0($a0) #load a byte from source string
    sb $t1, 0($a1) #store byte in destination string
    addi $a0, $a0, 1 #increment both addresses
    addi $a1, $a1, 1
    beqz $t1, cpyExit #stop when null is copied
    j strcpy

cpyExit:
    jr $ra    

print2:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, S5
    syscall
    li $v0, 1
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra


strlen:
    move $t0, $zero #initialize count to start with 0 for first character
    j strlen.test

strlen.loop:
    addi $a0, $a0, 1 #load increment string pointer
    addi $t0, $t0, 1 #increment count

strlen.test:
    lb $t1, 0($a0) #load the next character to t0
    bnez $t1, strlen.loop #end loop if null character is reached
    move $v0, $t0
    jr $ra

print4:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, L1
    syscall
    li $v0, 1
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

Did you read my last post? Part of the problem is that you are overwriting the message strings. However, the copy function is also flawed, though in a way that is easy to overlook:

strcpy:
    lbu $t0, 0($a0) #load a byte from source string
    sb $t1, 0($a1)  #  <-- the source should be $t0, not $t1
    addi $a0, $a0, 1 #increment both addresses
    addi $a1, $a1, 1
    beqz $t1, cpyExit # <--- this should be $t0 as well
    j strcpy

cpyExit:
    jr $ra

Good news is, I have it to where it displays what I need. Bad news it, instead of displaying assembly, it displays 'assemblyu'. I'm curious as to where the 'u' is coming from, if its part of the system, or a minor error in code. I had to change some things to get to this point

.data
.align 4
S1: .asciiz "I "
S2: .asciiz "love "
S3: .asciiz "assembly"
string: .asciiz "Our three strings are:\n"
string4: .space 16
string5: .space 9
string6: .space 16
S4: .asciiz "\nThe three strings combined are: "
S5: .asciiz "\nWhen string 3 is copied into string 5, string 5 is: "
S6: .asciiz "\nWhen string 4 is copied into string 6, string 6 is: "
L1: .asciiz "\nThe length of the first string is: "
L2: .asciiz "\nThe length of string 4 is: "
.text
main:
    #display all three strings first
    li $v0, 4
    la $a0, string
    syscall
    la $a0, S1
    syscall
    la $a0, S2
    syscall
    la $a0, S3
    syscall
    #display the three string when concatenated
    #display string 5 when string 3 is copied into it
    la $s1, string5
    la $s2, S3
    jal strcpy

    jal print2
    addi $a2, $a1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $a0, S5
    syscall
    #display string 6, when string 4 is copied into it after concatenation
    #display length of string 1
    la $a0, S1 #load address of string
    jal strlen #call string length procedure
    move $a0, $v0
    jal print4
    addi $a1, $a0, 0 #move address of string to $a1
    addi $v1, $v0, 0 #move length of string to $v1
    addi $v0, $0, 11 #syscall code for message
    la $a0, L1 #address of message
    #display length of string 4, the concatenated string
    syscall
    li $v0, 10
    syscall

strcpy:
    lbu $t0, 0($s2) #load a byte from source string
    beqz $t0, cpyExit #stop when null is copied
    sb $t0, 0($s1) #store byte in destination string
    addi $s2, $s2, 1 #increment both addresses
    addi $s1, $s1, 1

    j strcpy

cpyExit:
    jr $ra

print2:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, S5
    syscall
    li $v0, 4
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

 strlen:
    move $t0, $zero #initialize count to start with 0 for first character
    j strlen.test
    strlen.loop:
    addi $a0, $a0, 1 #load increment string pointer
    addi $t0, $t0, 1 #increment count
    strlen.test:
    lb $t1, 0($a0) #load the next character to t0
    bnez $t1, strlen.loop #end loop if null character is reached
    move $v0, $t0
    jr $ra

print4:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, L1
    syscall
    li $v0, 1
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

Never mind, I was able to fix it. I just had to change the la $a0, S5 inot $s0, S5. Now I just have to work on concatenating, copying that(I can use the method I just used), as well as getting it's length

As a piece of advice: since the data you are working with is mostly strings, I'd recommend that you do all the computation (i.e., the calls to strcpy(), strcat(), and strlen()) first, then print out the strings in one go. The only thing you can't do this with would be the lengths, which are integers, but you can handle that easily enough by passing the integers as arguments to the print function.

Here's a trick that could save you a bunch of cutting and pasting: put the addresses of (most of) the strings into a fixed array delimited with a zero:

newline: .asciiz "\n"

# this array holds the addresses of the strings to print in the order
# they are to be printed in, all except S6, L1, and L2
print_table: .word string, S1, newline, S2, newline, S3, newline
             .word S4, string4, S5, string5, S6, string6, 0

Then have a loop that walks through the array, printing the strings as it goes:

# use a table-driven loop to print the strings 
# in the appropriate order. 
    la $t0, print_table  # get the beginning of the table of addresses
    li $v0, 4
    j print.test
print.loop:
    syscall
    addi $t0, $t0, 4     # go to the next address in the array
print.test:
    lw $a0, 0($t0)
    bnez $a0, print.loop

This should shorten the code significantly.

Edited 2 Years Ago by Schol-R-LEA

I think my strcat method is right, but I might be overwriting something, because instead of getting 'The three strings combined are: I love assembly', the display shows 'The three strings combined are: assembly'. Did I overwrite something

.data
.align 4
S1: .asciiz "I "
S2: .asciiz "love "
S3: .asciiz "assembly"
string: .asciiz "Our three strings are:\n"
string4: .space 16
string5: .space 9
string6: .space 16
S4: .asciiz "\nThe three strings combined are: "
S5: .asciiz "\nWhen string 3 is copied into string 5, string 5 is: "
S6: .asciiz "\nWhen string 4 is copied into string 6, string 6 is: "
L1: .asciiz "\nThe length of the first string is: "
L2: .asciiz "\nThe length of string 4 is: "
.text
main:
    #display all three strings first
    li $v0, 4
    la $a0, string
    syscall
    la $a0, S1
    syscall
    la $a0, S2
    syscall
    la $a0, S3
    syscall
    #display the three string when concatenated
    la $s1, string4
    la $s2, S1
    la $s3, S2
    la $s4, S3
    jal copy1
    jal print1
    addi $s2, $s1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $s1, S4
    syscall
    #display string 5 when string 3 is copied into it
    la $s1, string5
    la $s2, S3
    jal strcpy    
    jal print2
    addi $s2, $s1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $s0, S5
    syscall
    #display string 6, when string 4 is copied into it after concatenation
    #display length of string 1
    la $a0, S1 #load address of string
    jal strlen #call string length procedure
    move $a0, $v0
    jal print4
    addi $a1, $a0, 0 #move address of string to $a1
    addi $v1, $v0, 0 #move length of string to $v1
    addi $v0, $0, 11 #syscall code for message
    la $a0, L1 #address of message
    #display length of string 4, the concatenated string
    syscall
    li $v0, 10
    syscall

copy1:  lb $t0, ($s2)
    beqz $t0, copy2
    sb $t0,($s1)
    addi $s2, $s2, 1               # string1 pointer points a position forward  
        addi $s1, $s1, 1               # same for finalStr pointer  
        j copy1

copy2: 
    lb $t0, ($s3)
    beqz $t0, copy3
    sb $t0,($s1)
    addi $s3, $s3, 1               
        addi $s1, $s1, 1               
        j copy2

copy3:
    lb $t0, ($s4)
    beqz $t0, cpyExit
    sb $t0,($s4)
    addi $s4, $s4, 1               # string1 pointer points a position forward  
        addi $s1, $s1, 1               # same for finalStr pointer  
        j copy3               
print1:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, S4
    syscall
    li $v0, 4
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

strcpy:
    lbu $t0, 0($s2) #load a byte from source string
    beqz $t0, cpyExit #stop when null is copied
    sb $t0, 0($s1) #store byte in destination string
    addi $s2, $s2, 1 #increment both addresses
    addi $s1, $s1, 1

    j strcpy


cpyExit:
    jr $ra

print2:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, S5
    syscall
    li $v0, 4
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

 strlen:
    move $t0, $zero #initialize count to start with 0 for first character
    j strlen.test
    strlen.loop:
    addi $a0, $a0, 1 #load increment string pointer
    addi $t0, $t0, 1 #increment count
    strlen.test:
    lb $t1, 0($a0) #load the next character to t0
    bnez $t1, strlen.loop #end loop if null character is reached
    move $v0, $t0
    jr $ra
print4:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, L1
    syscall
    li $v0, 1
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

I think you'll have an easier time of it - and a better grade - if you write a general strcat() function and call it three times.

I think my strCat method may be wrong, because it's affecting my output for the entire thing. My S5 and Length of S1 output, which were originally 'assembly' and '2', are now wrong. My display now shows 'Our three strings are: I love assembly The three strings combined are: ssemblyy When string 4 is copied into string 6, string 6 is : ssemblyy The length of string 4 is: 268501001 When string 3 is copied into string 5, string 5 is: ssemblyy The length of the first string is 17a'/
What have I done to cause this, and what must be done to correct it, as well as to get the correct answer for 'The three strings combined are: , When string 4 is copied into string 6: , and the length of string 4'. The first two should be the same answer and length of string 4 should be '15'

.data
.align 4
S1: .asciiz "I "
S2: .asciiz "love "
S3: .asciiz "assembly"
string: .asciiz "Our three strings are:\n"
string4: .space 16
string5: .space 9
string6: .space 16
S4: .asciiz "\nThe three strings combined are: "
S5: .asciiz "\nWhen string 3 is copied into string 5, string 5 is: "
S6: .asciiz "\nWhen string 4 is copied into string 6, string 6 is: "
L1: .asciiz "\nThe length of the first string is: "
L2: .asciiz "\nThe length of string 4 is: "
.text
main:
    #display all three strings first
    li $v0, 4
    la $a0, string
    syscall
    la $a0, S1
    syscall
    la $a0, S2
    syscall
    la $a0, S3
    syscall
    #display the three string when concatenated, as well as copied into S6, and their length
    la $s0, string4
    la $s1, S1
    la $s2, S2
    la $s3, S3
    jal strcatFirst
    jal printConcat
    addi $a2, $a1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $s0, S4
    syscall
    jal printConcatCopy
    addi $s2, $s1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $s0, S6
    syscall
    jal printConcatLength
    addi $a2, $a1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $s0, L2
    syscall
    #display string 5 when string 3 is copied into it
    la $s1, string5
    la $s2, S3
    jal strcpy    
    jal print2
    addi $s2, $s1, 0
    addi $v1, $v0, 0
    addi $v0, $0, 11
    la $s0, S5
    syscall
    #display length of string 1
    la $a0, S1 #load address of string
    jal strlen #call string length procedure
    move $a0, $v0
    jal print4
    addi $a1, $a0, 0 #move address of string to $a1
    addi $v1, $v0, 0 #move length of string to $v1
    addi $v0, $0, 11 #syscall code for message
    la $a0, L1 #address of message
    syscall
    li $v0, 10
    syscall

strcatFirst:
    lb $t0, ($s1)
    beqz $t0, strcatSecond
    sb $t0, ($s0)
    addi $s1, $s1, 1
    addi $s0, $s0, 1
    j strcatFirst

strcatSecond:
    lb $t0,($s2)
    beqz $t0, strcatThird
    sb $t0, ($s1)
    addi $s2, $s2, 1
    addi $s1, $s1, 1
    j strcatSecond

strcatThird:
    lb $t0, ($s3)
    beqz $t0, endStrcat
    sb $t0, ($s2)
    addi $s3, $s3, 1
    addi $s2, $s2, 1
    j strcatThird

endStrcat:
    jr $ra
printConcat:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, S4
    syscall
    li $v0, 4
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

strcpy:
    lbu $t0, 0($s2) #load a byte from source string
    beqz $t0, cpyExit #stop when null is copied
    sb $t0, 0($s1) #store byte in destination string
    addi $s2, $s2, 1 #increment both addresses
    addi $s1, $s1, 1

    j strcpy


cpyExit:
    jr $ra

print2:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, S5
    syscall
    li $v0, 4
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

printConcatCopy:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, S6
    syscall
    li $v0, 4
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

printConcatLength:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, L2
    syscall
    li $v0, 1
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra                
 strlen:
    move $t0, $zero #initialize count to start with 0 for first character
    j strlen.test
strlen.loop:
    addi $a0, $a0, 1 #load increment string pointer
    addi $t0, $t0, 1 #increment count
strlen.test:
    lb $t1, 0($a0) #load the next character to t0
    bnez $t1, strlen.loop #end loop if null character is reached
    move $v0, $t0
    jr $ra
print4:
    addi $sp, $sp, -12
    sw $fp, 8($sp)
    addi $fp, $sp, 16
    sw $ra, 0($fp)
    sw $s0, -4($fp)
    move $s0, $a0 # store the argument in a save register
    li $v0, 4
    la $a0, L1
    syscall
    li $v0, 1
    move $a0, $s0 # retrieve the argument from $s0
    syscall
    lw $s0, -4($fp)
    lw $ra, 0($fp)
    lw $fp, 8($sp)
    addi $sp, $sp, 12
    jr $ra

OK, to begin with, why are you using the Save ($sN) registers to pass arguments, instead of the Arg ($aN) registers? I know it may seem like a trivial distinction, but it isn't; the conventions for whay you use the registers for exist so that different programmers and compilers can work together, and while that may not be an issue here, it is simply bad practice to deviate from the standards without a compelling reason.

In case your professor never went over this, the conventions are:

$t0 - $t7 - temporary registers: a function can use them without saving them for the caller

$s0 - $s7 - save registers: functions should always save these before using them, and restore them afterwards

$a0 - $a3 - argument registers: used for passing the first four parameters of a function (if a function needs more than 4 arguments, then they are passed on the stack)

$v0 - $v1 - value registers: used to pass back the return value of a function. $v1 is usually only used when passing a 64-bit value split between voth registers.

$at       - $assembler temporary register: *exclusively* for use by the assembler, should never be used by programmers

$k0 - $k3 - Kernel register: *exclusively* for use by the operating system, should never be used by application programmers

$sp       - stack pointer: holds the top value on the stack

$fp       - frame pointer: used to track the relativ positions of stack arguments

$ra       - return address: holds the address of a location to return to after the end of a function. Set by JAL as the address of the next instruction after the function call.

Thus, I would recommend changing strcpy() to

strcpy:
    move $v0, $a0           # set the return value
strcpy.loop:
    sb $t0, 0($a0)          # store byte in destination string
    addi $a1, $a1, 1        # increment both addresses
    addi $a0, $a0, 1
strcpy.test:
    lbu $t0, 0($a1)         # load a byte from source string
    bnez $t0, strcpy.loop   # stop when null is copied
strcpy.exit:
    jr $ra

OK, some explanations are in order. First off, the reason I use labels named in the form function.label in all my functions is to make it easier to avoid conflicts: as long as the label has the name of the function in front of it, I don't have to worry about using the same label name in two different functions by mistake. While the label names are longer, I can avoid things like always having to use label1, label2, etc. in different functions. This is called 'namespace control' and it becomes very important in larger programs, so much so that many languages have special rules for handling namespaces, and even some assemblers allow local label names inside functions.

The other thing you are probably wondering about is why I keep turning the conditional on its head like this. The reason is simple: it is more efficient, and efficiency is a big thing in assembly language. Because it only does the one unconditional jump at the beginning and one conditional in the loop, it saves both space and time inside the loop itself.

OK, now I'm finally getting to the original problem, and here I'm going to repeat myself: don't repeat yourself. You already have a function for copying a string, and another for getting the length of a string. What is concatenation? It's copying the source string to the end of the destination string. How do you find the end of the destination string? Why, you add the string's length to its starting pointer. Simple once you think it through, isn't it? The only tricky part is saving the starting value of $a0, so you can return it later:

strcat:
    addi $sp, $sp, -12    # set aside 12 bytes of stack space
    sw $fp, 0($sp)        # save the frame pointer
    sw $ra, 4($fp)        # save the return value
    sw $a0, 8($fp)        # save the first argument

    jal strlen
    lw $a0, 8($fp)        # reload the dest address
    add $a0, $a0, $v0     # add the length of dest to the dest pointer
    jal strcpy            # copy the src string to the end of dest

    lw $v0, 8($fp)        # fetch the original dest address as the return value

    lw $ra, 4($fp) 
    lw $fp, 0($sp)
    addi $sp, $sp, 12     # clear the stack frame

    jr $ra

So why bother with the return value? Because the assignment wasn't to concatenate the strings; that's just the proof that it works. The assignment was to write a version of strlen(), strcpy(), and strcat(), three functions defined in the C standard library. So, in order to make your strcat() work like the C/C++ version, you need to return the original address of the destination string.

I hope I have made a few things simpler for you now.

Edited 2 Years Ago by Schol-R-LEA

Thank you very much. I know this has been difficult, and probably tiresome. Part of the problem is that it's an online class, with vague notes. The other is that this is all new. I do apologize for taking all of this time. Hopefully, though, through your explanataions of how these different registers work, I'll have no more trouble with this code, or any others.

I tried your method above, but now I am getting a new error. It says the error is in the strcat loop at sw $ra, 4($fp). When I run it in mars and spim, it says 'Runtime exception at 0x00400118: address out of range 0x00000004'

Oops, I dropped a line. That should have read:

addi $sp, $sp, -12    # set aside 12 bytes of stack space
sw $fp, 0($sp)        # save the previous frame pointer
move $fp, $sp         # <--- set the frame pointer to the stack pointer
sw $ra, 4($fp)        # save the return value
sw $a0, 8($fp)        # save the first argument

More typically, you would add the number of bytes needed by the save registers tp the stack pointer and put that in the new frame pointer; but in this case, we don't have any save registers being used, so we just want the top of the stack.

Edited 2 Years Ago by Schol-R-LEA

Do I need to include another move, when restoring the stack pointer and use move $sp, $fp, because now on the display I am getting blank spots for 'The three strings combined are: , When String 4 is copied into string 6, string 6 is: , and When string 3 is copied into String 5, string 5 is: '. After each ':', it's just a blank empty space. The length of string 4 is being displayed as '268501009' and the length of string 1 is now displayed as '8a', when we had it fixed to display 2, as well as for S5 to display 'assembly' after S3 is copied into it. Do I need to create a new string copy and print method for the string cat?

Well, forget creating a new strcpy and strlen loop for the strcat. I just tried that, and I'm getting the same thing.

This article has been dead for over six months. Start a new discussion instead.