sections in this module City College of San Francisco - CS270
Computer Architecture

Module: MIPS-IV (Procedures)
module list

Problems-Procedures

The code for each of these problems is in the online/mipsIV directory. The code no longer uses syscalls. Instead they use the functions from the util.s package detailed in the SupportFunctions PDF file.

This first function is a slight modification from its counterpart in the mipsIII problems - arradd.s. In this version, a function is called to do the addition so that arradd() is not a leaf function.

arradd.s

int arradd(int result[], int arr1[], int arr2[], int nelem)

This function adds two arrays, placing the sum of each corresponding element in a third (result) array. It uses another function add, which adds two ints. We will see how silly this is in assembler, but, remember, a simple straightforward translation of some integer array class with a member function that adds two elements could be similar. It doesn't look bad in higher-level code, but it is very different in assembler.

First, add1.s is a leaf function that simply adds together two integers. It would be called like

result[index] = add1(arr1[index],arr2[index]);

#
# add1.s
#
# int add1(int,int);
# (so named as MARS reserves instruction names)
#
    .text
    .globl add1
add1:
    add $v0,$a0,$a1
    jr  $ra

Now the code for arradd:

#
# arradd.s - add two arrays and store
#    the sums of each pair of elements
#    in a parallel third array
#
#  int arradd(int *result, int *arr1, int *arr2, int nelem);
#
#  returns 0 (fail) iff one of result, arr1 or arr2 is NULL
#
    .text
    .globl arradd
arradd:
    # uses one S register and needs room for ra and its arguments.
    addiu   $sp,$sp,-24
    sw      $a3,36($sp) # home all of the arg regs
    sw      $a2,32($sp)
    sw      $a1,28($sp)
    sw      $a0,24($sp)
    sw      $ra,20($sp)  # save return address
    sw      $s0,16($sp)  # to keep index in register
    li      $v0,1       # default failure status
    # fail if any pointer is NULL or if nelems is <= 0
    beq     $a0,$zero,.Larrbad
    beq     $a1,$zero,.Larrbad
    beq     $a2,$zero,.Larrbad
    ble     $a3,$zero,.Larrbad
    # finally ready to start
    li      $s0,0       # index
.Larrloop:
    beq     $s0,$a3,.Larrdone   # if index == nelems we are done
    sll     $t4,$s0,2   # convert index to nbytes
    addu    $t0,$a1,$t4 # &arr1[index]
    lw      $a0,0($t0)  # arr1[index]
    addu    $t0,$a2,$t4 # &arr2[index]
    lw      $a1,0($t0)  # arr2[index]
    jal     add1
    lw      $a3,36($sp) # retrieve args from home loc.
    lw      $a2,32($sp)
    lw      $a1,28($sp)
    lw      $a0,24($sp)
    sll     $t4,$s0,2
    addu    $t0,$a0,$t4 # & result[index]
    sw      $v0,0($t0)  # result[index] = $v0
    addi    $s0,$s0,1   # index++
    b       .Larrloop
.Larrdone:
    li      $v0,0
.Larrbad:
    lw      $ra,20($sp)
    lw      $s0,16($sp)
    addu    $sp,$sp,24
    jr      $ra

The code is absolutely ridiculous in assembler. If the add instruction was put back into arradd() instead of calling the add1 function (called inlining the function), what percentage of the instructions would be saved? Calculate it for an array length of 10 elements. (The instructions above in bold face indicate the instructions that would be omitted if an appropriate add instruction was used instead of a call to a function. A quick estimate is 30% savings by inlining the function.)

The code for add1.s, arradd.s and the code with a main program, arradd_all.s is in the online/mipsIII directory.

strcat2.s

The function strcat2() in strcat2.s takes two strings of any length. strcat2() allocates space for a new string sufficient in size to concatenate the two strings together using the actual length of the two strings, then concatenates the strings and returns a pointer to the new string.

The strcat2() function calls the function int strlen(char *str) to get the length of each string. strlen() is part of util.s, but is copied here as well.

strcat2 is a bit complicated because it will accept strings entered by MARS syscalls as well as the functions in util.s. Since MARS' syscalls have a habit of leaving newlines on the end of strings (which the util.s functions clean up), strcat2 must search for either the newline or nul.

Let's write strcat in C first:


char * strcat2( char *first, char * second) {
int len = strlen(first) + strlen(second) + 1;
char *result=malloc(len);
for (temp=result; *first && (*first != '\n'); temp++,first++)
*temp = *first;
for (;*second && (*second != '\n'); temp++, second++)
*temp = *second;
*temp=0;
return (result);
}

Let's uglify it next:

char * strcat( char *first, char * second) {
//int len = strlen(first) + strlen(second) + 1;
int len; char *result, *temp, ch;
len=strlen(first);
len += strlen(second);
len += 1;

//char *result=malloc(len);
result = malloc(len);

//for (temp=result; *first && (*first != '\n'); temp++,first++)
// *temp = *first;

temp=result;
Lstrcatloop1:
ch = *first;
if (ch == 0) goto Lstrcatdone1;
if (ch == '\n') goto Lstrcatdone1;
*temp=ch;
temp++;
first++;
goto Lstrcatloop1;
Lstrcatdone1:

//for (;*second && (*second != '\n'); temp++, second++)
//*temp = *second;
Lstrcatloop2:
ch = *second;
if (ch == 0) goto Lstrcatdone2;
if (ch == '\n') goto Lstrcatdone2;
*temp=ch;
temp++;
second++;
goto Lstrcatloop2;
Lstrcatdone2:

*temp=0;
return (result);
}

