<< Previous | Index | Next >>

> ## 4. Language

> Dynamic C is based on the C language. The programmer is expected to know programming methodologies and the basic principles of the C language. Dynamic C has its own set of libraries, which include user-callable functions. Please see the Dynamic C Function Reference Manual for detailed descriptions of these API functions. Dynamic C libraries are in source code, allowing the creation of customized libraries.

> Before starting on your application, read through the rest of this chapter to review C-language features and understand the differences between standard C and Dynamic C.

> ### 4.1 C Language Elements

> A Dynamic C program is a set of files consisting of one file with a main() function and the requested library files. Each file is a stream of characters that compose statements in the C language. The language has grammar and syntax, that is, rules for making statements. Syntactic elements, often called tokens, form the basic elements of the C language. Some of these elements are listed in Table 4-1.

> Table 4-1. Language Elements
> Syntactic Element | Description
> punctuation | Symbols used to mark beginnings and endings
> names | Words used to name data and functions
> numbers | Literal numeric values
> strings | Literal character values enclosed in quotes
> directives | Words that start with `#` and control compilation
> keywords | Words used as instructions to Dynamic C
> operators | Symbols used to perform arithmetic operations

> ### 4.2 Punctuation Tokens

> Punctuation serves as boundaries in C programs. Table 4-2 lists the punctuation tokens.

> Table 4-2. Punctuation Marks and Tokens
> Token | Description
> : | Terminates a statement label.
> ; | Terminates a simple statement or a `do` loop.
> , | Separates items in a list, such as an argument list, declaration list, initialization list, or expression list.
> ( ) | Encloses argument or parameter lists. Function calls always require parentheses. Macros with parameters also require parentheses. Also used for arithmetic and logical sub expressions.
> { } | Begins and ends a compound statement, a function body, a structure or union body, or encloses a function chain segment.
> // | Indicates that the rest of the line is a comment and is not compiled.
> /* ... */ | Comments are nested between the `/*` and `*/` tokens.

> ### 4.3 Data

> Data (variables and constants) have type, size, structure, and storage class. Basic (a.k.a., primitive) data types are shown below.

> Table 4-3. Dynamic C Basic Data Types
> Data Type | Description
> `char` | 8-bit unsigned integer. Range: 0 to 255 (0xFF)
> `int` | 16-bit signed integer. Range: -32,768 to +32,767
> `unsigned int` | 16-bit unsigned integer. Range: 0 to +65,535
> `long` | 32-bit signed integer. Range: -2,147,483,648 to +2,147,483,647
> `unsigned long` | 32-bit unsigned integer. Range 0 to 232 - 1
> `float` | 32-bit IEEE floating-point value. The sign bit is 1 for negative values. The exponent has 8 bits, giving exponents from -127 to +128. The mantissa has 24 bits. Only the 23 least significant bits are stored; the high bit is 1 implicitly. (Rabbit controllers do not have floating-point hardware.) Range: 1.18 x 10-38 to 3.40 x 1038
> `enum` | Defines a list of named integer constants. The integer constants are signed and in the range: -32,768 to +32,767.

> #### 4.3.1 Data Type Limits

> The following symbolic names for the hardcoded limits of the data types are defined in `limits.h`.

>     #define CHAR_BIT             8
>     #define UCHAR_MAX          255
>     #define CHAR_MIN             0
>     #define CHAR_MAX           255
>     #define MB_LEN_MAX           1

>     #define SHRT_MIN        -32768
>     #define SHRT_MAX         32767
>     #define USHRT_MAX        65535

>     #define INT_MIN         -32767
>     #define INT_MAX          32767
>     #define UINT_MAX         65535
>     #define LONG_MIN   -2147483647
>     #define LONG_MAX    2147483647
>     #define ULONG_MAX   4294967295

> ### 4.4 Names

> Names identify variables, certain constants, arrays, structures, unions, functions, and abstract data types. Names must begin with a letter or an underscore (`_`), and thereafter must be letters, digits, or an underscore. Names may not contain any other symbols, especially operators. Names are distinct up to 32 characters, but may be longer. Names may not be the same as any keyword. Names are case-sensitive.

> ##### Examples

>     my_function              // ok
>     _block                   // ok
>     test32                   // ok

>     jumper-                  // not ok, uses a minus sign
>     3270type                 // not ok, begins with digit

>     Cleanup_the_data_now     // These names are distinct
>     Cleanup_the_data_later

> References to structure and union elements require compound names. The simple names in a compound name are joined with the dot operator (period).

>     cursor.loc.x = 10;   // set structure element to 10

> Use the `#define` directive to create names for constants. These can be viewed as symbolic constants. See Section 4.5, "Macros."

>     #define READ   10
>     #define WRITE  20
>     #define ABS     0
>     #define REL     1
>     #define READ_ABS   READ + ABS
>     #define READ_REL   READ + REL

> The term `READ_ABS` is the same as 10 + 0 or 10, and `READ_REL` is the same as 10 + 1 or 11. Note that Dynamic C does not allow anything to be assigned to a constant expression.

>     READ_ABS = 27;     // produces a compiler error

> To accomplish the above statement, do the following:

>     #undef READ_ABS
>     #define READ_ABS 27

> ### 4.5 Macros

> Macros may be defined in Dynamic C by using `#define`. A macro is a name replacement feature. Dynamic C has a text preprocessor that expands macros before the program text is compiled. The programmer assigns a name, up to 31 characters, to a fragment of text. Dynamic C then replaces the macro name with the text fragment wherever the name appears in the program. In this example,

>     #define OFFSET 12
>     #define SCALE  72
>     int i, x;
>     i = x * SCALE + OFFSET;

> the variable `i` gets the value `x * 72 + 12`. Macros can have parameters such as in the following code.

>     #define word( a, b ) (a<<8 | b)
>     char c;
>     int i, j;
>     i = word( j, c );      // same as i = (j << 8 | c)

> The compiler removes the surrounding white space (comments, tabs and spaces) and collapses each sequence of white space in the macro definition into one space. It places a `\` before any `"` or `\` to preserve their original meaning within the definition.

> #### 4.5.1 Macro Operators # and ##

> Dynamic C implements the `#` and `##` macro operators.

> The `#` operator forces the compiler to interpret the parameter immediately following it as a string literal. For example, if a macro is defined

