The purpose of this lab is 1) to introduce subroutines and 2) to learn to integrate C-code and assembly code.
In this lab, you are given a main function in C (download here).
You will be required to write an assembly function called 'printn'. The definition of this function should look as follows:
void printn ( char *fmt, ... ) ; /* Where ... is a comma-separated list of integers */
The first parameter is a string which defines two things i) how many numbers are to be printed as determined by the size of the string - note the end of a string is indicated by a zero ('\0') character, and ii) in which format will they be printed in (i.e. o - octal, h - hexadecimal, or d - decimal). The second and subsequent parameters are the integers you want to print. WARNING: the number of integers should match the size of the string.
For example, let's use printn to print the number 10 in octal, hexadecimal, and decimal. The C function call would look like:
printn("ohd",10,10,10); /* In C, all strings are terminated with a zero byte */
The output should then be:
12 a 10
Along with the C main function are three helper functions to help you with the printing. There is a helper function for printing in each of octal, hexadecimal, and decimal. You are to call these functions from your assembly 'printn' function. The prototypes are below:
void printOct ( int val ); void printHex ( int val ); void printDec ( int val );
In summary, the main program will be responsible for calling your 'printn' function. Then your 'printn' function will need to call a proper print function from one of printOct, printHex, or printDec with the appropriate parameter, as shown in the diagram below:
You will have to determine how the registers and stack are used by the compiler (these conventions form part of the Application Binary Interface or ABI), and making your code consistent with it. Read the sections on "Register Usage" and "Stacks" starting on page 120 of the Nios II Processor Reference Handbook. Study the tables and diagrams there.
You can also use the "make disasm" command to view the output of the compiler. Even with an empty printn function, you can disassemble the program and view the compiler's output. The full command is shown in the Preparation section below, just change the "compile" to "disasm". A sample output is shown below:
C Function | Generated Assembly |
#define TEXT "d" int main ( ) { char* text = TEXT; printn ( text, 16 ); return 0; } |
main: addi sp,sp,-4 /* allocated space in the stack */ stw ra,0(sp) /* push return address into the stack */ movhi r4,257 /* calculate the location of the text */ addi r4,r4,-27204 /* string, then place it in r4 */ movi r5,16 /* move the value to be printed into r5 */ call printn /* call printn subroutine */ mov r2,zero /* set return value to 0 */ ldw ra,0(sp) /* pop the return address off the stack */ addi sp,sp,4 /* de-allocate space from the stack */ ret /* return */ |
Note that the string parameter is passed as the address of the string in memory in r4, and that 16 gets passed as the 2nd parameter in r5. The example below gives a similar example but for more arguments. Examine it carefully, particularly the arguments that are pushed on the stack and in what order.
C Function | Generated Assembly |
#define TEXT "ddoohh" int main ( ) { char* text = TEXT; printn ( text, 16,17,18,19,20,21 ); return 0; } |
main: addi sp,sp,-16 stw ra,12(sp) movi r2,19 stw r2,0(sp) movi r2,20 stw r2,4(sp) movi r2,21 stw r2,8(sp) movhi r4,0 addi r4,r4,0 movi r5,16 movi r6,17 movi r7,18 call 0 |
Don't forget to declare the printn label as a global symbol since it must be used by another file. Your code should look like this:
.global printn printn: /* your code goes here */ ret |
make SRCS="lab3_main.c lab3_printn.s" JTAGOBJ=jtag.o compile
printn("ooohhhddd",8,9,10,11,12,13,14,15,16);
r4 | r5 | r6 | r7 | ra |
  |   |   |   |   |
0(sp)  | 4(sp)  | 8(sp)  | 12(sp) | 16(sp) | 20(sp) | 24(sp) |
  |   |   |   |   |   |   |
Demonstrate your working program on the DE2. To run the program type in the same make command as above, but change the "compile" to "run". To view the printed output, you must open a second Nios II Command Shell, go to your directory, and type "make terminal"