PROJECT 2: SPRINTF Submitting your Project: Submit your solution online by 11:59pm on Wednesday, March 3. Do this by creating a directory named 'proj2' that contains a file named sprintf.s. From within that directory, type "submit proj2". Write a MAL implementation of the C function SPRINTF: int sprintf(char *outbuf, char *format, ...) It must accept any number of arguments. (All arguments will be passed on the stack --- more on this below.) 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. 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 implement all of the formatting options of the real SPRINTF. Here are the ones you must implement: %d convert integer to decimal %u convert integer to unsigned decimal %x convert integer to hexadecimal %c include one character argument in result %s include string of characters in result %% include a percent sign in result You do not have to implement width or precision modifiers (e.g., %6d). -------------------- The procedure-calling convention we've been using up to now uses four registers ($4-$7) for passing arguments down to procedures. What if there are more than four arguments? That approach won't work. For the sprintf project you will use an alternative convention, in which all arguments are passed on the stack, not in registers at all. 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 a, b; ... a = y; ... } main() { int c, d; ... foo(3, 4); ... } Procedure FOO has two integer arguments. The space for those arguments will be 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($29) to hold the argument X, and 4($29) to hold the argument Y. (The first argument is always at the bottom of the frame -- you have to be consistent about this so that FOO knows which argument is which.) main: addi $29, $29, -20 # five wds: $31, c, d, arg x, arg y sw $31, 16($29) # save $31 ... addi $8, $0, 3 # first argument value is 3 sw $8, 0($29) # save on stack addi $8, $0, 4 # second argument value is 4 sw $8, 4($29) # save on stack jal foo ... addi $29, $29, 20 jr $31 foo: addi $29, $29, -12 # three wds: $31, a, b sw $31, 8($29) # save $31 ... lw $8, 16($29) # get argument y *** (see below) sw $8, 0($29) # store as a ... addi $29, $29, 12 jr $31 *** This instruction is the key to understanding the stack method of argument passing. Procedure FOO is referring to a word of stack memory that's beyond the boundary of its own stack frame. (Its own frame includes only the three words 0($29), 4($29), 8($29).) It thereby refers to the stack frame of its caller. This is legal only to the extent that the caller's stack frame contains FOO's arguments! (FOO doesn't know what's where on the rest of its caller's stack frame; it doesn't even know which procedure called it.) Note: For this project, although the arguments are passed on the stack, the return value should still be in $2. Your SPRINTF procedure should work with the following main program. (Of course, it should not have any assumptions about this particular main program built into it!) .data buffer: .space 200 format: .asciiz "%d%% of all %ss say %d (%u) %c %x!\n" str: .asciiz "American" chrs: .asciiz " characters:\n" .text __start:addi $sp, $sp, -36 la $8, buffer sw $8, 0($sp) la $8, format sw $8, 4($sp) addi $8, $0, 87 sw $8, 8($sp) la $8, str sw $8, 12($sp) addi $8, $0, -5002 sw $8, 16($sp) sw $8, 20($sp) addi $8, $0, 60 sw $8, 24($sp) addi $8, $0, 3840 sw $8, 28($sp) jal sprintf add $4, $2, $0 jal putint puts chrs puts buffer addi $sp, $sp, 36 done putint: addi $sp, $sp, -8 sw $31, 0($sp) rem $8, $4, 10 addi $8, $8, '0' div $4, $4, 10 beqz $4, onedig sw $8, 4($sp) jal putint lw $8, 4($sp) onedig: putc $8 lw $31, 0($sp) addi $sp, $sp, 8 jr $31 This program is online in ~cs61c/lib/spf-main.s Copy this file to your proj2 directory, but don't modify it. Place your sprintf function in sprintf.s. To run this project in xspim, you need to load two files in the correct order. Load spf-main.s, then load sprintf.s, and then run your program. P.S. The *real* MIPS argument passing convention is a combination of the two we've used. Stack space is allocated for all the arguments, but the first four arguments are passed in registers anyway; their stack space is unused. NOTE: The following labels are used by the autograder test cases. Please avoid using them as it will cause your code to fail the autograder: __start buffer chrs format line onedig putint str str1 str2