>     #define report(value,fmt)\
>     printf( #value "=" #fmt "\n", value )

> then the macro in

>     report( string, %s );

> will expand to

>     printf( "string" "=" "%s" "\n", string );

> and because C always concatenates adjacent strings, the final result of expansion will be

>     printf( "string=%s\n", string );

> The `##` operator concatenates the preceding character sequence with the following character sequence, deleting any white space in between. For example, given the macro

>     #define set(x,y,z) x ## z ## _ ## y()

> the macro in

>     set( AASC, FN, 6 );

> will expand to

>     AASC6_FN();

> For parameters immediately adjacent to the `##` operator, the corresponding argument is not expanded before substitution, but appears as it does in the macro call.

> #### 4.5.2 Nested Macro Definitions

> Generally speaking, Dynamic C expands macro calls recursively until they can expand no more. Another way of stating this is that macro definitions can be nested.

> The exceptions to this rule are

> 1. Arguments to the `#` and `##` operators are not expanded.

> 2. To prevent infinite recursion, a macro does not expand within its own expansion.

> The following complex example illustrates this.

>     #define A B
>     #define B C
>     #define uint unsigned int
>     #define M(x) M ## x
>     #define MM(x,y,z) x = y ## z
>     #define string something
>     #define write( value, fmt )\
>     printf( #value "=" #fmt "\n", value )

> The code

>     uint z;
>     M (M) (A,A,B);
>     write(string, %s);

> will expand first to

>     unsigned int z;                           // simple expansion
>     MM (A,A,B);                               // M(M) doesn't expand recursively
>     printf( "string" "=" "%s" "\n", string ); // #value  "string" #fmt   "%s"

> then to

>     unsigned int z;
>     A = AB;                                      // from A = A ## B
>     printf( "string" "=" "%s" "\n", something );
>                                                  // string  something

> then to

>     unsigned int z;
>     B = AB;                                 // A  B
>     printf( "string=%s\n", something );     // concatenation

> and finally to

>     unsigned int z;
>     C = AB;                                 // B  C
>     printf("string = %s\n", something);

> #### 4.5.3 Macro Restrictions

> The number of arguments in a macro call must match the number of parameters in the macro definition. An empty parameter list is allowed, but the macro call must have an empty argument list. Macros are restricted to 32 parameters and 126 nested calls. A macro or parameter name must conform to the same requirements as any other C name. The C language does not perform macro replacement inside string literals, character constants, comments, or within a `#define` directive.

> A macro definition remains in effect unless removed by an `#undef` directive. If an attempt is made to redefine a macro without using `#undef`, a warning will appear and the original definition will remain in effect.

> ### 4.6 Numbers

> Numbers are constant values and are formed from digits, possibly a decimal point, and possibly the letters `U, L, X`, or `A-F`, or their lower case equivalents. A decimal point or the presence of the letter `E` or `F` indicates that a number is real (has a floating-point representation).

> Integers have several forms of representation. The normal decimal form is the most common.

>     10   -327   1000   0

> An integer is long (32-bit) if its magnitude exceeds the 16-bit range (-32768 to +32767) or if it has the letter `L` appended.

>     0L   -32L   45000   32767L

> An integer is unsigned if it has the letter `U` appended. It is `long` if it also has `L` appended or if its magnitude exceeds the 16-bit range.

>     0U   4294967294U   32767U   1700UL

> An integer is hexadecimal if preceded by `0x`.

>     0x7E   0xE000   0xFFFFFFFA

> It may contain digits and the letters `a-f` or `A-F`.

> An integer is octal if begins with zero and contains only the digits `0-7`.

>     0177   020000   000000630

> A real number can be expressed in a variety of ways.

>     4.5 means 4.5

>     4f means 4.0

>     0.3125 means 0.3125

>     456e-31 means 456 ×  10-31

>     0.3141592e1 means 3.141592

> ### 4.7 Strings and Character Data

> A string is a group of characters enclosed in double quotes ("").

>        "Press any key when ready..."

> Strings in C have a terminating null byte appended by the compiler. Although C does not have a string data type, it does have character arrays that serve the purpose. C does not have string operators, such as concatenate, but library functions `strcat()` and `strncat()` are available.

> Strings are multibyte objects, and as such they are always referenced by their starting address, and usually by a `char*` variable. More precisely, arrays are always passed by address. Passing a pointer to a string is the same as passing the string. Refer to Section 4.15 for more information on pointers.

> The following code illustrates a typical use of strings.

>     const char * const select = "Select option\n";
>     char start[32];
>     strcpy(start,"Press any key when ready...\n");
>     printf( select );             // pass pointer to string
>     ...
>     printf( start );              // pass string

> Note that both the pointer and the elements of the array are explicitly defined as const. Some versions of Dynamic C allowed the second const to be omitted. Current versions of the compiler generate an error unless the second const is included.

> #### 4.7.1 String Concatenation

> Two or more string literals are concatenated when placed next to each other. For example:

>     "Rabbits" "like carrots."

> becomes, during compilation:

>     "Rabbits like carrots."

> If the strings are on multiple lines, the macro continuation character must be used. For example:

>     "Rabbits"\
>     "don't like line dancing."

> becomes, during compilation:

>     "Rabbits don't like line dancing."

> #### 4.7.2 Character Constants

> Character constants have a slightly different meaning. They are not strings. A character constant is enclosed in single quotes (' ') and is a representation of an 8-bit integer value.

>     'a'    '\n'    '\x1B'

> Any character can be represented by an alternate form, whether in a character constant or in a string. Thus, nonprinting characters and characters that cannot be typed may be used.

> A character can be written using its numeric value preceded by a backslash.

>     \x41                  // the hex value 41
>     \101                  // the octal value 101, a leading zero is optional
>     \B10000001            // the binary value 10000001

> There are also several "special" forms preceded by a backslash.

>     \a  bell
>     \f  formfeed
>     \r  carriage return
>     \v  vertical tab
>     \\  backslash
>     \'  single quote
>     \b  backspace
>     \n  newline
>     \t  tab
>     \0  null character
>     \c  the actual character c
>     \"  double quote

> ##### Examples

>     "He said \"Hello.\""   // embedded double quotes
>     const char j = 'Z';    // character constant
>     const char* MSG = "Put your disk in the A drive.\n";
>                            // embedded new line at end
>     printf( MSG );         // print MSG
>     char* default = "";    // empty string: a single null byte

> ### 4.8 Statements

> Except for comments, everything in a C program is a statement. Almost all statements end with a semicolon. A C program is treated as a stream of characters where line boundaries are (generally) not meaningful. Any C statement may be written on as many lines as needed. The Dynamic C text editor enforces a 512 byte limit on the length of a line. Similarly, the Dynamic C compiler is only guaranteed to parse up to 512 bytes for any single C statement.

> A statement can be many things. A declaration of variables is a statement. An assignment is a statement. A `while` or `for` loop is a statement. A compound statement is a group of statements enclosed in braces `{` and `}`. A group of statements may be single statements and/or compound statements.

> Comments (the `/*...*/` kind) may occur almost anywhere, even in the middle of a statement, as long as they begin with `/*` and end with `*/`.

> ### 4.9 Declarations

> A variable must be declared before it can be used. That means the variable must have a name and a type, and perhaps its storage class could be specified. If an array is declared, its size must be given. Root data arrays are limited to a total of 32,767 elements.

>     static int thing, array[12];     // static integer variable &
>                                      //  static integer array

>     auto float matrix[3][3];         // auto float array with 2 dimensions

>     char *message="Press any key..." // initialized pointer to char array

> If an aggregate type (`struct` or `union`) is being declared, its internal structure has to be described as shown below.

>     struct {                        // description of structure
>        char flags;
>        struct {                     // a nested structure here
>           int x;
>           int y;
>        } loc;
>     } cursor;
>     ...
>     int a;
>     a = cursor.loc.x;              // use of structure element here

> ### 4.10 Functions

> The basic unit of a C application program is a function. Most functions accept parameters (a.k.a., arguments) and return results, but there are exceptions. All C functions have a return type that specifies what kind of result, if any, it returns. A function with a `void` return type returns no result. If a function is declared without specifying a return type, the compiler assumes that it is to return an `int` (integer) value.

> A function may call another function, including itself (a recursive call). The `main` function is called automatically after the program compiles or when the controller powers up. The beginning of the `main` function is the entry point to the entire program.

> ### 4.11 Prototypes

> A function may be declared with a prototype. This is so that:

> - Functions that have not been compiled may be called.

> - Recursive functions may be written.

> - The compiler may perform type-checking on the parameters to make sure that calls to the function receive arguments of the expected type.

> A function prototype describes how to call the function and is nearly identical to the function's initial code.

>     /* This is a function prototype.*/
>     long tick_count ( char clock_id );

>     /* This is the function's definition.*/
>     long tick_count ( char clock_id ){
>        ...
>     }

> It is not necessary to provide parameter names in a prototype, but the parameter type is required, and all parameters must be included. (If the function accepts a variable number of arguments, as `printf` does , use an ellipsis.)

>     /* This prototype is as good as the one above. */
>     long tick_count ( char );

>     /* This is a prototype that uses ellipsis. */
>     int startup ( device id, ... );

> ### 4.12 Type Definitions

> Both types and variables may be defined. One virtue of high-level languages such as C and Pascal is that abstract data types can be defined. Once defined, the data types can be used as easily as simple data types like `int`,`char`, and `float`. Consider this example.

>     typedef int MILES;    // a basic type named MILES

>     typedef struct {      // a structure type...
>        float re;          // ...
>        float im;          // ...
>     } COMPLEX;            // ...named COMPLEX

>     MILES distance;       // declare variable of type MILES
>     COMPLEX z, *zp;       // declare variable of & pointer to type COMPLEX .

> Use `typedef` to create a meaningful name for a class of data. Consider this example.

>     typedef unsigned int node;
>     void NodeInit( node );              // type name is informative
>     void NodeInit( unsigned int );      // not very informative

> This example shows many of the basic C constructs.

>     /*Put descriptive information in your program code using this form of comment,
>      which can be inserted anywhere and can span lines. The double slash comment
>     (shown below) may be placed at the end of a line.*/

>     #define SIZE 12                   // A symbolic constant defined.
>     int g, h;                         // Declare global integers.
>     float sumSquare( int, int );      // Prototypes for
>     void init();                      //   functions below.

>     main(){                           // Program starts here.
>        float x;                       // x is local to main.
>        init();                        // Call a void function.
>        x = sumSquare( g, h );         // x gets sumSquare value.
>        printf("x = %f",x);            // printf is a standard function.
>     }

>     void init(){                      // Void functions do things but
>        g = 10;                        //   they return no value.
>        h = SIZE;                      // Here, it uses the symbolic
>     }                                 //   constant defined above.

>     float sumSquare( int a, int b ){  // Integer arguments.
>        float temp;                    // Local variables.
>        temp = a*a + b*b;              // Arithmetic statement.
>        return( temp );                // Return value.
>     }

>     /* and here is the end of the program */

> The program above calculates the sum of squares of two numbers, `g` and `h`, which are initialized to 10 and 12, respectively. The main function calls the `init` function to give values to the global variables `g` and `h`. Then it uses the `sumSquare` function to perform the calculation and assign the result of the calculation to the variable `x`. It prints the result using the library function `printf`, which includes a formatting string as the first argument.

> Notice that all functions have `{` and `}` enclosing their contents, and all variables are declared before use. The functions `init()` and `sumSquare()` were defined before use, but there are alternatives to this.This was explained in Section 4.11.

> ### 4.13 Aggregate Data Types

> Simple data types can be grouped into more complex aggregate forms.

> #### 4.13.1 Array

> A data type, whether it is simple or complex, can be replicated in an array. The declaration

>     int item[10];        // An array of 10 integers.

> represents a contiguous group of 10 integers. Array elements are referenced by their subscript.

>     j = item[n];        // The nth element of the array.

> Array subscripts count up from 0. Thus, `item[7]` above is the eighth item in the array. Notice the `[` and `]` enclosing both array dimensions and array subscripts. Arrays can be "nested." The following doubly dimensioned array, or "array of arrays."

>     int matrix[7][3];

> is referenced in a similar way.

>     scale = matrix[i][j];

> The first dimension of an array does not have to be specified as long as an initialization list is specified.

>     int x[][2] = { {1, 2}, {3, 4}, {5, 6} };
>     char string[] = "abcdefg";

> #### 4.13.2 Structure

> Variables may be grouped together in structures (`struct` in C) or in arrays. Structures may be nested.

>     struct {
>        char flags;
>        struct {
>           int x;
>           int y;
>        } loc;
>     } cursor;

> Structure members--the variables within a structure--are referenced using the dot operator.

>     j = cursor.loc.x

> The size of a structure is the sum of the sizes of its components.

> #### 4.13.3 Union

> A union overlays simple or complex data. That is, all the union members have the same address. The size of the union is the size of the largest member.

>     union {
>        int ival;
>        long jval;
>        float xval;
>     } u;

> Unions can be nested. Union members--the variables within a union--are referenced, like structure elements, using the dot operator.

>     j = u.ival

> #### 4.13.4 Composites

> Composites of structures, arrays, unions, and primitive data may be formed. This example shows an array of structures that have arrays as structure elements.

>     typedef struct {
>        int *x;
>        int c[32];       // array in structure
>     } node;

>     node list[12];      // array of structures

> Refer to an element of array `c` (above) as shown here.

>     z = list[n].c[m];
>     ...
>     list[0].c[22] = 0xFF37;

> ### 4.14 Storage Classes

> Variable storage can be `auto` or `static`. The term "static" means the data occupies a permanent fixed location for the life of the program. The term "auto" refers to variables that are placed on the system stack for the life of a function call.The default storage class is `auto`, but can be changed by using `#class static`. The default storage class can be superseded by the use of the keyword `auto` or `static` in a variable declaration.

> These terms apply to local variables, that is, variables defined within a function. If a variable does not belong to a function, it is called a global variable--available anywhere in the program--but there is no keyword in C to represent this fact. Global variables always have static storage.

> The `register` type is reserved, but is not currently implemented. Dynamic C will change a variable to be of type `auto` if `register` is encountered. Even though the `register` keyword is not implemented, it still can not be used as a variable name or other symbol name. Its use will cause unhelpful error messages from the compiler.

> ### 4.15 Pointers

> A pointer is a variable that holds the 16-bit logical address of another variable, a structure, or a function. Far pointers are supported. The indirection operator (*) is used to declare a variable as a pointer. The address operator (&) is used to set the pointer to the address of a variable.

>     int *ptr_to_i;
>     int i;
>     ptr_to_i = &i;       // set pointer equal to the address of i
>     i = 10:              // assign a value to i
>     j = *ptr_to_i;       // this sets j equal to the value in i

> In this example, the variable `ptr_to_i` is a pointer to an integer. The statement "j = *ptr_to_i;" references the value of the integer by the use of the asterisk. Using correct pointer terminology, the statement dereferences the pointer `ptr_to_i`. Then `*ptr_to_i` and `i` have identical values.

> Note that `ptr_to_i` and `i` do not have the same values because `ptr_to_i` is a pointer and `i` is an `int`. Note also that `*` has two meanings (not counting its use as a multiplier in others contexts) in a variable declaration such as `int *ptr_to_i;` the `*` means that the variable will be a pointer type, and in an executable statement `j = *ptr_to_i;` means "the value stored at the address contained in `ptr_to_i`."

> Pointers may point to other pointers.

>     int *ptr_to_i;
>     int **ptr_to_ptr_to_i;

>     int i,j;

>     ptr_to_i = &i;                // Set pointer equal to the address of i
>     ptr_to_ptr_to_i = &ptr_to_i;  // Set a pointer to the pointer
>                                   // to the address of i
>     i = 10;                       // Assign a value to i
>     j = **ptr_to_ptr_to_i;        // This sets j equal to the value in i.

> It is possible to do pointer arithmetic, but this is slightly different from ordinary integer arithmetic. Here are some examples.

>     float f[10], *p, *q;         // an array and some ptrs
>     p = &f;                      // point p to array element 0
>     q = p+5;                     // point q to array element 5
>     q++;                         // point q to array element 6
>     p = p + q;                   // illegal!

> Because the `float` is a 4-byte storage element, the statement `q = p+5` sets the actual value of `q` to `p+20.` The statement `q++` adds 4 to the actual value of `q`. If `f` were an array of 1-byte characters, the statement `q++` adds 1 to `q`.

> Beware of using uninitialized pointers. Uninitialized pointers can reference ANY location in memory. Storing data using an uninitialized pointer can overwrite code or cause a crash.

> A common mistake is to declare and use a pointer to `char`, thinking there is a string. But an uninitialized pointer is all there is.

>     char* string;
>     ...
>     strcpy( string, "hello" );     // Invalid!
>     printf( string );              // Invalid!

> Pointer checking is a run-time option in Dynamic C. Use the Compiler tab on the Options | Project Options menu. Pointer checking will catch attempts to dereference a pointer to unallocated memory. However, if an uninitialized pointer happens to contain the address of a memory location that the compiler has already allocated, pointer checking will not catch this logic error. Because pointer checking is a run-time option, pointer checking adds instructions to code when pointer checking is used.

> Pointer checking is not currently supported for far pointers.

> ### 4.16 Far Pointers and Far Data

> This section examines the syntax of the `far` keyword, using examples from simple variables to complex aggregate types.

> #### 4.16.1 The far Qualifier

> The `far` keyword allows a programmer to directly declare variables in xmem. Previous to this development, usage of xmem was limited to library routines such as `root2xmem()` and `xmem2root()` using memory allocated using `xalloc`. Now, the compiler will directly generate code to access xmem allocated through standard variable declarations with the addition of the `far` keyword.

> #### 4.16.2 Basic Declarations

> In almost all respects, `far` behaves syntactically identically to the `const` qualifier.

> The keyword `far` was added to use the same basic principles as `const`, with a few exceptions. The reason for this is that `far` and `const` both indicate the storage type for variables. In the case of `const`, the storage is in the flash device. Variables declared as `far` are stored in xmem in RAM (and can therefore be modified). A variable can also be declared as `const far`, which places the constant variable in the xmem space on the flash device.

>     far type var;             // Declares a variable "var" having far storage

> We also allow

>     type far var;

> which has the same meaning as the previous declaration. In other words, the `far` keyword may appear before or after the base type.

> We do not allow

>     far type far var;

> In this context, these are base type qualifiers. The `far` keyword can also qualify pointer types, such as in the following example:

>     type * far ptr;

> This declares a variable, `ptr`, having `far` storage pointing to an object of type type. Pointer qualifiers are always found on the right-hand side of the ``*`' token.

> Here is a slightly more complex declaration:

>     far type * far ptr;

> Here, the object type to which `ptr` points is qualified as having `far` storage.

> #### 4.16.3 Multi-Level Far Pointers

> The semantics of the `far` qualifier can become quite complex if used with multi-level pointers. Some confusion arises when thinking about how to qualify different pointer levels in a more complex declaration such as the following basic pointer-to-pointer declaration:

>     type * * ptr;

> This declares `ptr` as a variable which points to an object of type pointer to type, or simply, `ptr` is a pointer to pointer-to-type. What if we wanted to declare `ptr` to be a pointer to a pointer having `far` storage (the pointer to type is in xmem, but what it points to is in root)? This would have the following declaration:

>     type * far * ptr;

> Here we see that pointer declarations are right-associative. Recalling that the `far` qualifier associates with the ``*`' token to its left, we see that the nested pointer type is the left ``*`' not the right one, illustrated using brackets:

>     [type * far] * ptr;

> In the above example, the association of the ``*`' and `far` is evident - the variable `ptr` is a pointer-in-root, and it points to a pointer-in-far.

> For another example, a complex and infrequent declaration might be:

>     far type * far * * far ptr;

> A succinct way of stating the type of `ptr` in this example would be: `ptr` is a pointer-in-far to a pointer-in-root to a pointer-in-far to a variable of type having `far` storage.

> #### 4.16.4 Arrays and Structures

> The `far` qualifier can also be applied to arrays and structures, with the effect of the compiler allocating storage for those variables from xmem. The declarations for both structures and arrays (and pointers to those types) follow the same rules as basic type variable declarations. An example structure declaration might be:

>     struct S {
>        int a;
>        char b[20];
>     };

>     far struct S str;            // A structure of type S in xmem

> Note that the `far` qualifier is applied only to the actual declaration of a variable with the structure type, not the structure definition itself. The `far` qualifier may not be applied to either a structure type definition or any member of a structure. If a structure instance variable is placed in xmem using the `far` keyword, then all members of that instance are in xmem - you cannot mix xmem and root within a single structure.

> Arrays can also be placed in xmem using `far`. The following is a possible declaration of an array in xmem:

>     far type array[5000];      // An array of 5000 elements of type type in xmem

> Note that the size limit imposed on arrays in root memory (32,767 bytes) also apply to far arrays. You can declare an array as large as your largest contiguous free block of xmem available up to this limit. See Chapter 10 "Memory Management" for more information on how xmem is allocated and used by the compiler.

> #### 4.16.5 Complex Declarations

> All of the elements discussed so far can be applied in a single declaration to produce very complex types for variables. As an example, such a declaration may look like the following:

>     const type (* far const ptr)[c0][c1] = &const_array;

> In this example, `ptr` is a constant pointer-in-far (xmem constant) to a 2-dimensional array of `c0` x `c1` elements of type constant type. In other words, we have a pointer in xmem to a two-diminsional array of constant elements. The array the pointer is pointing to is in root memory, since the `far` qualifier only associates with the pointer variable itself. The pointer is constant, so it must be initialized, and the first `const` implies that we cannot change the elements in the array since they represent constants (which are in flash and cannot be modified). We assume that `c0`, `c1`, and `const_array` are all constant variables or literals defined previously.

> #### 4.16.6 Sample Programs

> From the Dynamic C installation directory, look in `/Samples/Rabbit4000/FAR/` for sample programs demonstrating the use of the `far` keyword. The sample `far_demo.c` shows how to declare a local variable that will be stored in far memory (which means it must be declared `static`) and accessed just like any other local variable. The sample `LinkedList.c` demonstrates far pointers and includes a library, `LinkedList.LIB`, that creates and maintains a linked list in the far memory space.

> ### 4.17 Pointers to Functions, Indirect Calls

> Pointers to functions may be declared. When a function is called using a pointer to it, instead of directly, we call this an indirect call.

> The syntax for declaring a pointer to a function is different than for ordinary pointers, and Dynamic C syntax for this is slightly different than the standard C syntax. Standard syntax for a pointer to a function is:

>     returntype (*name)( [argument list] );

> for example:

>     int (*func1)(int a, int b);
>     void (*func2)(char*);

> Dynamic C doesn't recognize the argument list in function pointer declarations. The correct Dynamic C syntax for the above examples would be:

>     int (*func1)();
>     void (*func2)();

> You can pass arguments to functions that are called indirectly by pointers, but the compiler will not check them for correctness. This means that the auto promotions provided by Dynamic C type checking will not occur, so values must be cast to the type that is expected or the size may not be correct. For example, if a function takes a long as a parameter, and you pass it a 16-bit integer value, it must be cast to type long in order for 4 bytes to be put onto the stack.

> The following program shows some examples of using function pointers.

>     typedef int (*fnptr)();  // create pointer to function that returns an integer

>     main(){
>        int x,y;
>        int (*fnc1)();        // declare var fnc1 as a pointer to an int function.
>        fnptr  fp2;           // declare var fp2 as pointer to an int function
>        fnc1 = intfunc;       // initialize fnc1 to point to intfunc()
>        fp2 = intfunc;        // initialize fp2 to point to the same function.

>        x = (*fnc1)(1,2);     // call intfunc() via fnc1
>        y = (*fp2)(3,4);      // call intfunc() via fp2

>        printf("%d\n", x);
>        printf("%d\n", y);
>     }

>     int intfunc(int x, int y){
>        return x+y;
>     }

> ### 4.18 Argument Passing

> In C, function arguments are generally passed by value. That is, arguments passed to a C function are generally copies on the program stack of the variables or expressions specified by the caller. Changes made to these copies do not affect the original values in the calling program.

> In Dynamic C and most other C compilers, however, arrays are always passed by address. This policy includes strings (which are character arrays).

> Dynamic C passes `structs` by value on the stack. Passing a large `struct` takes a long time and can easily cause a program to run out of memory. Pass pointers to large `structs` if such problems occur.

> For a function to modify the original value of a parameter, pass the address of, or a pointer to, the parameter and then design the function to accept the address of the item.

> ### 4.19 Program Flow

> Three terms describe the flow of execution of a C program: sequencing, branching and looping. Sequencing is simply the execution of one statement after another. Looping is the repetition of a group of statements. Branching is the choice of groups of statements. Program flow is altered by calling a function, that is transferring control to the function. Control is passed back to the calling function when the called function returns.

> #### 4.19.1 Loops

> A `while` loop tests a condition at the start of the loop. As long as expression is true (non-zero), the loop body (some statement(s)) will execute. If expression is initially false (zero), the loop body will not execute. The curly braces are necessary if there is more than one statement in the loop body.

>     while( expression ){
>        some statement(s)
>     }

> A `do` loop tests a condition at the end of the loop. As long as expression is true (non-zero) the loop body (some statement(s)) will execute. A `do` loop executes at least once before its test. Unlike other controls, the `do` loop requires a semicolon at the end.

>     do{
>        some statements
>     }while( expression );

> The `for` loop is more complex: it sets an initial condition (exp1), evaluates a terminating condition (exp2), and provides a stepping expression (exp3) that is evaluated at the end of each iteration. Each of the three expressions is optional.

>     for( exp1 ; exp2 ; exp3 ){
>        some statement(s)
>     }

> If the end condition is initially false, a `for` loop body will not execute at all. A typical use of the `for` loop is to count `n` times.

>     sum = 0;
>     for( i = 0; i < n; i++ ){
>        sum = sum + array[i];
>     }

> This loop initially sets `i` to 0, continues as long as `i` is less than `n` (stops when `i` equals `n`), and increments `i` at each pass.

> Another use for the `for` loop is the infinite loop, which is useful in control systems.

>     for(;;){ some statement(s) }

> Here, there is no initial condition, no end condition, and no stepping expression. The loop body (some statement(s)) continues to execute endlessly. An endless loop can also be achieved with a `while` loop. This method is slightly less efficient than the `for` loop.

>     while(1)  { some statement(s) }

> #### 4.19.2 Continue and Break

> Two keywords are available to help in the construction of loops: `continue` and `break`.

> The `continue` statement causes the program control to skip unconditionally to the next pass of the loop. In the example below, if `bad` is true, more statements will not execute; control will pass back to the top of the `while` loop.

>     get_char();

>     while( ! EOF ){
>        some statements
>        if( bad ) continue;
>        more statements
>     }

> The `break` statement causes the program control to jump unconditionally out of a loop. In the example below, if `cond_RED` is true, more statements will not be executed and control will pass to the next statement after the ending curly brace of the `for` loop

>     for( i=0;i<n;i++ ){
>        some statements
>        if( cond_RED ) break;
>        more statements

>     }

> The `break` keyword also applies to the `switch/case` statement described in the next section. The `break` statement jumps out of the innermost control structure (loop or switch statement) only.

> There will be times when `break` is insufficient. The program will need to either jump out more than one level of nesting or there will be a choice of destinations when jumping out. Use a `goto` statement in such cases. For example,

>     while( some statements ){
>        for( i=0;i<n;i++ ){
>           some statements
>           if( cond_RED ) goto yyy;
>           some statements
>           if( code_BLUE ) goto zzz;
>           more statements
>        }
>     }
>     yyy:
>        handle cond_RED
>     zzz:
>        handle code_BLUE

> #### 4.19.3 Branching

> The `goto` statement is the simplest form of a branching statement. Coupled with a statement label, it simply transfers program control to the labeled statement.

>        some statements
>     abc:
>        other statements
>        goto abc;
>        ...
>        more statements
>        goto def;
>        ...
>     def:
>        more statements

> The colon at the end of the labels is required. In general, the use of the `goto` statement is discouraged in structured programming.

> The next simplest form of branching is the `if` statement. The simple form of the `if` statement tests a condition and executes a statement or compound statement if the condition expression is true (non-zero). The program will ignore the `if` body when the condition is false (zero).

>     if( expression ){
>        some statement(s)
>     }

> A more complex form of the `if` statement tests the condition and executes certain statements if the expression is true, and executes another group of statements when the expression is false.

>     if( expression ){
>        some statement(s)      // if true
>     }else{
>        some statement(s)      // if false
>     }

> The fullest form of the `if` statements produces a succession of tests.

>     if( expr1 ){
>        some statements
>     }else if( expr2 ){
>        some statements
>     }else if( expr3 ){
>        some statements
>        ...
>     }else{
>        some statements
>     }

> The program evaluates the first expression (expr1). If that proves false, it tries the second expression (expr2), and continues testing until it finds a true expression, an `else` clause, or the end of the if statement. An `else` clause is optional. Without an `else` clause, an `if/else if` statement that finds no true condition will execute none of the controlled statements.

> The `switch` statement, the most complex branching statement, allows the programmer to phrase a "multiple choice" branch differently.

>     switch( expression ){

>        case const1 :
>           statements1
>           break;

>        case const2 :
>           statements2
>           break;

>        case const3 :
>           statements3
>           break;
>        ...
>        default:
>           statementsDEFAULT
>     }

> First the `switch` expression is evaluated. It must have an integer value. If one of the `const`N values matches the `switch` expression, the sequence of statements identified by the `const`N expression is executed. If there is no match, the sequence of statements identified by the `default` label is executed. (The `default` part is optional.) Unless the `break` keyword is included at the end of the case's statements, the program will "fall through" and execute the statements for any number of other cases. The `break` keyword causes the program to exit the`switch/case` statement.

> The colons (`:`) after `case` and `default` are required.

> ### 4.20 Function Chaining

> Function chaining allows special segments of code to be distributed in one or more functions. When a named function chain executes, all the segments belonging to that chain execute. Function chains allow the software to perform initialization, data recovery, and other kinds of tasks on request. There are two directives, `#makechain` and `#funcchain`, and one keyword, `segchain` that create and control function chains:

> #### #makechain chain_name

> Creates a function chain. When a program executes the named function chain, all of the functions or chain segments belonging to that chain execute. (No particular order of execution can be guaranteed.)

> #### #funcchain chain_name name

> Adds a function, or another function chain, to a function chain.

> #### segchain chain_name { statements }

> Defines a program segment (enclosed in curly braces) and attaches it to the named function chain.

> Function chain segments defined with `segchain` must appear in a function directly after data declarations and before executable statements, as shown below.

>     my_function(){
>        /* data declarations */
>        segchain chain_x{
>            /* some statements which execute under chain_x  */
>        }
>        segchain chain_y{
>           /* some statements which execute under chain_y */
>        }
>        /* function body which executes when my_function is called  */
>     }

> A program will call a function chain as it would an ordinary void function that has no parameters. The following example shows how to call a function chain that is named `recover`.

>     #makechain recover
>     ...
>     recover();

> ### 4.21 Global Initialization

> Various hardware devices in a system need to be initialized, not only by setting variables and control registers, but often by complex initialization procedures. Dynamic C provides a specific function chain, `_GLOBAL_INIT`, for this purpose. Your program can add segments to the `_GLOBAL_INIT` function chain, as shown in the example below.

>     long my_func( char j );

>     main(){
>        my_func(100);
>     }

>     long my_func(char j){
>        static int i;
>        static long array[256];

>        // The GLOBAL_INIT section is automatically run once when the program starts up

>        #GLOBAL_INIT{
>           for( i = 0; i < 100; i++ ){
>              array[i] = i*i;
>           }
>        }

>        return array[j];   // only this code runs when the function is called
>     }

> The special directive `#GLOBAL_INIT{ }` tells the compiler to add the code in the block enclosed in braces to the `_GLOBAL_INIT` function chain. Any number of `#GLOBAL_INIT` sections may be used in your code. The order in which they are called is indeterminate since it depends on the order in which they were compiled. The storage class for variables used in a global initialization section must be static. Since the default storage class is auto, you must define variables as static in your application.

> The `_GLOBAL_INIT` function chain is always called when your program starts up, so there is nothing special to do to invoke it. In addition, it may be called explicitly at any time in an application program with the statement:

>     _GLOBAL_INIT();

> Make this call this with caution. All costatements and cofunctions will be initialized. See Section 7.2 for more information about calling `_GLOBAL_INIT()`.

> ### 4.22 Libraries

> Dynamic C includes many libraries--files of useful functions in source code form. They are located in the \LIB directory where Dynamic C was installed. To support larger memories, some changes to the Dynamic C environment were made in version 10.21. One such change is that the \LIB directory now contains two separate directories, Rabbit2000_3000 and Rabbit4000. Each directory contains the same structure previously used by \LIB, but the libraries have been updated for the Rabbit 4000 processor in the \Lib\Rabbit4000 directory.

> The default library file extension is "LIB". Dynamic C uses functions and data from library files and compiles them with an application program that is then downloaded to a controller or saved to a `.bin` file.

> An application program (the default file extension is `.c`) consists of a source code file that contains a main function (called `main`) and usually other user-defined functions. Any additional source files are considered to be libraries (though they may have a `.c` extension) and are treated as such. The minimum application program is one source file, containing only:

>     main(){}

> Libraries (those defined by you and those defined by Rabbit) are "linked" with the application through the `#use` directive. The `#use` directive identifies a file from which functions and data may be extracted. Files identified by `#use` directives are nestable, as shown below. (The `#use` directive is a replacement for the `#include` directive, which is not supported in Dynamic C.)

> Figure 4.1 Nesting Files in Dynamic C

> Most libraries needed by Dynamic C programs have #use statements in `lib\..\default.h`.

> Section 4.24 explains how Dynamic C knows which functions and global variables in a library are available for use.

> #### 4.22.1 LIB.DIR

> Any library that is to be #use'd in a Dynamic C program must be listed in the file `LIB.DIR`, or another `*.DIR` file specified by the user. `LIB.DIR` is in the root directory of the Dynamic C installation.

> The lib.dir strategy allows naming a folder with optional mask(s). Having no mask implies "*.*" and multiple masks are separated by ";" so that "lib" and "lib\*.*" both include all files and "`lib\*.lib;*.c;*.h*`" includes all files with extensions of `.lib`, `.c` and `.h`. Dynamic C generated files (e.g., `.mdl`, `.hxl`, etc.) are not parsed, which means they are excluded when using the wildcard mask.

> Dynamic C enforces unique file extension names regardless of path, so that "#use myfile.lib" can not use an unintended copy of `myfile.lib` as the list of pathnames included in `lib.dir` is searched for the first occurrence of that file extension. An error message naming both full paths will come up when trying to compile ANY program alerting the user of the infraction.

> A new feature introduced in Dynamic C10.21 is the ability to define masks that use exclusion criteria that will exclude specified folders and files from the "lib.dir" file. Such a lib.dir entry is just like a normal one except the it starts with ">", a character that Windows does not allow in a folder name. Once exclusions are defined, they persist throughout all entries that follow. To make this clear, look at the following examples of "lib.dir entries:

> Example 1:

> The following "lib.dir" entries:

>     >CVS
>     Lib\Rabbit4000
>     Samples\*.Lib

> excludes CVS folder trees throughout following entries, includes all files in Lib\Rabbit4000 and its subfolders (except for CVS subfolders), and includes all ".lib" files in Samples and its subfolders (except for CVS subfolders).

> Thus, the ordering of the LIB.DIR entries is meaningful, as shown in the next example.

> Example 2:

> As dictated by the following three entries, the file `Lib\Rabbit4000\BiosLib\Stdbios.c` is excluded from `LIB.DIR`.

>     >*.c
>     Samples
>     Lib\Rabbit4000

> Reordering the entries as shown below results in the file `Lib\Rabbit4000\BiosLib\Stdbios.c` being included in `LIB.DIR`.

>     Lib\Rabbit4000
>     >*.c
>     Samples

> ### 4.23 Headers

> The following table describes two kinds of headers used in Dynamic C libraries.

> Table 4-4. Dynamic C Library Headers
> Header Name | Description
> Module headers | Make functions and global variables in the library known to Dynamic C.
> Function Description headers | Describe functions. Function headers form the basis for function lookup help.

> You may also notice some "Library Description" headers at the top of library files. These have no special meaning to Dynamic C, they are simply comment blocks.

> ### 4.24 Modules

> A Dynamic C library typically contains several modules. Modules must be understood to write efficient custom libraries. Modules provide Dynamic C with the names of functions and variables within a library that may be referenced by files that have a `#use` directive for the library somewhere in the code.

> Modules organize the library contents in such a way as to allow for smaller code size in the compiled application that uses the library. To create your own libraries, write modules following the guidelines in this section.

> The scope of modules is global, but indeterminate compilation order makes the situation less than straightforward. Read this entire section carefully to understand module scope.

> #### 4.24.1 The Parts of a Module

> A module has three parts: the key, the header, and the body. The structure of a module is:

>     /*** BeginHeader func1, var2, .... */
>        prototype for func1
>        extern var2
>     /*** EndHeader */
>        definition of func1
>        declaration for var2
>        possibly other functions and data

> A module begins with its `BeginHeader` comment and continues until either the next `BeginHeader` comment or the end of the file is encountered.

> ##### 4.24.1.1 Module Key

> The module key is usually contained within the first line of the module header. It is a list of function and data names separated by commas. The list of names may continue on subsequent lines.

>     /*** BeginHeader [name1, name2, ....] */

> It is important to format the `BeginHeader` comment correctly, otherwise Dynamic C cannot find the contents of the module. The case of the word "beginheader" is unimportant, but it must be preceded by a forward slash, 3 asterisks and one space (`/*** )`. The forward slash must be the first character on the line. The `BeginHeader` comment must end with an asterisk and a forward slash ( `*/`).

> The key tells the compiler which functions exist in the module so the compiler can exclude the module if names in the key are not referenced. Data declarations (constants, structures, unions and variables) as well as macros and function chains (both `#makechain` and `#funchain` statements) do not need to be named in the key if they are completely defined in the header, i.e, no `extern` declaration. They are fully known to the compiler by being completely defined in the module header. An important thing to remember is that variables declared in a header section will be allocated memory space unless the declaration is preceded with `extern`.

> ##### 4.24.1.2 Module Header

> Every line between the `BeginHeader` and `EndHeader` comments belongs to the header of the module. When a library is linked to an application (i.e., the application has the statement: #use "library_name"), Dynamic C precompiles every header in the library, and only the headers.

> With proper function prototypes and variable declarations, a module header ensures proper type checking throughout the application program. Prototypes, variables, structures, typedefs and macros declared in a header section will always be parsed by the compiler if the library is #used, and everything will have global scope. It is even permissible to put function bodies in header sections, but it's not recommended because the function will be compiled with any application that #uses the library. Since variables declared in a header section will be allocated memory space unless the declaration is preceded with `extern`, the variable declaration should be in the module body instead of the header to save data space.

> The scope of anything inside the module header is global; this includes compiler directives. Since the headers are compiled before the module bodies, the last one of a given type of directive encountered will be in effect and any previous ones will be forgotten.

> Using compiler directives like `#class` or `#memmap` inside module headers is inadvisable. If it is important to set, for example, "#class auto" for some library modules and "#class static" for others, the appropriate directives should be placed inside the module body, not in the module header. Furthermore, since there is no guaranteed compilation order and compiler directives have global scope, when you issue a compiler directive to change default behavior for a particular module, at the end of the module you should issue another compiler directive to change back to the default behavior. For example, if a module body needs to have its storage class as static, have a "#class static" directive at the beginning of the module body and "#class auto" at the end.

> ##### 4.24.1.3 Module Body

> Every line of code after the `EndHeader` comment belongs to the body of the module until (1) end-of-file or (2) the `BeginHeader` comment of another module. Dynamic C compiles the entire body of a module if any of the names in the key or header are referenced anywhere in the application. So keep modules small, don't put all the functions in a library into one module. If you look at the Dynamic C libraries you'll notice that many modules consist of one function. This saves on code size, because only the functions that are called are actually compiled into the application.

> To further minimize waste, define code and data only in the body of a module. It is recommended that a module header contain only prototypes and `extern` declarations because they do not generate any code by themselves. That way, the compiler will generate code or allocate data only if the module is used by the application program.

> #### 4.24.2 Module Sample Code

> There are many examples of modules in the `Lib` directory of Dynamic C. The following code will illustrate proper module syntax and show the scope of directives, functions and variables.

>     /*** BeginHeader ticks*/
>        extern unsigned long ticks;
>     /*** EndHeader */

>        unsigned long ticks;

>     /*** BeginHeader Get_Ticks */
>        unsigned long Get_Ticks();
>     /*** EndHeader */

>     unsigned long Get_Ticks(){
>        ...
>     }

>     /*** BeginHeader Inc_Ticks */
>        void Inc_Ticks( int i );
>     /*** EndHeader */

>     #asm
>     Inc_Ticks::
>        or   a
>        ipset 1
>        ...
>        ipres
>        ret
>     #endasm

> There are three modules defined in this code. The first one is responsible for the variable `ticks`, the second and third modules define functions `Get_Ticks()` and `Inc_Ticks` that access the variable. Although `Inc_Ticks` is an assembly language routine, it has a function prototype in the module header, allowing the compiler to check calls to it.

> If the application program calls `Inc_Ticks` or `Get_Ticks()` (or both), the module bodies corresponding to the called routines will be compiled. The compilation of these routines triggers compilation of the module body corresponding to `ticks` because the functions use the variable `ticks`.

>     /*** BeginHeader func_a */

>     int func_a();

>     #ifdef SECONDHEADER
>        #define XYZ
>     #endif

>     /*** EndHeader */

>     int func_a(){
>     #ifdef SECONDHEADER
>        printf ("I am function A.\n");
>     #endif
>     }

>     /*** BeginHeader func_b */

>        int func_b();
>        #define SECONDHEADER

>     /*** EndHeader */

>        #ifdef XYZ
>           #define FUNCTION_B
>        #endif

>     int func_b() {
>        #ifdef FUNCTION_B
>           printf ("I am function B.\n");
>        #endif
>     }

> Let's say the above file is named `mylibrary.lib`. If an application has the statement `#use "mylibrary.lib"` and then calls `func_b()`, will the printf statement be reached? The answer is no. The order of compilation for module headers is sequential from the beginning of the file, therefore, the macro `SECONDHEADER` is undefined when the first module header is parsed.

> If an application #uses this library and then makes a call to `func_a()`, will that function's print statement be reached? The answer is yes. Since all the headers were compiled first, the macro `SECONDHEADER` is defined when the first module body is compiled.

> #### 4.24.3 Important Notes

> Remember that in a Dynamic C application there is only one file that contains `main()`. All other source files used by the file that contains `main()` are regarded as library files. Each library must be included in a `LIB.DIR` (or a user defined replacement for it). Although Dynamic C uses `.LIB` as the library extension, you may use anything you like as long as the complete path is entered in your `LIB.DIR` file.

> There is no way to define file scope variables in Dynamic C libraries.

> ### 4.25 Function Description Headers

> Each user-callable function in a Dynamic C library has a descriptive header preceding the function to describe the function. Function headers are extracted by Dynamic C to provide on-line help messages.

> The header is a specially formatted comment, such as the following example.

>     /* START FUNCTION DESCRIPTION **********************
>     WrIOport                      <IO.LIB>
>     SYNTAX: void WrIOport(int portaddr, int value);
>     DESCRIPTION:
>     Writes data to the specified I/O port.
>     PARAMETER1:  portaddr - register address of the port.
>     PARAMETER2:  value - data to be written to the port.

>     RETURN VALUE:  None
>     KEY WORDS: parallel port

>     SEE ALSO:  RdIOport
>     END DESCRIPTION ***********************************/

> If this format is followed, user-created library functions will show up in the Function Lookup <Ctrl+H> feature if the library is listed in `lib.dir` or its replacement. Note that these sections are scanned in when Dynamic C starts and changed libraries are rescanned with every Ctrl+H.

> ### 4.26 Support Files

> Dynamic C has several support files that are necessary in building an application. These files are listed below.

> Table 4-5. Dynamic C Support Files
> File Name | Purpose of File
> `DCW.CFG` | Contains configuration data for the target controller.
> `DC.HH` | Contains prototypes, basic type definitions, `#define`, and default modes for Dynamic C. This file can be modified by the programmer.
> `DEFAULT.H` | Contains a set of `#use` directives for each control product that Rabbit ships. This file can be modified.
> `LIB.DIR` | Contains pathnames for all libraries that are to be known to Dynamic C. The programmer can add to, or remove libraries from this list. The factory default is for this file to contain all the libraries on the Dynamic C distribution disk. Any library that is to be used in a Dynamic C program must be listed in the file`LIB.DIR`, or another `*.DIR` file specified by the user.
> `PROJECT.DCP DEFAULT.DCP` | These files hold the default compilation environment that is shipped from the factory. `DEFAULT.DCP` may be modified, but not `PROJECT.DCP`. See Chapter 18 for details on project files.

Dynamic C User's Manual | << Previous | Index | Next>> | rabbit.com