All that you're missing to write a function is the knowledge on how te return to the caller
Like a good magic trick, the hand is faster than the eyes. In the case of SPARC, the register windows is faster than the eyes....
To understand how to return from a function after the register window has moved, you have to follow the return address very carefully
Let's track the return address starting at the call instruction
Suppose the call instruction is at address X>.
The following diagram depict the situation before the call instruction:
Current Window +---------------+ --+ | | | input Caller: | | | registers +---------------+ | --> .... | | | local X: call Callee | | | registers nop +---------------+ | .... | | | output | | | registers +---------------+ --+ Callee: | | save %sp, -96, %sp | | .... +---------------+ .... | | | | +---------------+ How do we return ??
The following diagram depict the situation after the call instruction but before the save instruction:
Current Window +---------------+ --+ | | | input Caller: | | | registers +---------------+ | .... | | | local X: call Callee | | | registers nop +---------------+ | .... | | | output | o7 = X | | registers +---------------+ --+ Callee: | | --> save %sp, -96, %sp | | .... +---------------+ .... | | | | +---------------+ How do we return ??
The following diagram depict the situation after the save instruction:
Current Window +---------------+ --+ | | | Caller: | | | +---------------+ | .... | | | X: call Callee | | | nop +---------------+ | --+ .... | | | | input | i7 = X | | | registers +---------------+ --+ | Callee: | | | local save %sp, -96, %sp | | | registers --> .... +---------------+ | .... | | | output | | | registers +---------------+ --+ How do we return ??
Remember that we are talking about "how to return from a function that pushed the register down with a save instruction"...
Because the caller did not changed the position of the register window (the callee did it !), the caller must NOT be made responsible to push the register window back.... - that would be illogical (kinda like doing time for someone else's crime...)
Hence: the Callee is responsible for pushing the register windows back up before it returns back to the callee...
You can return from a subroutine in two ways.
Now you may wonder how this is possible.... in fact, this is how the pros do things :-)
The most intuitive way to return from a function that has moved the register windows is as follows:
In this case, the return address will be stored in OUTPUT register o7 when you execute the jmpl instruction and the instruction pair that you need to use to return from a function is:
// The intuitive return sequence
restore (push register windows back)
jmpl %o7+8, %g0 (the return address is in OUTPUT register o7
Follow the return address in the following sequence of diagrams....
The following diagram depicts the situation just before we return (which is the same situation after the save instruction):
Current Window +---------------+ --+ | | | Caller: | | | +---------------+ | .... | | | X: call Callee | | | nop +---------------+ | --+ .... | | | | input | i7 = X | | | registers +---------------+ --+ | Callee: | | | local save %sp, -96, %sp | | | registers .... +---------------+ | .... | | | output | | | registers +---------------+ --+ // The intuitive return sequence --> restore (push register windows back) jmpl %o7+8, %g0 (the return address is in OUTPUT register o7
The following diagram depicts the situation right after the restore instruction:
Current Window +---------------+ --+ | | | input Caller: | | | registers +---------------+ | .... | | | local X: call Callee | | | registers nop +---------------+ | Y: .... | | | output | o7 = X | | registers +---------------+ --+ Callee: | | save %sp, -96, %sp | | .... +---------------+ .... | | | | +---------------+ // The intuitive return sequence restore (push register windows back) --> jmpl %o7+8, %g0 (the return address is in OUTPUT register o7 NOP
A more efficient way to return from a function/subroutine is to exploit the fact that the instruction following a jmpl instruction will be executed
In this case, the return address will be stored in INPUT register i7 when you execute the jmpl instruction and the instruction pair that you need to use to return from a function is:
// The intuitive return sequence
jmpl %i7+8, %g0 (the return address is in INPUT register i7
restore (push register windows back)
Follow the return address in the following sequence of diagrams....
The following diagram depicts the situation just before we return (which is the same situation after the save instruction):
Current Window +---------------+ --+ | | | Caller: | | | +---------------+ | .... | | | X: call Callee | | | nop +---------------+ | --+ Y: .... | | | | input | i7 = X | | | registers +---------------+ --+ | Callee: | | | local save %sp, -96, %sp | | | registers .... +---------------+ | .... | | | output | | | registers +---------------+ --+ // The intuitive return sequence --> jmpl %i7+8, %g0 (the return address is in INPUT register i7 restore (push register windows back)
Since "jmpl %i7+8, %g0" and "jmpl %o7+8, %g0" are used often to return from subroutines, the SPARC assembler has defined 2 "aliases" for these mnemonics:
A "leave" function is a function at the "end of the subroutine calling tree" - i.e., a "leave" function does not call any other functions.
These functions need not move the register windows down (see an example later)
Hence, the mnemonic "retl" is used for "jmpl %o7+8, %g0" which is the instruction that is used to return from a subroutine that does not move the register window down (see: click here )
Let us write the following program in SPARC assembler:
main program: int A[5] = {5, 2, 6, 7, 1}; // Array int s; // Sum s = sum(A, 5); // Call function sum with A and 5 // to compute sum of the array A Write("Sum = "); // Call function Write to print a string WriteInt(s); // Call function WriteInt to print an integer The sum function: int sum(int A[], int n) { int i; int s; s = 0; for (i = 0; i < n; i++) s = s + A[i]; return(s); }
to print a string, we must call the library function Write() with the address of the memory location where the string (ASCII codes) are stored in register o0.
The string (ASCII codes) can be defined as follows:
Str: .ascii "Sum = \0"The special character '\0' is used to denote the "end of the string"
To print an integer, call the library function WriteInt and pass the integer value in register o0.