📄 chapter 10 functions -- valvano.htm
字号:
<B>GetFifo</B> will store the return parameter into a local variable of
<B>InChar</B>. Normally <B>GetFifo</B> does not have the scope to access local
variables of <B>InChar</B>, but in this case <B>InChar</B> explicitly granted
that right by passing a pointer to <B>GetFifo</B>.</FONT></P>
<DIR>
<P><CODE>int GetFifo (char *datapt) { <BR> if (Size == 0 ) <BR> return(0); /* Empty if Size=0 */<BR> else{<BR> asm(" sei"); /* make atomic, entering critical section */<BR> *datapt=Fifo[GetI++]; Size--;<BR> if (GetI == FifoSize) GetI = 0;<BR> asm(" cli"); /* end critical section */<BR> return(-1); }<BR>}<BR>char
InChar(void){ char
data; <BR> while(GetFifo(&data)){};<BR> return
(data);}</CODE></P></DIR>
<ADDRESS>Listing 10-5: Mulitple output parameters can be implemented using call
by reference</ADDRESS>
<P><FONT face="Times New Roman,Times">When we use the <I>call by value</I>
scheme, the values, not references, are passed to functions. With call by value
copies are made of the parameters. Within a called function, references to
formal arguments see copied values on the stack, instead of the original objects
from which they were taken. At the time when the computer is executing within
PutFifo() of the example below, there will be three separate and distinct copies
of the 0x41 data (main, OutChar and PutFifo).</FONT></P>
<DIR>
<P><CODE>int PutFifo (char data) { <BR> if
(Size == FifoSize ) {<BR> return(0);}
/* Failed, fifo was full
*/<BR> else{<BR> Size++;<BR> *(PutPt++)=data;
/* put data into fifo */<BR> if (PutPt ==
&Fifo[FifoSize]) PutPt = &Fifo[0]; /* Wrap
*/<BR> return(-1); /* Successful */
<BR> }<BR>}<BR>void OutChar(char data){
<BR> while(PutFifo(data)){};<BR> SC0CR2=0xAC;}<BR>void
main(void){ char data=0x41;
<BR> OutChar(data);}</CODE></P></DIR>
<ADDRESS>Listing 10-6: Call by value passes a copy of the data.</ADDRESS>
<P><FONT face="Times New Roman,Times">The most important point to remember about
passing arguments by value in C is that there is no connection between an actual
argument and its source. Changes to the arguments made within a function, have
no affect what so ever on the objects that might have supplied their values.
They can be changed with abandon and their sources will not be affected in any
way. This removes a burden of concern from the programmer since he may use
arguments as local variables without side effects. It also avoids the need to
define temporary variables just to prevent side effects.</FONT></P>
<P><FONT face="Times New Roman,Times">It is precisely because C uses call by
value that we can pass expressions, not just variables, as arguments. The value
of an expression can be copied, but it cannot be referenced since it has no
existence in global memory. Therefore, call by value adds important generality
to the language.</FONT></P>
<P><FONT face="Times New Roman,Times">Although the C language uses the call by
value technique, it is still possible to write functions that have side effects;
but it must be done deliberately. This is possible because of C's ability to
handle expressions that yield addresses. And, since any expression is a valid
argument, addresses can be passed to functions.</FONT></P>
<P><FONT face="Times New Roman,Times">Since expressions may include assignment,
increment, and decrement operators (<A
href="http://www.ece.utexas.edu/~valvano/embed/chap9/chap9.htm">Chapter 9</A>),
it is possible for argument expressions to affect the values of arguments lying
to their right. (Recall that C evaluates argument expressions from left to
right.) Consider, for example,</FONT></P>
<DIR>
<P><CODE>func (y=x+1, 2*y);</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">where the first argument has the value
<B>x+1</B> and the second argument has the value <B>2*(x+1)</B>. What would be
the value of the second argument if arguments were evaluated from right to left?
This kind of situation should be avoided, since the C language does not
guarantee the order of argument evaluation. The safe way to write this
is</FONT></P>
<DIR>
<P><CODE>y=x+1;<BR>func (y, 2*y);</CODE></P></DIR>
<P><FONT face="Times New Roman,Times">It is the programmer's responsibility to
ensure that the parameters passed match the formal arguments in the function's
definition. Some mistakes will be caught as syntax errors by the compiler, but
this mistake is a common and troublesome problem for all C
programmers.</FONT></P>
<P><FONT face="Times New Roman,Times">Occasionally, the need arises to write
functions that work with a variable number of arguments. An example is
<B>printf()</B> in the library. ICC11 and ICC12 implement this feature using
macros defined in the library file STDARG.C. To use these features you include
STDARG.H in your file. For examples see the STDIO.C source file in your LIBSRC
directory. </FONT></P>
<P> </P>
<P><B><I><FONT face=Helvetica,Arial><A name=PRIVATE></A>Private versus Public
Functions</FONT></I></B></P>
<P><FONT face="Times New Roman,Times">For every function definition, ICC11 and
ICC12 generates an assembler directive declaring the function's name to be
<I>public</I>. This means that every C function is a potential entry point and
so can be accessed externally. One way to create private/public functions is to
control which functions have declarations. Consider again the main program in
Listing 10-2 shown earlier. Now lets look inside the Timer.H and Timer.C files.
To implement Private and Public functions we place the function declarations of
the Public functions in the Timer.H file. </FONT></P>
<DIR>
<P><CODE>void TimerInit(void);<BR>void TimerMsWait(unsigned int
time);</CODE></P></DIR>
<ADDRESS>Listing 10-7: Timer.H header file has public functions</ADDRESS>
<P><FONT face="Times New Roman,Times">The implementations of all functions are
included in the Timer.C file. The function, <B>TimerWait</B>, is private and can
only be called by software inside the Timer.C file. We can apply this same
approach to private and public global variables. Notice that in this case the
global variable, <B>TimerClock</B>, is private and can not be accessed by
software outside the Timer.C file.</FONT> </P>
<DIR>
<P><CODE>unsigned int TimerClock; // private global<BR>void TimerInit(void){ //
public function<BR> TSCR |=0x80; //
TEN(enable)<BR> TMSK2=0xA2; // TOI arm, TPU(pullup)
timer/4 (500ns)<BR> TimerClock=2000; // 2000 counts per
ms<BR>}<BR>void TimerWait(unsigned int time){ // private
function<BR> TC5=TCNT+TimerClock; // 1.00ms
wait<BR> TFLG1 = 0x20;
// clear
C5F<BR> while((TFLG1&0x20)==0){};}<BR>void TimerMsWait(unsigned
int time){ // public
function<BR> for(;time>0;time--)<BR> TimerWait(TimerClock);
// 1.00ms wait<BR>}</CODE></P></DIR>
<ADDRESS>Listing 10-8: Timer.C implementation file defines all
functions</ADDRESS>
<P><FONT face="Times New Roman,Times">For more information about software
development see Chapter 2 of the book <U>Embedded Microcomputer Systems: Real
Time Interfacing</U> by Jonathan Valvano published by Brooks Cole.</FONT></P>
<ADDRESS> </ADDRESS>
<P><B><I><FONT face=Helvetica,Arial><A name=STACK></A>The Stack
Frame</FONT></I></B></P>
<P><FONT face="Times New Roman,Times">Figure 10-2 illustrates the structure of a
C stack frame. The stack frame generated by the ICC11 compiler places the
explicit local variables (and other temporary data) at the top of the stack.
Input parameters to the function and the subroutine return address are also on
the stack. Recall that the 6811 stack pointer points to an empty place just
above the top element of the stack. Within a 6811 function, <B>RegX</B> is not
saved, but rather whenever stack access is required, the SP is copied into
<B>RegX</B> using the <B>tsx</B> instruction and the stack is accessed using
X-index address. The 6811 stack picture in Figure 10-2 illustrates the condition
after executing <B>tsx</B>. The stack frame generated by the ICC12 compiler is
similar, but not identical. Just like the 6811, the 6812 stack includes local
variables, temporaries, subroutine return address and the input parameters. Just
like the 6811, the first input parameter is above the return address and the
remaining input parameters are below the return addressing. In actuality, this
first input parameter is passed into the function in <B>RegD</B>, and the
function itself pushes it on the stack. Different from the 6811, the 6812 stack
pointer points to the top element of the stack. Within the 6812 function,
<B>RegX</B> is saved, and then <B>RegX</B> is using within the function as a
stack frame pointer. Because the value of <B>RegX</B> is maintained throughout
the function and the 6812 stack is accessed simply using X-index addressing
without having to execute <B>tsx</B> or (<B>tfr s,x</B>) each time. In
particular notice <B>tsx</B> is executed twice in the 6811 main program in
Listing 10-10, but <B>tfr s,x</B> is executed only once in the 6812 main program
in Listing 10-11.</FONT></P>
<P><FONT face="Times New Roman,Times"><IMG height=211
src="Chapter 10 Functions -- Valvano.files/stacks.gif" width=350></FONT></P>
<ADDRESS>Figure 10-2: Stack frame for a function with one local variable and
three input parameters.</ADDRESS>
<P><FONT face="Times New Roman,Times">In order to understand both the machine
architecture and the C compiler, we can look at the assembly code generated.
This example shows a simple C program with three global variables
</FONT><B><FONT face=Courier>x1,x2,x3</FONT></B><FONT
face="Times New Roman,Times">, two local variables both called </FONT><B><FONT
face=Courier>y </FONT></B><FONT face="Times New Roman,Times">and three function
parameters </FONT><B><FONT face=Courier>z1,z2,z3</FONT></B><FONT
face="Times New Roman,Times">.</FONT></P>
<DIR>
<P><CODE>int x1;<BR>static int x2;<BR>const int x3=1000;<BR>int add3(int z1, int
z2, int z3){ int
y;<BR> y=z1+z2+z3;<BR> return(y);}<BR>void
main(void){ int
y;<BR> x1=1000;<BR> x2=1000;<BR> y=add3(x1,x2,x3);</CODE></P></DIR>
<ADDRESS>Listing 10-9: Example function call with local variables</ADDRESS>
<P>The first compiler we will study is the ImageCraft ICC11 version 4.0 for the
Motorola 6811. The disassembled output has been edited to clarify its operation.
The linker/loader allocates 3 segmented memory areas: code pointed to by the PC;
global accessed with absolute addressing; and locals pointed to by the stack
pointer SP. The global symbols, <B><FONT face=Courier>_x1 _x2_x3</FONT></B>,
will be assigned or bound by the linker/loader. The <B><FONT
face=Courier>pshx</FONT></B><FONT face=Courier> </FONT>instruction allocates the
local variable, and the <B><FONT face=Courier>tsx</FONT></B><FONT face=Courier>
</FONT>instruction establishes a stack frame pointer, X. This compiler passes
the first input parameter (<B><FONT face=Courier>z1</FONT></B>) into the
subroutine by placing it in register D. The remaining parameters (<B><FONT
face=Courier>z2</FONT></B>, <B><FONT face=Courier>z3</FONT></B> in this example)
are pushed on the stack by the main program before the subroutine is called. The
first operation the subroutine performs is to push the remaining parameter on
the stack (<B><FONT face=Courier>pshb psha</FONT></B>) so that all three
parameters,<B><FONT face=Courier> z1 z2 z3</FONT></B>, are on the stack. </P>
<DIR>
<P><CODE> .area text ;text area is
ROM<BR> .globl _x3<BR>_x3: .word
1000<BR> .area text<BR> .globl
_add3<BR>; y -> 0,x<BR>; z3
-> 8,x<BR>; z2 -> 6,x<BR>;
z1 -> 2,x<BR>_add3: pshb ;push z1 on
stack<BR> psha<BR> pshx ;allocate
y<BR> tsx ;create
local stack
frame<BR> ldd 2,x ;RegD=z1<BR> addd 6,x ;RegD=z1+z2<BR> addd 8,x ;RegD=z1+z2+z3<BR> std 0,x ;y=z1+z2+z3<BR> pulx ;deallocate
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -