The purpose of this lab is to familiarize you with the details of
the MIPS calling convention and pump up your assembly programming
skills.
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.
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.
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).
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.