CS61C Summer 2004

Project 02
Attack of the sprintf MIPS Clones

Purpose

The purpose of this lab is to familiarize you with the details of the MIPS calling convention and pump up your assembly programming skills.

Administrative Requirements

To submit your project, create a directory named proj2 that contains your sprintf.s file. From within that directory, type "submit proj2". As usual, the project will be due by 11:59pm on Tuesday, July 20. This is an individual project, not to be done in partnership. Hand in your own work, and do not collaborate with anyone else.

Disclaimer

This document mentions certain test cases. However, this input/output behavior is not a comprehensive specification of all possible behavior. Therefore, even if your project submission passes all of the test cases mentioned here, it may not be entirely correct. We strongly urge you to create test cases in addition to the ones provided here.

Background

The MIPS procedure calling convention uses registers $a0-$a3 for passing arguments down to procedures. If there are more than four, the remaining arguments are passed on the stack. Each argument gets one word of stack space. Suppose we are trying to write in MIPS assembler a program like this:

int foo (int x, int y, int quux, int bar, int baz) {

     int a, b;
     ...
     a = y;
     ...
}

int main () {
     int c, d;
     ...
     foo (3, 4, 43, 62, 1);
     ...
}
Procedure foo has five integer arguments. The space for those arguments is allocated on the stack as part of the caller's stack frame. In other words, main , not foo , must allocate the space. The arguments go at the bottom of main's stack frame. That is, main will use 0($sp) to hold the argument x and 4($sp) to hold the argument y. The first argument is always at the top of the stack. you have to be consistent about this so that foo knows which argument is which.
main:
        ...
        addi $t0, $0, 3     # assume this holds the value of c
        addi $t1, $0, 4     # assume this holds the value of d
        ...
        addi $sp, $sp, -32  # make stack space for 8 words: 
                            # $ra, c, d, and 
                            # the args x, y, quux, bar, baz
        sw   $ra, 28($sp)   # save $ra
        sw   $t0, 24($sp)   # save c before calling foo
        sw   $t1, 20($sp)   # save d before calling foo
        add  $a0, $0, $t0   # arg x = c
        add  $a1, $0, $t1   # arg y = d
        addi $a2, $0, 43    # arg quux = 43
        addi $a3, $0, 62    # bar = 62
        addi $t0, $0, 1     # baz = 1, but no more registers
        sw   $t0, 16($sp)   # so pass on the stack
        jal  foo
        ...
        addi $sp, $sp, 32   # restore stack space
        lw   $ra, -4($sp)   # reload return address
        jr   $ra            # return to caller

foo:    addi $sp, $sp, -12  # make stack space for 3 words:
                            # $ra, a, b
        sw   $ra, 8($sp)    # save $ra
        ...
        add  $t0, $0, $a1   # get argument y
        lw   $t1, 28($sp)   # *** (see below)
                            # 12 (foo's frame) + 16 = 28 up on stack
                            # fetched argument baz
        ...
        addi $sp, $sp, 12   # restore stack space
        lw   $ra, -4($sp)   # reload return address
        jr   $ra            # return to caller

The instruction indicated by "***" is the key to understanding the stack method of argument passing. Procedure foo is referring to a word of stack memory that is from the caller's stack frame. Its own frame includes only the three words 0($sp) , 4($sp) , and 8($sp).

Description

Write a MIPS assembly language implementation of the C function sprintf:

int sprintf (char *outbuf, char *format, ...)

sprintf works like printf , except that it writes to the string outbuf instead of to standard output. outbuf is assumed already to point to allocated memory sufficient to hold the generated characters. Your function must accept any number of arguments, passed according to MIPS standard conventions: Stack space is allocated for all the arguments, but the first four arguments are passed in registers anyway; their stack space is unused. The first four arguments are passed in the $a0-$a3 registers, and the rest are passed on the stack.

The first argument is the address of a character array into which your procedure will put its results. The second argument is a format string in which each occurrence of a percent sign (%) indicates where one of the subsequent arguments is to be substituted and how it is to be formatted. The remaining arguments are values that are to be converted to printable character form according to the format instructions. sprintf returns the number of characters in its output string not including the null at the end.

You do not have to do any error checking (e.g. comparing the number of arguments to the number of % specifications). You also do not have to implement all of the formatting options of the real sprintf. Here are the ones you are to implement:

Don’t implement width or precision modifiers (e.g., %6d ). Copy the two files sprintf.s and spf-main.s. Or you can grab the files from your accounts in the folder ~cs61c/lib/proj2/ . Only your sprintf.s will be graded. You should modify spf-main.s to test your code more thoroughly. To run this project, you need to load two files in the correct order: run xspim and load spf-main.s. Next load sprintf.s. Finally, run your program.

Miscellaneous Requirements


Extra notes to help you:


Shamelessly stolen by Carolen Maganito <cs61c-tc@imail.eecs.berkeley.edu>. (*note from Carolen* curse you all! ::shaking fist wildly at all the TAs::) All rights reserved. No refunds, exchanges, or substitutions. Objects in mirror closer than they appear. Thank you for submission. You should be hearing from us soon. Safeway. Doing Our Best(tm). Only One Man Saw It Coming.