This is going to be a lot of assembler code. To avoid confusion, the code below has limited C code inserted, and it is only from the uglified version.

#
# strcat2.s - implements the function
#   char * strcat2( char *first, char * second) {
#
    .globl strcat2
strcat2:
# int len; char *result, *temp, ch;
#   Need several registers. result and len must be active past
#   calls (s registers). temp and ch can be t-registers. Not a leaf
#   so it needs arg space (2 s-regs + ra + arg space = 28 bytes)
    addiu   $sp,$sp,-28
    sw      $a1,32($sp)
    sw      $a0,28($sp)
    sw      $ra,24($sp)
    sw      $s1,20($sp)  # len
    sw      $s0,16($sp)  # result
    # use $t9 for temp and $t8 for ch
    #
#len=strlen(first);
    jal     strlen
    move    $s1,$v0
#len += strlen(second);
    move    $a0,$a1
    jal     strlen
    add     $s1,$s1,$v0
#len += 1;
    addi    $s1,$s1,1

#result = malloc(len);
    move    $a0,$s1
    jal     malloc
    move    $s0,$v0

# reload $a0 and $a1 after all the calls are done
    lw      $a1,32($sp)
    lw      $a0,28($sp)

#temp=result;
    move    $t9,$s0
Lstrcatloop1:
#ch = *first;
    lb      $t8,0($a0)
#if (ch == 0) goto Lstrcatdone1;
    beq     $t8,$zero,Lstrcatdone1
#if (ch == '\n') goto Lstrcatdone1;
    li      $t1,'\n'
    beq     $t8,$t1,Lstrcatdone1
#*temp=ch;
    sb      $t8,0($t9)
#temp++;
    addi    $t9,$t9,1
#first++;
    addi    $a0,$a0,1
#goto Lstrcatloop1;
    b       Lstrcatloop1
Lstrcatdone1:

Lstrcatloop2:
#ch = *second;
    lb      $t8,0($a1)
#if (ch == 0) goto Lstrcatdone2;
    beq     $t8,$zero,Lstrcatdone2
#if (ch == '\n') goto Lstrcatdone2;
    li      $t1,'\n'
    beq     $t8,$t1,Lstrcatdone2
#*temp=ch;
    sb      $t8,0($t9)
#temp++;
    addi    $t9,$t9,1
#second++;
    addi    $a1,$a1,1
#goto Lstrcatloop2;
    b       Lstrcatloop2
Lstrcatdone2:
#*temp=0;
    sb      $zero,0($t9)
#return (result);
    move    $v0,$s0
#}
    lw      $ra,24($sp)
    lw      $s1,20($sp)
    lw      $s0,16($sp)
    addiu   $sp,$sp,28
    jr      $ra

This code above does not include the main program, which should request two strings from the user and place them in static storage (with a max length of 100). It then calls strcat2 with the strings and prints the result. Using functions from util.s, the main program is fairly simple. It is shown below:

    .data
prompt: .asciiz "Enter the string:"
resultprompt: .asciiz "The concatenated string is: "
    .text
    .globl main
main:
    # the main program simply:
    #   1. allocates two buffers using malloc
    #   2. requests a string from the user, placing the result in each buffer
    #   3. calls strcat2 to concatenate them
    #   4. outputs the result.
    #
    # note: it does not check for the user to enter q for quit or if they
    # entered an empty string.
    #
    # uses $s0 and $s1 to hold 80-character buffers
    addiu   $sp,$sp,-28
    sw      $ra,24($sp)
    sw      $s1,20($sp)
    sw      $s0,16($sp)
    li      $a0,80
    jal     malloc
    move    $s0,$v0
    li      $a0,80
    jal     malloc
    move    $s1,$v0
    #
    # ask for the two strings using the same prompt
    #
    la      $a0,prompt
    move    $a1,$s0
    li      $a2,80
    jal     InputDialogString
    la      $a0,prompt
    move    $a1,$s1
    li      $a2,80
    jal     InputDialogString
    #
    # concatenate the strings
    #
    move    $a0,$s0
    move    $a1,$s1
    jal     strcat2
    #
    # now output the result
    #
    la      $a0,resultprompt
    move    $a1,$v0
    jal     MessageDialogString
    #
    # that's it
    #
    # we should free our three strings by calling free, but this is moot
    # when using Mars. You can add that code if you want.

    lw      $ra,24($sp)
    lw      $s1,20($sp)
    lw      $s0,16($sp)
    addiu   $sp,$sp,28
    jr      $ra

The writing of the main program is left up to you.

The strcat2 function is in strcat2.c, strcat2-1.c and strcat2.s in the online/mipsIV directory. The file strcat2-all.s includes strcat2.s and the main program. It, of course, uses functions from util.s

You could more easily write strcat2.s using strcpy()


Prev This page was made entirely with free software on linux:  
Kompozer
and Openoffice.org    
Next

Copyright 2016 Greg Boyd - All Rights Reserved.