DCPUM
DCPUM
DCPUM
User’s Manual
019-0125 • 080121-H
The latest revision of this manual is available on the Rabbit Web site,
www.rabbit.com, for free, unregistered download.
Dynamic C User’s Manual
Part Number 019-0125 • 080121–H • Printed in the U.S.A.
Digi International Inc.© 2007 - 2008 • All rights reserved.
No part of the contents of this manual may be reproduced or transmitted in any form or by any means
without the express written permission of Digi International Inc.
Permission is granted to make one or more copies as long as the copyright page contained therein is
included. These copies of the manuals may not be let or sold for any reason without the express written
permission of Digi International Inc.
Trademarks
RabbitSys™ is a trademark of Digi International Inc.
Rabbit® and Dynamic C® are registered trademarks of Digi International Inc.
Windows® is a registered trademark of Microsoft Corporation
ii
Table of Contents
1. Installing Dynamic C ......................................... 1 4.15 Pointers .................................................... 29
1.1 Requirements .............................................. 1 4.16 Pointers to Functions, Indirect Calls........ 30
1.2 Assumptions ............................................... 1 4.17 Argument Passing.................................... 31
4.18 Program Flow .......................................... 32
2. Introduction to Dynamic C................................ 3 4.18.1 Loops ......................................... 32
2.1 The Nature of Dynamic C .......................... 3
4.18.2 Continue and Break ................... 33
2.1.1 Speed .............................................. 3
4.18.3 Branching................................... 34
2.2 Dynamic C Enhancements and Differences4
4.19 Function Chaining ................................... 36
2.3 Rabbit and Z180 Comparison..................... 6
4.20 Global Initialization................................. 37
3. Quick Tutorial ..................................................... 7 4.21 Libraries................................................... 38
3.1 Run DEMO1.C ........................................... 8 4.21.1 LIB.DIR ..................................... 39
3.1.1 Single Stepping .............................. 9 4.22 Headers .................................................... 39
3.1.2 Watch Expression........................... 9 4.23 Modules ................................................... 39
3.1.3 Breakpoint ...................................... 9 4.23.1 The Parts of a Module................ 40
3.1.4 Editing the Program ..................... 10 4.23.2 Module Sample Code................. 42
3.2 Run DEMO2.C ......................................... 10 4.23.3 Important Notes ......................... 43
3.2.1 Watching Variables Dynamically. 10 4.24 Function Description Headers ................. 44
3.3 Run DEMO3.C ......................................... 11 4.25 Support Files............................................ 44
3.3.1 Cooperative Multitasking............. 11 5. Multitasking with Dynamic C ........................ 45
3.4 Run DEMO4.C ......................................... 12
5.1 Cooperative Multitasking ......................... 45
3.4.1 Trace Macros................................ 13
5.2 A Real-Time Problem............................... 46
3.5 Summary of Features................................ 14
5.2.1 Solving the Real-Time Problem
4. Language ............................................................ 15 with a State Machine ......................... 47
4.1 C Language Elements ............................... 15 5.3 Costatements ............................................ 48
4.2 Punctuation Tokens................................... 16 5.3.1 Solving the Real-Time Problem
4.3 Data........................................................... 17 with Costatements ............................. 48
4.3.1 Data Type Limits.......................... 17 5.3.2 Costatement Syntax ..................... 49
4.4 Names ....................................................... 18 5.3.3 Control Statements....................... 49
4.5 Macros ...................................................... 19 5.4 Advanced Costatement Topics ................. 51
4.5.1 Macro Operators # and ##............ 19 5.4.1 The CoData Structure .................. 51
4.5.2 Nested Macro Definitions ............ 20 5.4.2 CoData Fields .............................. 52
4.5.3 Macro Restrictions ....................... 21 5.4.3 Pointer to CoData Structure ......... 53
4.6 Numbers.................................................... 21 5.4.4 Functions for Use With Named
4.7 Strings and Character Data ....................... 22 Costatements ..................................... 53
4.7.1 String Concatenation.................... 22 5.4.5 Firsttime Functions ...................... 54
4.7.2 Character Constants ..................... 23 5.4.6 Shared Global Variables............... 54
4.8 Statements................................................. 24 5.5 Cofunctions .............................................. 54
4.9 Declarations .............................................. 24 5.5.1 Cofunction Syntax ....................... 54
4.10 Functions.................................................. 25 5.5.2 Calling Restrictions...................... 55
4.11 Prototypes................................................. 25 5.5.3 CoData Structure.......................... 56
4.12 Type Definitions....................................... 26 5.5.4 Firsttime Functions ...................... 56
4.13 Aggregate Data Types.............................. 27 5.5.5 Types of Cofunctions ................... 56
4.13.1 Array .......................................... 27 5.5.6 Types of Cofunction Calls ........... 58
4.13.2 Structure ..................................... 28 5.5.7 Special Code Blocks .................... 59
4.13.3 Union.......................................... 28 5.5.8 Solving the Real-Time Problem
4.13.4 Composites................................. 28 with Cofunctions ............................... 60
4.14 Storage Classes ........................................ 29 5.6 Patterns of Cooperative Multitasking....... 60
The installation program will begin and guide you through the installation process.
1.1 Requirements
Dynamic C requires an IBM-compatible PC running Windows 2000 or later with at least one free COM or
USB port.
Please note that Windows Vista is supported by Dynamic C out of the box if there is only one processor in
the host PC or laptop. With multiple processors (a.k.a., dual cores) present in the host system, you must
check Windows “Processor Affinity” setting in order to ensure Vista compatibility with Dynamic C. Tech-
nical note TN257 “Running Dynamic C with Windows Vista” has instructions for modifying the “Proces-
sor Affinity” setting. This technical note is available on the Rabbit website:
http://www.rabbit.com/support/techNotes_whitePapers.shtml#dcp
Starting with Dynamic C 9.60, the “Processor Affinity” setting is set automatically.
1.2 Assumptions
It is assumed that the reader has a working knowledge of:
• The basics of operating a software program and editing files under Windows on a PC.
• Programming in a high-level language.
• Assembly language and architecture for controllers.
2.1.1 Speed
Dynamic C compiles directly to memory. Functions and libraries are compiled and linked and downloaded
on-the-fly. On a fast PC, Dynamic C might load 30,000 bytes of code in five seconds at a baud rate of
115,200 bps.
• Dynamic C has keywords that help protect data shared between different contexts (shared) or stored in
battery-backed memory (protected).
• Dynamic C has a set of features that allow the programmer to make the fullest use of xmem (extended
memory). The compiler supports a 1 MB physical address space.
Normally, Dynamic C takes care of memory management, but there are instances where the program-
mer will want to take control of it. Dynamic C has keywords and directives to help put code in the
proper place, such as: root, xmem, and #memmap.
See Chapter 9 for further details on memory management.
To initialize static variables in Static RAM (SRAM) use #GLOBAL_INIT sections. Note that other C
compilers will automatically initialize all static variables to zero that are not explicitly initialized before
entering the main function. Dynamic C programs do not do this because in an embedded system you
may wish to preserve the data in battery-backed RAM on reset
• The numerous include files found in typical C programs are not used because Dynamic C has a library
system that automatically provides function prototypes and similar header information to the compiler
before the user’s program is compiled. This is done via the #use directive. This is an important topic
for users who are writing their own libraries. Those users should refer to Section 4.23, “Modules” for
more information.
• When declaring pointers to functions, arguments should not be used in the declaration. Arguments may
be used when calling functions indirectly via pointer, but the compiler will not check the argument list
in the call for correctness. See Section 4.16 for more information
• Advanced users can see and modify the BIOS kernel directly.
• Board developers can design Dynamic C compatible boards around the Rabbit CPU by simply follow-
ing a few simple design guidelines and using a “skeleton” BIOS provided by Rabbit.
• A major feature is the ability to program and debug over the Internet or local Ethernet. This requires
either the use of a RabbitLink board, available alone or as an option with Rabbit-based development
kits, or the use of RabbitSys.
The subfolders contain sample programs that illustrate the use of the various Dynamic C libraries. For
example, the subfolders “Cofunc” and “Costate” have sample programs illustrating the use of
COFUNC.LIB and COSTATE.LIB, libraries that support cooperative multitasking using Dynamic C lan-
guage extensions. Besides its subfolders, the Samples folder also contains some sample programs to dem-
onstrate various aspects of Dynamic C. For example, the sample program Pong.c demonstrates output
to the Stdio window.
In the rest of this chapter we examine four sample programs in some detail.
To run DEMO1.C compile it using the Compile menu, and then run it by selecting “Run” in the
Run menu. (The keyboard shortcut <F9> will compile and run the program. You may also use the
green triangle toolbar button as a substitute for <F9>.)
The value of the counter should be printed repeatedly to the Stdio window if everything went well. If this
doesn’t work, review the following points:
• The target should be ready, indicated by the message “BIOS successfully compiled...” If you did not
receive this message or you get a communication error, recompile the BIOS by pressing <Ctrl+Y> or
select “Reset Target / Compile BIOS” from the Compile menu.
• A message reports “No Rabbit Processor Detected” in cases where the wall transformer is not con-
nected or not plugged in.
• The programming cable must be connected to the controller. (The colored wire on the programming
cable is closest to pin 1 on the programming header on the controller). The other end of the program-
ming cable must be connected to the PC serial port. The COM port specified in the Communications
After the program compiles a highlighted character (green) will appear at the first executable
statement of the program. Press the <F8> key to single step (or use the toolbar button). Each time
the <F8> key is pressed, the cursor will advance one statement. When you get to the statement:
for(j=0, j< ... , it becomes impractical to single step further because you would have to press
<F8> thousands of times. We will use this statement to illustrate watch expressions.
3.1.3 Breakpoint
Move the cursor to the start of the statement:
for (j=0; j<20000; j++);
To set a breakpoint on this statement, press <F2> or select “Toggle Breakpoint” from the Run menu. A red
highlight appears on the first character of the statement. To get the program running at full speed, press
<F9>. The program will advance until it hits the breakpoint. The breakpoint will start flashing both red and
green colors.
To remove the breakpoint, press <F2> or select “Toggle Breakpoint” on the Run menu. To continue pro-
gram execution, press <F9>. You will see the value of “i” displayed in the Stdio window repeatedly until
program execution is halted.
You can set breakpoints while the program is running by positioning the cursor to a statement and using
the <F2> key. If the execution thread hits the breakpoint, a breakpoint will take place. You can toggle the
breakpoint with the <F2> key and continue execution with the <F9> key.
main() {
int secs; // seconds counter
secs = 0; // initialize counter
(1) while (1) { // endless loop
The numbers in the left margin are reference indicators and not part of the code. Load and run the pro-
gram. The elapsed time is printed to the Stdio window once per second. Push several keys and note how
they are reported.
_TRACE
The _TRACE macro creates one entry in the trace buffer containing the program state information at the
time the macro executes. It is useful if you want to monitor one statement closely rather than follow the
flow of part of a program. In Demo4.c, _TRACE is executed at lines 45 and 77, as you can see in the
screenshot in Figure 3.3.
Figure 3.3 Trace window contents after running Demo4.c
The _TRACE macro does not affect the _TRACEON and _TRACEOFF macros, and likewise is not
affected by them. It will execute regardless of whether tracing is turned on or off. An interesting thing to
note about _TRACE is that it generate a trace statement even when it appears in a nodebug function.
_TRACEON
The _TRACEON macro turns on tracing. This does not cause any information to be recorded by itself like
the _TRACE macro, but rather causes a change of state within the debug kernel so that program state infor-
mation is recorded for program and library statements executed thereafter, until the _TRACEOFF macro is
executed or by menu command. Dynamic C captures the information you specified in the Project Options
dialog and displays it in the Trace window.
In Demo4.c, _TRACEON is executed in the function foo(). Note that tracing is turned on in the second
call to foo1() in main(), but that except for the _TRACE statement there are no trace statements for
foo1(). This is because statements in nodebug functions are not traceable.
_TRACEOFF
The _TRACEOFF macro turns off tracing, starting with the next statement after it executes. Instances of
the _TRACE macro will still execute, but tracing remains off until it is turned on by the _TRACEON macro
or by menu command.
Development Functions
When you load a program it appears in an editor window. You compile by clicking Compile on the task bar
or from the Compile menu. The program is compiled into machine language and downloaded to the target
over the serial port. The execution proceeds to the first statement of main, where it pauses, waiting to run.
Press <F9> or select “Run” on the Run menu. If want to compile and run the program with one keystroke,
use <F9>, the run command; if the program is not already compiled, the run command compiles it.
Single Stepping
This is done with the F8 key. The F7 key can also be used for single stepping. If the F7 key is used, then
descent into functions will take place. With F8 the function is executed at full speed when the statement
that calls it is stepped over.
Setting Breakpoints
The F2 key is used to toggle a breakpoint at the cursor position. Prior to Dynamic C 9, breakpoints could
only be toggled while in run mode, either while stopped at a breakpoint or when the program ran at full
speed. Starting with Dynamic C 9, breakpoints can be set in edit mode and retained when changing modes
or closing the file.
Watch Expressions
A watch expression is a C expression that is evaluated on command in the Watches window. An expression
is basically any type of C statement that can include operators, variables, structures and function calls, but
not statements that require multiple lines such as for or switch. You can have a list of watch expres-
sions in the Watches window. If you are single stepping, then they are all evaluated on each step. You can
also command the watch expressions to be evaluated by using the <Ctrl+U> command. When a watch
expression is evaluated at a breakpoint, it is evaluated as if the statement was at the beginning of the func-
tion where you are single stepping.
Costatements
A costatement is a Dynamic C extension that allows cooperative multitasking to be programmed by the
user. Keywords, like abort and waitfor, are available to control multitasking operation from within
costatements.
Execution Tracing
Execution tracing allows you to follow the flow of your program’s execution in real time instead of single
stepping through it. The Trace window can show which statement was executed, what type of action it
was, when it was executed, and the contents of the registers after executing it. You can also save the con-
tents of the Trace window to a file.
Token Description
16 rabbit.com Language
4.3 Data
Data (variables and constants) have type, size, structure, and storage class. Basic (a.k.a., primitive) data
types are shown below.
#define CHAR_BIT 8
#define UCHAR_MAX 255
#define CHAR_MIN 0
#define CHAR_MAX 255
#define MB_LEN_MAX 1
my_function // ok
_block // ok
test32 // ok
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
18 rabbit.com Language
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 pro-
grammer 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.
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.
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.
#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);
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);
20 rabbit.com Language
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 liter-
als, 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 let-
ter 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 magni-
tude 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.
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.
22 rabbit.com Language
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.
\a bell \b backspace
\f formfeed \n newline
\r carriage return \t tab
\v vertical tab \0 null character
\\ backslash \c the actual character c
\’ single quote \” double quote
Examples
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.
If an aggregate type (struct or union) is being declared, its internal structure has to be described as
shown below.
24 rabbit.com Language
4.10 Functions
The basic unit of a C application program is a function. Most functions accept parameters (a.k.a., argu-
ments) 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 auto-
matically after the program compiles or when the controller powers up. The beginning of the main func-
tion 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.
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.)
Use typedef to create a meaningful name for a class of data. Consider this example.
/*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.
26 rabbit.com Language
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.1 Array
A data type, whether it is simple or complex, can be replicated in an array. The declaration
represents a contiguous group of 10 integers. Array elements are referenced by their subscript.
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];
scale = matrix[i][j];
The first dimension of an array does not have to be specified as long as an initialization list is specified.
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
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 ele-
ments, 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
z = list[n].c[m];
...
list[0].c[22] = 0xFF37;
28 rabbit.com Language
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 mes-
sages 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.
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;” refer-
ences 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 vari-
able 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.
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.
for example:
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)();
30 rabbit.com Language
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.
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.
printf("%d\n", x);
printf("%d\n", y);
}
int intfunc(int x, int y){
return x+y;
}
4.18.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.
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.
32 rabbit.com Language
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.
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 state-
ment 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,
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 con-
dition 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 expres-
sion 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
}
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 state-
ment. 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.
34 rabbit.com Language
The switch statement, the most complex branching statement, allows the programmer to phrase a “mul-
tiple 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 constN values
matches the switch expression, the sequence of statements identified by the constN expression is exe-
cuted. 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 key-
word causes the program to exit the switch/case statement.
The colons (:) after case and default are required.
Function chain segments defined with segchain must appear in a function directly after data declara-
tions 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 fol-
lowing example shows how to call a function chain that is named recover.
#makechain recover
...
recover();
36 rabbit.com Language
4.20 Global Initialization
Various hardware devices in a system need to be initialized, not only by setting variables and control regis-
ters, 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.
// 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().
38 rabbit.com Language
4.21.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.
The lib.dir strategy starting with Dynamic C 9.30 allows naming a folder with optional mask(s). 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 gener-
ated file (e.g., .mdl, .hxl, etc.) are not parsed, which means they are excluded when using the wildcard
mask.
Dynamic C now 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 try-
ing to compile ANY program alerting the user of the infraction.
4.22 Headers
The following table describes two kinds of headers used in Dynamic C libraries.
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.23 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 straight-
forward. Read this entire section carefully to understand module scope.
A module begins with its BeginHeader comment and continues until either the next BeginHeader
comment or the end of the file is encountered.
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 pre-
ceded with extern.
40 rabbit.com Language
Using compiler directives like #class or #memmap inside module headers is inadvisable. If it is impor-
tant to set, for example, “#class auto” for some library modules and “#class static” for others, the appropri-
ate 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.
There are three modules defined in this code. The first one is responsible for the variable ticks, the sec-
ond 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 corre-
sponding 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.
42 rabbit.com Language
/*** 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 state-
ment 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.
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 only
when Dynamic C starts.
44 rabbit.com Language
5. MULTITASKING WITH DYNAMIC C
In a multitasking environment, more than one task (each representing a sequence of operations) can
appear to execute in parallel. In reality, a single processor can only execute one instruction at a time. If an
application has multiple tasks to perform, multitasking software can usually take advantage of natural
delays in each task to increase the overall performance of the system. Each task can do some of its work
while the other tasks are waiting for an event, or for something to do. In this way, the tasks execute almost
in parallel.
There are two types of multitasking available for developing applications in Dynamic C: preemptive and
cooperative. In a cooperative multitasking environment, each well-behaved task voluntarily gives up con-
trol when it is waiting, allowing other tasks to execute. Dynamic C has language extensions, costatements
and cofunctions, to support cooperative multitasking.
Preemptive multitasking is supported by the slice statement, which allows a computation to be divided into
small slices of a few milliseconds each, and by the µC/OS-II real-time kernel.
Top of loop
State machine
State machine
State machine
Within this endless loop, tasks are accomplished by small fragments of a program that cycle through a
series of states. The state is typically encoded as numerical values in C variables.
main() {
int i;
while(1) { // endless loop for multitasking framework
costate { // task 1
. . . // body of costatement
}
costate { // task 2
... // body of costatement
}
}
}
The most rudimentary way to perform this function is to idle (“busy wait”) in a tight loop at each of the
steps where waiting is specified. But most of the computer time will used waiting for the task, leaving no
execution time for other tasks.
task1state = 1; // initialization:
while(1){
switch(task1state){
case 1:
if( buttonpushed() ){
task1state=2; turnondevice1();
timer1 = time; // time incremented every second
}
break;
case 2:
if( (time-timer1) >= 60L){
task1state=3; turnondevice2();
timer2=time;
}
break;
case 3:
if( (time-timer2) >= 60L){
task1state=1; turnoffdevice1();
turnoffdevice2();
}
break;
}
/* other tasks or state machines */
}
If there are other tasks to be run, this control problem can be solved better by creating a loop that processes
a number of tasks. Now each task can relinquish control when it is waiting, thereby allowing other tasks to
proceed. Each task then does its work in the idle time of the other tasks.
while(1){
costate{ ... } // task 1
costate{ // task 2
waitfor( buttonpushed() );
turnondevice1();
waitfor( DelaySec(60L) );
turnondevice2();
waitfor( DelaySec(60L) );
turnoffdevice1();
turnoffdevice2();
}
The solution is elegant and simple. Note that the second costatement looks much like the original descrip-
tion of the problem. All the branching, nesting and variables within the task are hidden in the implementa-
tion of the costatement and its waitfor statements.
Costatements can be named or unnamed. If name is absent the compiler creates an unnamed structure of
type CoData for the costatement.
Figure 5-3 shows the execution thread through a costatement when a waitfor evaluates to true.
Figure 5-3 Execution thread when waitfor evaluates to true
costate ... {
statement
statement
...
waitfor( ... );
...
statement
statement
}
yield
The yield statement makes an unconditional exit from a costatement or a cofunction. Execution contin-
ues at the statement following yield the next time the costatement or cofunction is encountered by the
execution thread.
Figure 5-4 Execution thread with yield statement
A costatement can have as many C statements, including abort, yield, and waitfor statements, as
needed. Costatements can be nested.
The function isCoDone() returns true (1) if both the STOPPED and INIT flags are set. The function
isCoRunning() returns true (1) if the STOPPED flag is not set.
The CSState field applies only if the costatement has a name. The CSState flag has no meaning for
unnamed costatements or cofunctions.
Last Location
The two fields lastlocADDR and lastlocCBR represent the 24-bit address of the location at which to
resume execution of the costatement. If lastlocADDR is zero (as it is when initialized), the costatement
executes from the beginning, subject to the CSState flag. If lastlocADDR is nonzero, the costatement
resumes at the 24-bit address represented by lastlocADDR and lastlocCBR.
These fields are zeroed whenever one of the following is true:
• the CoData structure is initialized by a call to _GLOBAL_INIT, CoBegin or CoReset
• the costatement is executed to completion
• the costatement is aborted.
Check Sum
The ChkSum field is a one-byte check sum of the address. (It is the exclusive-or result of the bytes in
lastlocADDR and lastlocCBR.) If ChkSum is not consistent with the address, the program will
generate a run-time error and reset. The check sum is maintained automatically. It is initialized by
_GLOBAL_INIT, CoBegin and CoReset.
First Time
The firsttime field is a flag that is used by a waitfor, or waitfordone statement. It is set to 1
before the statement is evaluated the first time. This aids in calculating elapsed time for the functions
DelayMs, DelaySec, DelayTicks, IntervalTick, IntervalMs, and IntervalSec.
For more information see the Dynamic C Function Reference Manual. These functions should be called
inside a waitfor statement because they do not yield while waiting for the desired time to elapse, but
instead return 0 to indicate that the desired time has not yet elapsed.
5.5 Cofunctions
Cofunctions, like costatements, are used to implement cooperative multitasking. But, unlike costatements,
they have a form similar to functions in that arguments can be passed to them and a value can be returned
(but not a structure).
The default storage class for a cofunction’s variables is Instance. An instance variable behaves like
a static variable, i.e., its value persists between function calls. Each instance of an Indexed Cofunction
has its own set of instance variables. The compiler directive #class does not change the default storage
class for a cofunction’s variables.
All cofunctions in the program are initialized when the function chain _GLOBAL_INIT is called. This
call is made by premain.
cofunc, scofunc
The keywords cofunc or scofunc (a single-user cofunction) identify the statements enclosed in curly
braces that follow as a cofunction.
name
A name can be any valid C name not previously used. This results in the creation of a structure of type
CoData of the same name.
dim
The cofunction name may be followed by a dimension if an indexed cofunction is being defined.
cofunction body
A cofunction can have as many C statements, including abort, yield, waitfor, and waitfordone
statements, as needed. Cofunctions can contain calls to other cofunctions.
int j,k,x,y,z;
j = waitfordone x = Cofunc1;
k = waitfordone{ y=Cofunc2(...); z=Cofunc3(...); }
The keyword waitfordone (can be abbreviated to the keyword wfd) must be inside a costatement or
cofunction. Since a cofunction must be called from inside a wfd statement, ultimately a wfd statement
must be inside a costatement. If only one cofunction is being called by wfd the curly braces are not
needed.
The wfd statement executes cofunctions and firsttime functions. When all the cofunctions and
firsttime functions listed in the wfd statement are complete (or one of them aborts), execution pro-
ceeds to the statement following wfd. Otherwise a jump is made to the ending brace of the costatement or
cofunction where the wfd statement appears and when the execution thread comes around again control is
given back to wfd.
In the example above, x, y and z must be set by return statements inside the called cofunctions. Exe-
cuting a return statement in a cofunction has the same effect as executing the end brace. In the example
above, the variable k is a status variable that is set according to the following scheme. If no abort has taken
place in any cofunction, k is set to 1, 2, ..., n to indicate which cofunction inside the braces finished exe-
cuting last. If an abort takes place, k is set to -1, -2, ..., -n to indicate which cofunction caused the abort.
The value between the square brackets must be positive and less than the maximum number of instances
for that cofunction. There is no runtime checking on the instance selected, so, like arrays, the programmer
is responsible for keeping this value in the proper range.
5.5.5.2.1 Indexed Cofunction Restrictions
Costatements are not supported inside indexed cofunctions. Single user cofunctions can not be indexed.
This is because the same cofunction is being called from the second location after it has already started,
but not completed, execution for the call from the first location. The cofunction is a state machine and it
has an internal statement pointer that cannot point to two statements at the same time.
Single-user cofunctions can be used instead. They can be called simultaneously because the second and
additional callers are made to wait until the first call completes. The following statement, which contains
two calls to single-user cofunction, is okay.
waitfordone( scofunc_nameA(); scofunc_nameA();}
loopinit()
This function should be called in the beginning of a program that uses single-user cofunctions. It initializes
internal data structures that are used by loophead().
loophead()
This function should be called within the “big loop” in your program. It is necessary for proper single-user
cofunction abandonment handling.
main(){
int x;
for(x=0;x<=10;x++) {
loophead();
if(x<5) {
costate {
wfd SCofTest(1); // first caller
}
}
costate {
wfd SCofTest(2); // second caller
}
}
}
In this example two tasks in main() are requesting access to SCofTest. The first request is honored
and the second request is held. When loophead() notices that the first caller is not being called each
time around the loop, it cancels the request, calls the abandonment code and allows the second caller in.
for(;;){
costate{ // task 1
wfd emergencystop();
for (i=0; i<MAX_DEVICES; i++)
wfd turnoffdevice(i);
}
costate{ // task 2
wfd x = buttonpushed();
wfd turnondevice(x);
waitfor( DelaySec(60L) );
wfd turnoffdevice(x);
}
...
costate{ ... } // task n
}
Using cofunctions, new machines can be added with only trivial code changes. Making
buttonpushed() a cofunction allows more specificity because the value returned can indicate a partic-
ular button in an array of buttons. Then that value can be passed as an argument to the cofunctions
turnondevice and turnoffdevice.
The next statement will execute after the last character is sent.
Some tasks may not have an end. They are endless loops. For example, a task to control a servo loop may
run continuously to regulate the temperature in an oven. If there are a a number of tasks that need to run
continuously, then they can be called using a single waitfordone statement as shown below.
costate {
waitfordone { Task1(); Task2(); Task3(); Task4(); }
[ to come here is an error ]
}
Each task will receive some execution time and, assuming none of the tasks is completed, they will con-
tinue to be called. If one of the cofunctions should abort, then the waitfordone statement will abort,
and corrective action can be taken.
If all goes well, the first costatement will be executed at the periodic rate. The second costatement will,
however, be delayed by the first costatement. The third will be delayed by the second, and so on. The fre-
quency of the routine and the time it takes to execute comprise the granularity of the routine.
If the routine executes every 25 milliseconds and the entire group of costatements executes in 5 to 10 mil-
liseconds, then the granularity is 30 to 35 milliseconds. Therefore, the delay between the occurrence of a
waitfor event and the statement following the waitfor can be as much as the granularity, 30 to 35
ms. The routine may also be interrupted by higher priority tasks or interrupt routines, increasing the varia-
tion in delay.
The consequences of such variations in the time between steps depends on the program’s objective. Sup-
pose that the typical delay between an event and the controller’s response to the event is 25 ms, but under
unusual circumstances the delay may reach 50 ms. An occasional slow response may have no conse-
quences whatsoever. If a delay is added between the steps of a process where the time scale is measured in
seconds, then the result may be a very slight reduction in throughput.
If there is a delay between sensing a defective product on a moving belt and activating the reject solenoid
that pushes the object into the reject bin, the delay could be serious. If a critical delay cannot exceed 40
ms, then a system will sometimes fail if its worst-case delay is 50 ms.
context_buffer_size
This value must evaluate to a constant integer. The value specifies the number of bytes for the buffer
context_buffer. It needs to be large enough for worst-case stack usage by the user program and
interrupt routines.
time_slice
The amount of time in ticks for the slice to run. One tick = 1/1024 second.
name
When defining a named slice statement, you supply a context buffer as the first argument. When you
define an unnamed slice statement, this structure is allocated by the compiler.
5.9.2 Usage
The slice statement can run both cooperatively and preemptively all in the same framework. A slice
statement, like costatements and cofunctions, can suspend its execution with an abort, yield, or
waitfor. It can also suspend execution with an implicit yield determined by the time_slice
parameter that was passed to it. A routine called from the periodic interrupt forms the basis for scheduling
slice statements. It counts down the ticks and changes the slice statement’s context.
struct SliceData {
int time_out;
void* my_sp;
void* caller_sp;
CoData codata;
}
struct SliceBuffer {
SliceData slice_data;
char stack[]; // fills rest of the slice buffer
};
main () {
int x, y, z;
...
for (;;) {
costate a {
...
}
slice(500, 20) { // slice_a
...
}
slice(500, 40) { // slice_b
...
}
}
}
5.9.5.2 Example 2
This code guarantees that the first slice starts on TICK_TIMER evenly divisible by 80 and the second
starts on TICK_TIMER evenly divisible by 105.
main() {
for(;;) {
costate {
slice(500,20) { // slice_a
waitfor(IntervalTick(80));
...
}
slice(500,50) { // slice_b
waitfor(IntervalTick(105);
...
}
}
}
}
main() {
int time_left;
long start_time;
for(;;) {
start_time = TICK_TIMER;
slice(500,20) { // slice_a
waitfor(IntervalTick(80));
...
}
slice(500,50) { // slice_b
waitfor(IntervalTick(105));
...
}
time_left = 75-(TICK_TIMER-start_time);
if(time_left>0) {
slice(500,75-(TICK_TIMER-start_time)) { // slice_c
...
}
}
}
These macros can be placed into each µC/OS-II application so that the number of each size stack can be
customized based on the needs of the application. Suppose that an application needs 5 tasks, and each task
has a consecutively larger stack. The macros and calls to OSTaskCreate would look as follows
#define STACK_CNT_256 2 // number of 256 byte stacks
#define STACK_CNT_512 2 // number of 512 byte stacks
#define STACK_CNT_1K 1 // number of 1K stacks
#define STACK_CNT_2K 1 // number of 2K stacks
#define STACK_CNT_4K 1 // number of 4K stacks
Note that STACK_CNT_256 is set to 2 instead of 1. µC/OS-II always creates an idle task which runs
when no other tasks are in the ready state. Note also that there are two 512 byte stacks instead of one. This
is because the program is given a 512 byte stack. If the application utilizes the µC/OS-II statistics task,
then the number of 512 byte stacks would have to be set to 3. (Statistic task creation can be enabled and
disabled via the macro OS_TASK_STAT_EN which is located in ucos2.lib). If only 6 stacks were
declared, one of the calls to OSTaskCreate would fail.
5.10.1.3 Restrictions
At the time of this writing, µC/OS-II for Dynamic C is not compatible with the use of slice statements.
Also, see the function description for OSTimeTickHook() for important information about preserving
registers if that stub function is replaced by a user-defined function.
Due to Dynamic C's stack allocation scheme, special care should be used when posting messages to either
a mailbox or a queue. A message is simply a void pointer, allowing the application to determine its mean-
ing. Since tasks can have their stacks in different segments, auto pointers declared on the stack of the task
posting the message should not be used since the pointer may be invalid in another task with a different
stack segment.
Interrupt X
Interrupt X ISR
ipres
Task 1
If, however, an ISR needs to signal a task to the ready state, then the ISR must be tasking aware. In the
example in Figure 5-8, the TA-ISR increments the interrupt nesting counter, does the work necessary for
the ISR, readies a higher priority task, decrements the nesting count, and returns to the higher priority task.
Figure 5-8 Type 2 ISR
Task 2
Interrupt X
Nesting = 1
Interrupt X TA-ISR Task 1 is readied
Nesting = 0
ipres
Task 1
It may seem as though the ISR in Figure 5-8 does not have to increment and decrement the nesting count.
However, this is very important. If the ISR for Interrupt X is called during an ISR that re-enables interrupts
before completion, scheduling should not be performed when Interrupt X completes; scheduling should
instead be deferred until the least nested ISR completes. Figure 5-9 shows an example of this situation.
Task 2
Interrupt Z
Nesting = 1
Do critical code
Interrupt Z TA-ISR ipres
Interrupt X
Nesting = 2
Interrupt X TA-ISR Task 1 is readied
Nesting = 1
ipres
Finish ISR
Nesting = 0
Task 1
As can be seen here, although the ISR for interrupt Z does not signal any tasks by posting to a semaphore,
mailbox, or queue, it must increment and decrement the interrupt nesting count since it re-enables inter-
rupts (ipres) prior to finishing all of its work.
µC/OS-II Application
Call OSIntExit
Yes
Is Nesting == 0 ? Is switch pending ?
No
No Yes
The Dynamic C serial port functions (RS232.LIB functions) should be used in a restricted manner with
µC/OS-II. Two tasks can use the same port as long as both are not reading, or both are not writing; i.e., one
task can read from serial port X and another task can write to serial port X at the same time without con-
flict.
It is assumed that the reader has a familiarity with µC/OS-II or has a µC/OS-II reference (MicroC/OS-II,
The Real-Time Kernel by Jean J. Labrosse is highly recommended).
If a custom stack configuration is needed also, define the necessary macros for the counts of the different
stack sizes needed by the application.
#define STACK_CNT_256 1 // idle task stack
#define STACK_CNT_512 2 // initial program + stat task stack
#define STACK_CNT_1K 10 // task stacks
#define STACK_CNT_2K 10 // number of 2K stacks
In the application code, follow the µC/OS-II and stack configuration constants with a #use
“ucos2.lib” statement. This ensures that the definitions supplied outside of the library are used, rather
than the defaults in the library.
This configuration uses 20 tasks, two semaphores, up to 15 memory partitions that the memory manager
will control, and makes use of the statistics task. Note that the configuration constants for task creation,
task deletion, and semaphores are not defined, as the library defaults will suffice. Also note that ten of the
5.10.4.3 Examples
The following sample programs demonstrate the use of the default configuration supplied in UCOS2.LIB
and a custom configuration which overrides the defaults.
Example 1
In this application, ten tasks are created and one semaphore is created. Each task pends on the semaphore,
gets a random number, posts to the semaphore, displays its random number, and finally delays itself for
three seconds.
Looking at the code for this short application, there are several things to note. First, since µC/OS-II and
slice statements are mutually exclusive (both rely on the periodic interrupt for a “heartbeat”), #use
“ucos2.lib” must be included in every µC/OS-II application (1). In order for each of the tasks to have
access to the random number generator semaphore, it is declared as a global variable (2). In most cases, all
mailboxes, queues, and semaphores will be declared with global scope. Next, OSInit() must be called
before any other µC/OS-II function to ensure that the operating system is properly initialized (3). Before
µC/OS-II can begin running, at least one application task must be created. In this application, all tasks are
created before the operating system begins running (4). It is perfectly acceptable for tasks to create other
tasks. Next, the semaphore each task uses is created (5). Once all of the initialization is done,
OSStart() is called to start µC/OS-II running (6). In the code that each of the tasks run, it is important
to note the variable declarations. Each task runs as an infinite loop and once this application is started,
µC/OS-II will run indefinitely.
OSTaskQuery(OS_PRIO_SELF, &data);
while(1)
{
// Rand is not reentrant, so access must be controlled via a semaphore.
OSSemPend(RandomSem, 0, &err);
RNum = (int)(rand() * 100);
OSSemPost(RandomSem);
printf("Task%02d's random #: %d\n",data.OSTCBPrio,RNum);
// Wait 3 seconds in order to view output from each task.
OSTimeDlySec(3);
}
}
5.11 Summary
Although multitasking may actually decrease processor throughput slightly, it is an important concept. A
controller is often connected to more than one external device. A multitasking approach makes it possible
to write a program controlling multiple devices without having to think about all the devices at the same
time. In other words, multitasking is an easier way to think about the system.
• Software Breakpoints - Stop execution, allow the available debug windows to be examined: Stack,
Assembly, Dump and Register windows are always available.
• Single Stepping - Execute one C statement or one assembly statement. This is an extension of break-
points, so again, the Stack, Assembly, Dump and Register windows are always available.
• Watch Expressions - Keep running track of any valid C expression in the application. Fly-over hints
evaluate any watchable statement.
• Memory Dump - Displays blocks of raw values and their ASCII representation at any memory location
(can also be sent to a file).
• MAP File - Shows a global view of the program: memory usage, mapping of functions, global/static
data, parameters and local auto variables, macro listing and a function call graph.
• Assert Macro - This is a preventative measure, a kind of defensive programming that can be used to
check assumptions before they are used in the code. This was introduced in Dynamic C 8.51.
• Blinking Lights - LEDs can be toggled to indicate a variety of conditions. This requires a signal line
connected to an LED on the board.
• Symbolic Stack Trace - Helps customers find out the path of the program at each single step or break
point. By looking through the stack, it is possible to reconstruct the path and allow the customer to eas-
ily move backwards in the current call tree to get a better feeling for the current debugging context.
• Persistent Breakpoints - Persistent breakpoints mean the information is retained when transitioning
back and forth from edit mode to debug mode and when a file is closed and re-opened.
• Enhanced Watch Expressions - The Watches window is now a tree structure capable of showing struct
members. That is, all members of a structure become viewable as watch expressions when a structure is
added, without having to add them each separately.
• Enhanced Memory Dumps - Changed data in the Memory Dump window is highlighted in reverse
video or in customizable colors every time you single step in either C or assembly.
• Enhanced Mode Switching - Debug mode can be entered without a recompile and download. If the
contents of the debugged program are edited, Dynamic C prompts for a recompile.
Execution tracing is available with Dynamic C version 9. For more information on this debugging feature
please see technical note TN253 “Execution Tracing.” All technical notes are available at rabbit.com.
6.3.1 printf()
The printf() function has always been available in Dynamic C, with output going to the Stdio window
by default, and optionally to a file (by configuring the Stdio window contents to log to a file). The ability
to redirect output to any one of the serial ports A, B, C or D was introduced in Dynamic C 7.25. In DC
8.51, serial ports E and F were added for the Rabbit 3000. See Samples\stdio_serial.c for
instructions on how to use the serial port redirect. This feature is intended for debug purposes only.
The syntax for printf() is explained in detail in the Dynamic C Function Reference Manual, including
a listing of allowable conversion characters.
Pros A printf() statement is quick, easy and sometimes all that is needed to nail
down a problem.
You can use #ifdef directives to create levels of debugging information that
can be conditionally compiled using macro definitions. This is a technique used
by Rabbit engineers when developing Dynamic C libraries. In the library code
you will see statements such as:
#ifdef LIBNAME_DEBUG
printf(“Insert information here.\n”);
...
#endif
...
#ifdef LIBNAME_VERBOSE
printf(“Insert more information.\n”);
...
#endif
By defining the above mentioned macro(s) you include the corresponding printf
statements.
Cons The printf() function is so easy to use, it is easy to overuse. This can lead to
a shortage of root memory. A solution to this that allows you to still have lots of
printf strings is to place the strings in extended memory (xmem) using the key-
word xdata and then call printf() with the conversion character “%ls.” An
overuse of printf statements can also affect execution time.
Pros Software breakpoints can be set on any C statement unless it is marked “node-
bug” and in any “#asm debug” assembly block. Breakpoints let you run a pro-
gram at full speed until the specified stopping point is reached. You can set
multiple breakpoints in a program or even on the same line. They are easy to tog-
gle on and off individually and can all be cleared with one command. You can
choose whether to leave interrupts turned on (soft breakpoint) or not (hard
breakpoint).
When stopped at a breakpoint, you can examine up-to-date contents in debug
windows and choose other debugging features to employ, such as single step-
ping, dumping memory, fly-over watch expressions.
Cons To support large sector flash, breakpoint internals require that breakpoint over-
head remain, even when the breakpoint has been toggled off. Recompile the pro-
gram to remove this overhead.
When the debug keyword is added to an assembly block, relative jumps (which
are limited to 128 bytes) may go out of range. If this happens, change the JR in-
struction to a JP instruction. Another solution is to embed a null C statement in
the assembly code like so:
#asm
...
c ; // Set a breakpoint on the semicolon
...
#endasm
Uses Use software breakpoints when you need to stop at a specified location to begin
single stepping or to examine variables, memory locations or register values.
Pros Single stepping allows you to closely supervise program execution at the source
code level, either by C statement or assembly statement. This helps in tracing the
logic of the program. You can single step any debuggable statement. Even Dy-
namic C library functions can be stepped into as long as they are not flagged as
not available with the keyword nodebug.
Cons Single stepping is of limited use if interaction with an external device is being
examined; an external device does not stop whatever it is doing just because the
execution of the application has been restrained.
Also, single stepping can be very tedious if stepping through many instructions.
Well-placed breakpoints might serve you better.
Uses Single stepping is typically used when you have isolated the problem and have
stopped at the area of interest using a breakpoint.
Example To single step through a program instead of running at full execution speed, you
must either set a breakpoint while in edit mode (if you have DC 9) or compile
the program without running it.
To compile the program with-
out running it, use the Com-
pile menu option, the
keyboard shortcut F5 or the
toolbar menu button (pictured
to the left of the Compile
menu option).
F7, F8, Alt+F7 and Alt+F8 are
the keyboard shortcuts for
stepping through code. Use F7 if you want to step at the C statement level, but
want to step into calls to debuggable functions. Use F8 instead if you want to
step over function calls.
If the Assembly window is open, the stepping will be done by assembly instruc-
tion instead of by C statement if the feature “Enable instruction level single step-
ping” is checked on the Debugger tab of the Project Options dialog; otherwise,
stepping is done by C statement regardless of the status of the Assembly win-
dow. If you have checked “Enable instruction level single stepping” but wish to
continue to step by C statement when the Assembly window is open, use Alt+F7
or Alt+F8 instead of F7 or F8.
Pros Any valid C expression can be watched. Multiple expressions can be watched
simultaneously. Once a watch is set on an expression, its value is updated in the
Watches window whenever program execution is stopped.
The Watches window may be updated while the program is running
(which will affect timing) by issuing the “Update Watch Window” com-
mand: use the Inspect menu, Ctrl+U or the toolbar menu button shown
hereto update the Watches window.
You can use flyover hints to find out the value of any highlighted C expression
when the program is stopped.
Cons The scope of variables in watch expressions affects the value that is displayed in
the Watches window. If the variable goes out of scope, its true value will not be
displayed until it comes back into scope.
Keep in mind two additional things, which are not bad per se, but could be if they
are used carelessly: Assignment statements in a watch expression will change
the value of a variable every time watches are evaluated. Similarly, when a func-
tion call is in a watch expression, the function will run every time watches are
evaluated.
Uses Use a watch expression when the value of the expression is important to the be-
havior of the part of the program you are analyzing.
Pros Like watches, you can use the Evaluate Expression feature on any valid C ex-
pression. Multiple Evaluate Expression dialogs can be opened simultaneously.
Cons Can alter program data adversely if the change being made is not thought out
properly
Uses This feature can be used to quickly and easily explore a variant of program flow.
Example Say you have an application that is supposed to treat the 100th iteration of a loop
as a special case, but it does not. You do not want to set a breakpoint on the loop-
ing statement and hit F9 that many times, so instead you force the loop variable
to equal 99 using the evaluate expression dialog. To do this compile the program
without running it. Set a breakpoint at the start of the loop and then single step
to get past the loop variable initialization. Open the Inspect menu and choose
Evaluate Expression. Type in “j=99” and click on the Evaluate button. Now you
are ready to start examining the program’s behavior.
Pros Dump windows allow access to any memory location, beginning at any address.
There are alignment options; the data can be viewed as bytes, words or double-
words using a right-click menu.
Cons The Dump window does not contain symbolic information, which makes some
information harder to decipher. There is the potential for increased debugging
overhead if you open multiple dump windows and make them large.
Uses Use a dump window when you suspect memory is being corrupted. Or to watch
string or numerical data manipulation proceed. String manipulation can easily
cause memory corruption if you are not careful.
Cons If the compile was not successful, for example you get a message that says you
ran out of root code space, the map file will still be created, but will contain in-
complete and possibly incorrect information.
Uses Map files are useful when you want to gather more data or are trying to get a
comprehensive overview of the program. A map file can help you make better
use of memory in cases where you are running short or are experiencing stack
overflow problems.
Example Say you are pushing the limits of memory in your application and want to see
where you can shave bytes. The map file contains sizes for all the data used in
your program.The screen shot below shows some code and part of its map file.
Maybe you meant to type “200” as the size for my_array and added a zero on
the end by mistake. (This is a good place to mention that using hard-coded val-
ues is more prone to error than defining and using constants.)
Scanning the size column, the mistake jumps out at you more readily than look-
ing at the code, maybe because you expect to see “200” and so your brain filters
out the extra zero. For whatever reason, looking at the same information in a dif-
ferent format allows you to see more.
The size value for functions might not be accurate because it measures code dis-
tance. In other words, if a function spans a gap created with a follows action, the
size reported for the function will be much greater than the actual number of
The addresses in the format xx:yyyy are physical addresses. For code xx is the
XPC value, for data it is the value of DATASEG; yyyy is the PC value for both
code and data. In the above map file you can see examples of both code and data
addresses. Addresses in the format xx:yyyy are transformed by the MMU into a
5-digit physical address.
We will use the address fa:e64c to explain the actions of the MMU. It is really
very simple if you can do hex arithmetic in your head or have a decent calculator.
The MMU takes the XPC or DATASEG value, appends three zeros to it, then
adds it to the PC value, like so:
fa000 + e64c = 10864c
A sixth digit in the result is ignored, leaving us with the value 0x0864c. This is
the physical address. Again, you can check this in a couple of dump windows by
typing in the 5-digit physical address for one window and the XPC:offset into
another and seeing that the contents are the same.
The third way does not require tracing to be enabled and it can be done in nodebug functions.
• _TRACE: A macro that causes itself, and only itself, to be traced.
Note that execution tracing is intrusive, slightly more so when the Trace window is open.
Pros The large amount of tracing information that may be saved on the host PC is
available even if the program crashes. Tracing information fields can be turned
on and off by the user on the Debugger tab of the Project Options dialog. The
size of the trace buffer, which determines the number of trace entries, and wheth-
er to save the buffer to a file on program termination are also decided on the De-
bugger tab.
Cons Execution tracing alters the timing of a program because tracing information is
inserted between every source statement that is executed. Therefore, execution
tracing may not be useful in tracking down a timing related problem... it might
even cause one.
Uses A good data gathering tool to use when you are not sure what is happening.
Example Say you have an application in which program flow deviates at some unknown
point that is too tedious to detect by stepping. With execution tracing enabled,
compile the program and click “Trace On” in the Inspect menu. Run the program
and stop when the deviation is known or suspected to have occurred. Open the
Trace window. You can now follow the execution at any point in the trace by
double-clicking to source, or save to a file and grep for pertinent function calls
or lines executed.
Pros Provides a concise history of the call sequence and values of local variables and
function arguments that led to the current breakpoint, all for a very small cost in
execution time and BIOS memory.
Cons Currently, the Stack Trace window can not trace the parameters and local vari-
ables in cofunctions. Also the contents of the window can not be saved after a
program crash.
Uses Use stack tracing to capture the call sequence leading to a breakpoint and to see
the values of functions arguments and local variables.
Example Say you have a function that is behaving badly. You can set a breakpoint in the
function and use the Stack Trace window to examine the function call sequence.
Examining the call sequence and the parameters being passed might give enough
information to solve the problem.
The following screenshot shows an instance of qsortdemo.c and the Stack
Trace window. Note that the call to memcpy() is not represented on the stack.
The reason? Its stack activity had completed and program execution had re-
turned to main() when the stack was traced at the breakpoint in the function
mycmp().
Figure 6.2 Using Stack Trace
Pros The assert macro is self-checking software. It lets you explicitly state something
is true, and if it turns out to be false, the program terminates with an error mes-
sage. At the time of this writing, this link contained an excellent write-up on the
assert macro:
http://www.embedded.com/story/OEG20010311S0021
Cons Side effects can occur if the assert macro is not coded properly, e.g.,
assert(i=1)
will never trigger the assert and will change the value of the variable i; it should
be coded as:
assert(i==1)
Uses Use the assert macro when you must make sure your assumption is accurate.
in your application or use Inspect | Evaluate Expression. The Stdio window will display something similar
to the following:
A region is a contiguous piece of memory. Theoretically, up to four regions can exist; a region that is
marked “dummy” is a region that does not exist. Each region is identified as “normal” or “BB RAM,”
which refers to memory that is battery-backed.
SerialIO.exe
The utility serialIO.exe is located in \Diagnostics\Serial_IO. It is also in the file
SerialIO_1.zip, available for download at the Rabbit website. This utility is a specialized terminal
emulator program and comes with several diagnostic programs. The diagnostic programs test a variety of
functionality, and allow the user to simulate some of the behavior of the Dynamic C download process.
The utility has a Help button that gives complete instructions for its use. The Rabbit 3000 Designer’s
Handbook in the chapter titled “Troubleshooting Tips for New Rabbit-Based Systems” explains some of
the diagnostic programs that come with the serialIO utility. Understanding the information in this chapter
will allow you to write your own diagnostic programs for the serialIO utility.
reset_demo.c
The sample program Samples\reset_demo.c demonstrates using the functions that check the reason
for a reset: hard reset (power failure or pressing the reset button), soft reset (initiated by software), or a
watchdog timeout.
Error Logging
Chapter 8, “Run-Time Errors,” describes the exception handling routine for run-time errors that is supplied
with Dynamic C. The default handler may be replaced with a user-defined handler. Also error logging can
be enabled by setting ENABLE_ERROR_LOGGING to 1 in the BIOS (prior to Dynamic C version 9.30) or
in ERRLOGCONFIG.LIB (starting with DC 9.30). See Chapter 8 for more information.
Compiler Options
The Compiler tab of the Project Options dialog contains several options that assist debugging. They are
summarized here and fully documented starting on “Compiler Tab”.
• List Files - When enabled, this option generates an assembly list file for each compile. The list file con-
tains the same information and is in the same format as the contents of the Assembly window. List files
can be very large.
• Run-Time Checking - Run-time checking of array indices and pointers are enabled by default.
• Type Checking - Compile-time checking of type options are enabled by default. There are three type
checking options, labeled as: Prototype, Demotion and Pointer. Checking prototypes means that argu-
ments passed in function calls are checked against the function prototype. Demotion checking means
that the automatic conversion of a type to a smaller or less complex type is noted. Pointer checking
refers to making sure pointers of different types being intermixed are cast properly.
See the section titled, “Type Checking” on page 272 for more information.
Blinking Lights
Debugging software by toggling LEDs on and off might seem like a strange way to approach the problem,
but there are a number of situations that might call for it. Maybe you just want to exercise the board hard-
ware. Or, let us say you need to see if a certain piece of code was executed, but the board is disconnected
from your computer and so you have no way of viewing printf output or using the other debugging tools.
Or, maybe timing is an issue and directly toggling an LED with a call to WrPortE() or
BitWrPortE() gives you the information you need without as much affect on timing.
The sample program \Samples\LP3500\power.c demonstrates how to use LEDs to communicate
information.
Environment Options,
Debug Windows tab
Inspect Menu
Project Options, Project Options,
Execution Trace Programatically with
Debugger tab Debugger tab
macros
Right-click menu in the
Trace window
Symbolic Stack Environment Options, Project Options,
Windows Menu
Trace Debug Windows tab Debugger tab
Software Project Options, Project Options,
Run Menu
Breakpoints Debugger tab Debugger tab
In “Add | Edit Hardware
Run menu’s “Add/Edit
Hardware “Add | Edit Hardware breakpoint” dialog,
Hardware Breakpoints”
Breakpoints breakpoint” dialog change check box, then
option
click “Update” button
Single Stepping No configuration options Always enabled Run Menu
Instruction Level Project Options,
No configuration options Run Menu
Single Stepping Debugger tab
Environment Options,
Debug Windows tab Project Options,
Watch Expressions Inspect Menu
Project Options, Debugger tab
Debugger tab
Evaluate This feature is enabled when
No configuration options Inspect Menu
Expression Watch Expressions is enabled.
Automatically generated
Map File No configuration options Always enabled
for compiled programs
Environment Options,
Memory Dump Always enabled Inspect Menu
Debug Windows tab
Environment Options,
Disassemble Code Always enabled Inspect Menu
Debug Windows tab
Assert Macro Programatically Programatically Programatically
printf() Programatically Programatically Programatically
i. For an account of what can happen when time and money constraints all but disappear, read
“They Write the Right Stuff” by Charles Fishman.
- Use consistent indenting. This increases readability of the code. Look in the Editor tab in the
Environment Options dialog to turn on a feature that makes this automatic.
- Use block comments (/*...*/) only for multiple line comments on the global level and line
comments (//) inside functions, unless you really need to insert a long, multiple line comment.
The reason for this is it is difficult to temporarily comment out sections of code using /*...*/
when debugging if the section being commented out has block comments, since block com-
ments are not nestable.
- Use Dynamic C code templates to minimize syntax errors and some typos. Look in the Code
Templates tab in the Environment Options dialog to modify existing templates or create you
own. Right click in an editor window and select Insert Code Template from the popup menu.
This will bring up a scroll box containing all the available templates from which to choose.
• Syntax Highlighting: Many syntactic elements are visually enhanced with color or other text attributes
by default. These elements are user-configurable from the Syntax Colors tab of the Environment
Options dialog. This is more than mere lipstick. The visual representation of material can aid in or
detract from understanding it, especially when the material is complex.
• Revision Control System: If your company has a code revision control systems in place, use it. In addi-
tion, when in development or testing stages, keep a known good copy of your program close at hand.
That is, a compiles-and-runs-without-crashing copy of your program. Then if you make changes,
improvements or whatever and then can’t compile, you can go back to the known good copy.
One of the difficulties of debugging is that the source of a bug and its effect may not appear closely related
in the code. For example, if an array goes out of bounds and corrupts memory, it may not be a problem
until much later when the corrupted memory is accessed.
Another good online source of information and help is the Rabbit bulletin board. Go to:
www.rabbit.com/support/bb/
If you are having trouble figuring out what is happening, remember to analyze the bug under various con-
ditions. For example, run the program without the programming cable attached. Change the baud rate.
Change the processor speed. Do bug symptoms change? If they do, you have more clues.
At the time of this writing the following links provided some good information:
• http://www.embeddedstar.com/technicalpapers/content/d/embedded1494.html
• “They Write the Right Stuff” by Charles Fishman
http://www.fastcompany.com/magazine/06/writestuff.html
Initialization Services
• Call _GLOBAL_INIT()
• Initialize the global timer variables
• Start the Virtual Driver periodic interrupt
#define N 10000
main(){ timeit(); }
nodebug timeit(){
unsigned long int T0;
float T2,T1;
int x,y;
int i;
T0 = MS_TIMER;
for(i=0;i<N;i++) { }
T0 = MS_TIMER;
for(i=0;i<N;i++){ x+y;}
// subtract empty loop time and convert to time for single pass
T2=(T2-T1)/(float)N;
If “MS_TIMER + delay” overflows, this returns immediately. The correct way to code the delay loop so
that an overflow of MS_TIMER does not break it, is this:
endtime = MS_TIMER + delay;
while ((long)MS_TIMER - endtime < 0) {
//do something
}
The interval defined by the subtraction is always correct. This is true because the value of the interval is
based on the values of MS_TIMER and “endtime” relative to one another, so the actual value of these vari-
able does not matter.
One way to conceptualize why the second code snippet is always correct is to consider a number circle like
the one in Figure 7.1. In this example, delay=5. Notice that the value chosen for MS_TIMER will “roll
over” but that it is only when MS_TIMER equals or is greater than “endtime” that the while loop will eval-
uate to false.
Figure 7.1 “delay=5”
MS_TIMER
15 0 1
14 2 endtime
13 3
12 4
11 5
10 6
9 7
8
Another important point to consider is that the interval is cast to a signed number, which means that any
number with the high bit set is negative. This is necessary in order for the interval to be less than zero
when MS_TIMER is a large number.
However, it is recommended that the watchdog not be disabled. The watchdog prevents the target from
entering an endless loop in software due to coding errors or hardware problems. If the Virtual Driver is not
used, the user code should periodically call hitwd().
When debugging a program, if the program is stopped at a breakpoint because the breakpoint was explic-
itly set, or because the user is single stepping, then the debug kernel hits the hardware watchdog periodi-
cally.
i. Starting with the Rabbit 3000A, Rabbit microprocessors have secondary hardware watchdog timers. See
the user’s manual for your Rabbit processor for details, e.g., the Rabbit 3000 Microprocessor User’s
Manual.
Calling “exit(-800)” in an application or library will cause Dynamic C to report “My own run-time error
message” in a message box.
Please see Section 8.2 for information on replacing the default error handler with a custom one.
This function sets the BIOS function pointer for run-time errors to the one passed to it.
When a run-time error occurs, exception() pushes onto the stack the information detailed in the table
below.
.
Table 8-3. Stack setup for run-time errors
Then exception() calls the installed error handler. If the error handler passes the run-time error to
Dynamic C (i.e. it is a fatal error and the system needs to be halted or reset), then registers must be loaded
appropriately before calling the _xexit function.
Dynamic C expects the following values to be loaded:
The size of the error log buffer is determined by the number of entries, the size of an entry, and the header
information at the beginning of the buffer. The number of entries is determined by the macro
ERRLOG_NUM_ENTRIES (default is 78). The size of each entry is dependent on the settings of the global
options for stack dump, register dump and error message. The default size of the buffer is about 4K in
extended RAM.
“Deployment” is defined as the first power up without the programming cable attached. Reprogramming
the board using the programming cable, the RFU, or a RabbitLink board and starting the program again
without the programming cable attached is a new deployment.
ENABLE_ERROR_LOGGING
Default: 0. Disables error logging. Changing this to “1” enables error logging.
ERRLOG_USE_REG_DUMP
Default: 1. Include a register dump in log entries. Changing this to zero excludes the register
dump in log entries.
ERRLOG_STACKDUMP_SIZE
Default: 16. Include a stack dump of size ERRLOG_STACKDUMP_SIZE in log entries. Chang-
ing this to zero excludes the stack dump in log entries.
ERRLOG_NUM_ENTRIES
Default: 78. This is the number of entries allowed in the log buffer.
ERRLOG_USE_MESSAGE
Default: 0. Exclude error messages from log entries. Changing this to “1” includes 8 byte
error messages in log entries The default error handler makes no use of this feature.
ResetErrorLog Resets the exception and restart type counts in the error
log buffer header.
and
samples\ErrorHandling\Display_errorlog.c
Additional xmem,
unused by Root
0xFFFF
Xmem Segment
0xBE000
0xE000
Root Data
Stack Segment Interrupt Vectors, RAM
0xD000 0xD000 Watch Code
External Interrupt 0xB1000
Vectors 0xCC00 0xA9000
0xCF00
Stack
Data Segment 0xA8000
(Root Data)
Internal Interrupt UNUSED
0x80000
Vectors
0xCE00
Xmem Code
(from Xmem Segment)
Flash
Watch Code Memory
Base Segment
(Root Code)
Root Code
0xCC00 0x0000 0x00000
Logical Address Space Physical Address Space
Figure 9.1 illustrates how the logical address space is divided and where code resides in physical memory.
Both the static RAM and the flash memory are 128K in the diagram. Physical memory starts at address
0x00000 and flash memory is usually mapped to the same address. SRAM typically begins at address
0x80000.
10.1 FS2
The Dynamic C file system, known as the filesystem mk II or simply as FS2, was designed to be used with
a second flash memory or in SRAM on Rabbit 2000- or 3000-based boards.
FS2 allows:
• the ability to overwrite parts of a file
• the simultaneous use of multiple device types
• the ability to partition devices
• efficient support for byte-writable devices
• better performance tuning
• a high degree of backwards compatibility with its predecessor
• all necessary run-time data to be reconstructed on power up
NOTE: Dynamic C’s low-level flash memory access functions should not be used in the
same area of the flash where the flash file system exists.
FS2 supports a total of 255 files, but storing a large number of small files is not recommended. It is much
more efficient to have a few large ones.
For the simplest applications, this is all that is necessary for configuration. For more complex applications,
there are several other macro definitions that may be used before the inclusion of FS2.LIB. These are:
#define FS_MAX_DEVICES 3
#define FS_MAX_LX 4
#define FS_MAX_FILES 10
These specify certain static array sizes that allow control over the amount of root data space taken by FS2.
If you are using only one flash device (and possibly battery-backed RAM), and are not using partitions,
then there is no need to set FS_MAX_DEVICES or FS_MAX_LX.
For more information on partitioning, please see Section 10.1.4 “Setting up and Partitioning the File Sys-
tem”.
FS_MAX_DEVICES
This macro defines the maximum physical media. If it is not defined in the program code,
FS_MAX_DEVICES will default to 1, 2, or 3, depending on the values of
FS2_USE_PROGRAM_FLASH, XMEM_RESERVE_SIZE and FS2_RAM_RESERVE.
FS_MAX_LX
This macro defines the maximum logical extents. You must increase this value by 1 for each new partition
your application creates. It this is not defined in the program code it will default to FS_MAX_DEVICES.
For a description of logical extents please see Section 10.1.4.2.
FS_MAX_FILES
This macro is used to specify the maximum number of files allowed to coexist in the entire file system.
Most applications will have a fixed number of files defined, so this parameter can be set to that number to
avoid wasting root data memory. The default is 6 files. The maximum value for this parameter is 255.
FS2_DISALLOW_GENERIC_FLASH
This macro is used to prevent FS2 from mistakenly attempting to recover a nonexistent file system on the
“generic” (second) flash, or to prevent RAM corruption caused by _GetFlashID() when flash is not
mapped into memory at all.
FS2_DISALLOW_PROGRAM_FLASH
This macro is used to prevent FS2 from mistakenly attempting to recover a nonexistent file system on the
“program” (first) flash, or to prevent RAM corruption caused by _GetFlashID() when flash is not
mapped into memory at all.
FS2_RAM_RESERVE
This macro determines the amount of space used for FS2 in RAM. If some battery-backed RAM is to be
used by FS2, then this macro must be modified to specify the amount of RAM to reserve. The memory is
reserved near the top of RAM. Note that this RAM will be reserved whether or not the application actually
uses FS2.
Prior to Dynamic C 7.06 this macro was defined as the number of bytes to reserve and had to be a multiple
of 4096. It is now defined as the number of blocks to reserve, with each block being 4096 bytes.
This macro is defined in the BIOS prior to Dynamic C version 9.30 and in memconfig.lib thereafter.
FS2_SHIFT_DOESNT_UPDATE_FPOS
If this macro is defined before the #use fs2.lib statement in an application, multiple file descriptors
can be opened, but their current position will not be updated if fshift() is used.
XMEM_RESERVE_SIZE
This macro is the number of bytes (which must be a multiple of 4096) reserved in the first flash for use by
FS2 and possibly other customer-defined purposes. This is defined as 0x0000. Memory set aside with
XMEM_RESERVE_SIZE will NOT be available for xmem code.
This macro is defined in the BIOS prior to Dynamic C version 9.30 and in memconfig.lib thereafter.
flash1 = fs_get_other_lx();
if (flash1) {
fs_set_lx(flash1, flash1);
fcreate(&f, 10);
. . .
}
To obtain the logical extent number for a one flash board, fs_get_flash_lx() must be called instead
of fs_get_other_lx().
i. For a description of logical extents please see Section 10.1.4.2, “Logical Extents (LX),” on
page 128.
Command Description
Further Partitioning
The initial default logical extents can be divided further. This must be done before calling fs_init().
The function to create sub-partitions is called fs_setup(). This function takes an existing LX number,
divides that LX according to the given parameters, and returns a newly created LX number. The original
partition still exists, but is smaller because of the division. For example, in a system with LX#1 as a flash
device of 256K and LX#2 as 4K of RAM, an initial call to fs_setup() might be made to partition
LX#1 into two equal sized extents of 128K each. LX#1 would then be 128K (the first half of the flash) and
LX#3 would be 128K (the other half). LX#2 is untouched.
Having partitioned once, fs_setup() may be called again to perform further subdivision. This may be
done on any of the original or new extents. Each call to fs_setup() in partitioning mode increases the
total number of logical extents. You will need to make sure that FS_MAX_LX is defined to a high enough
value that the LX array size is not exceeded.
While developing an application, you might need to adjust partitioning parameters. If any parameter is
changed, FS2 will probably not recognize data written using the previous parameters. This problem is
common to most operating systems. The “solution” is to save any desired files to outside the file system
before changing its organization; then after the change, force a format of the file system.
The low-order byte specifies the file number and the high-order byte specifies the LX number of the meta-
data (1 through number of LXs). If the high-order byte is zero, then a suitable “default” LX will be located
by the file system. The default LX will default to 1, but will be settable via a #define, for file creation.
For existing files, a high-order byte of zero will cause the file system to search for the LX that contains the
file. This will require no or minimal changes to existing customer code.
Only the metadata LX may be specified in the file number. This is called a “fully-qualified” file number
(FQFN). The LX number always applies to the file metadata. The data can reside on a different LX, how-
ever this is always determined by FS2 once the file has been created.
Command Description
Associate a name with the flash file system file number. The return
sspec_addfsfile value is an index into an array of structures associated with the
named files.
Read a file represented by the return value of
sspec_readfile
sspec_addfsfile into a buffer.
sspec_getlength Get the length (number of bytes) of the file.
Get the file system file number (1- 255). Cast return value to
sspec_getfileloc
FILENUMBER.
Find the index into the array of structures associated with named
sspec_findname
files of the file that has the specified name.
Get file type. For flash file system files this value will be
sspec_getfiletype
SSPEC_FSFILE.
Find the next named file in the flash file system, at or following the
sspec_findnextfile
specified index, and return the index of the file.
sspec_remove Remove the file name association.
Saves to the flash file system the array of structures that reference
sspec_save
the named files in the flash file system.
Restores the array of structures that reference the named files in the
sspec_restore
flash file system.
#use "FS2.LIB"
#define TESTFILE 1
main()
{
File file;
static char buffer[256];
fs_init(0, 0);
fseek(&file, 0, SEEK_END);
fwrite(&file,"hello",6);
fwrite(&file,"12345",6);
fwrite(&file,"67890",6);
fseek(&file, 0, SEEK_SET);
while(fread(&file,buffer,6)>0) {
printf("%s\n",buffer);
}
fclose(&file);
For a more robust program, more error checking should be included. See the sample programs in the
Samples\FILESYSTEM folder for more complex examples, including error checking, formatting, parti-
tioning and other new features.
www.rabbit.com/support/downloads/
The FAT library can be used in either blocking or non-blocking mode and supports both FAT12 and FAT16.
(See Section 10.2.5.3.1 for more information on these FAT types.)
Let’s define some terms before continuing.
• A device is a single physical hardware item such as a hard drive, a serial flash or a NAND flash. E.g.,
one serial flash is a single device. The device, in turn, can host one to four partitions.
• A partition is a range of logical sectors on a device. A real-world example of a partition is what is com-
monly known as the C drive on a PC.
• A driver is the software interface that handles the hardware-specific aspects of any communication to
or from the device.
• Blocking is a term that describes a function’s behavior in regards to completion of the requested task. A
blocking function will not return until it has completely finished its task. In contrast, a non-blocking
function will return to its calling function before the task is finished if it is waiting for something. A
non-blocking function can return a code that indicates it is not finished and should be called again.
Used in conjunction with cooperative multitasking, non-blocking functions allow other processes to
proceed while waiting for hardware resources to finish or become available.
i. We use the terms directory and subdirectory somewhat interchangeably. The exception is the
root directory—it is never called a subdirectory. Any directory below the root directory may be
referred to as a directory or a subdirectory.
NOTE: All error codes returned from the Dynamic C FAT file system are defined in
LIB/.../FILESYSTEM/ERRNO.LIB.
int main(){
int i;
int rc; // Check return codes from FAT API
long prealloc; // Used if the file needs to be created.
fat_part *first_part; // Use the first mounted FAT partition.
rc = fat_AutoMount( FDDF_USE_DEFAULT );
first_part = NULL;
for(i=0;i < num_fat_devices * FAT_MAX_PARTITIONS; ++i)
{ // Find the first mounted partition
if ((first_part = fat_part_mounted[i]) != NULL) {
break; // Found mounted partition, so use it
}
}
if (first_part == NULL) { // Check if mounted partition was found
rc = (rc < 0) ? rc : -ENOPART; // None found, set rc to a FAT error code
} else{
printf("fat_AutoMount() succeeded with return code %d.\n", rc);
rc = 0; // Found partition; ignore error, if any
}
if (rc < 0){ // negative values indicate error
if (rc == -EUNFORMAT)
printf("Device not Formatted, Please run Fmt_Device.c\n");
else
printf("fat_AutoMount() failed with return code %d.\n", rc);
exit(1);
} // OK, file system exists and is ready to access. Let's create a file.
Defining the macro _DRIVER_CUSTOM notifies fat_config.lib that a custom driver or hardware
configuration is being used. For more information on how this works, see Section 10.2.5
Next some static variables are declared: a file structure to be used as a handle to the file that will be created
and a buffer that will be used for reading and writing the file.
Now we are in main(). First there are some variable declarations: the integer rc is for the code returned
by the FAT API functions. This code should always be checked, and must be checked if the non-blocking
mode of the FAT is used. The descriptions for each function list possible return codes.
The variable prealloc stores the number of bytes to reserve on the device for use by a specific file.
These clusters are attached to the file and are not available for use by any other files. This has some advan-
tages and disadvantages. The obvious disadvantage is that it uses up space on the device. Some advantages
are that having space reserved means that a log file, for instance, could have a portion of the drive set aside
for its use only. Another advantage is that if you are transferring a known amount of information to a file,
pre-allocation not only sets aside the space so you know you will not get half way through and run out, but
it also makes the writing process a little faster as the allocation of clusters has already been dealt with so
there is no need to spend time getting another cluster.
This feature should be used with care as pre-allocated clusters do not show up on directory listings until
data is actually written to them, even though they have locked up space on the device. The only way to get
unused pre-allocated clusters back is to delete the file to which they are attached, or use the
fat_truncate() or fat_split() functions to trim or split the file. In the case of fat_split(),
the pre-allocated space is not freed, but rather attached to the new file created in the split.
fat_part *first_part;
This pointer will be used as a handle to an active partition. (The fat_part structure and other data struc-
tures needed by the FAT file system are discussed in fat_AutoMount().) The partition pointer will be
passed to API functions, such as fat_open().
Now a call is made to fat_AutoMount(). This function was introduced in FAT version 2.01 as a
replacement for fat_Init(). Whereas fat_Init() can do all the things necessary to ready the first
partition on the first device for use, it is limited to that. The function fat_AutoMount() is more flexi-
ble because it uses data from the configuration file fat_config.lib to identify FAT partitions and to
optionally ready them for use, depending on the flags parameter that is passed to it. The flags parameter is
described in the function description for fat_AutoMount().
For this sample program, we are interested in the first usable FAT partition. The for loop after the call to
fat_AutoMount() finds the partition, if one is available.
The for loop allows us to check every possible partition by using num_fat_devices, which is the
number of configured devices, and then multiplying the configured devices by the maximum number of
allowable partitions on a device, which is four. The for loop also makes use of fat_part_mounted,
an array of pointers to partition structures that is populated by the fat_autoMount() call.
Next, the sample program writes the data "Hello, world!\r\n" to the file.
fat_Write( &my_file, "Hello, world!\r\n", 15 );
The parameters are as follows:
• &my_file is a pointer to the file handle opened by fat_Open().
• “Hello, world!\r\n” is the data written to the file. Note that \r\n (carriage return, line feed)
appears at the end of the string in the call. This is essentially a FAT (or really, DOS) convention for text
files. It is good practice to use the standard line-end conventions. (If you just use \n, the file will read
just fine on Unix systems, but some DOS-based programs may have difficulties.)
• 15 is the number of characters to write. Be sure to select this number with care since a value that is too
small will result in your data being truncated, and a value that is too large will append any data that
already exists beyond your new data.
The parameter &my_file is a handle to the file to be closed. Remember to check for any return code
from fat_Close() since an error return code may indicate the loss of data.
The file must be opened for any further work, even though &my_file may still reference the desired file.
The file must be open to be active, so we call fat_Open() again. Now the file can be read.
rc = fat_Read( &my_file, buf, sizeof(buf));
The function fat_Read() returns the number of characters actually read. The parameters are as follows:
• &my_file is a handle to the file to be read.
• buf is a buffer for reading/writing the file that was defined at the beginning of the program.
• sizeof(buf) is the number of bytes to be read into buf. It does not have to be the full size of the
buffer
Characters are read beginning at the current position of the file. (The file position can be changed with the
fat_Seek() function.) If the file contains fewer than sizeof(buf) characters from the current posi-
tion to the end-of-file marker (EOF), the transfer will stop at the EOF. If the file position is already at EOF,
0 is returned. The maximum number of characters read is 32767 bytes per call.
The file can now be closed. Call fat_UnmountDevice()i rather than simply calling fat_Close() to
ensure that any data stored in cache will be written to the device. With a write-back cache, writes are
delayed until either:
• all cache buffers are full and a new FAT read request requires a “dirty” cache buffer to be written out
before the read can take place, or
• cache buffers for a partition or a device are being flushed due to an unmount call or explicit flush call.
Calling fat_UnmountDevice() will close all open files and unmount all mounted FAT partitions. This
is the safest way to shut down a device. The parameter first_part->dev is a handle to the device to
be unmounted.
fat_UnmountDevice( first_part->dev );
NOTE: A removable device must be unmounted in order to flush its data before removal.
Failure to unmount any partition on a device that has been written to could corrupt the file
system.
You can type “h” and press enter at any time to display the FAT shell commands.
In the following examples the commands that you enter are shown in boldface type. The response from the
shell program is shown in regular typeface.
This shows the HELLO.TXT file that was created using the FAT_CREATE.C sample program. The file
length is 15 bytes. Cluster 2 has been allocated for this file. The “ls” command will display up to the first
six clusters allocated to a file.
The flag, rhsvdA, displays the file or directory attributes, with upper case indicating that the attribute is
turned on and lower case indicating that the attribute is turned off. In this example, the archive bit is turned
on and all other attributes are turned off.
These are the six attributes:
r - read-only v - volume label
h - hidden file d - directory
s - system a - archive
This shows that DIR1 was created, and is 1024 bytes (size may vary by flash type).
Now, select DIR1:
> cd dir1
PWD = '/dir1'
>
Note that the file name was appended to the current directory. Now we can write to RABBIT.TXT. The
shell program has predetermined characters to write, and does not allow you to enter your own data.
> wr rabbit.txt
File '/dir1/rabbit.txt' written with 1024 bytes out of 1024
>
Each line is an entry into a file that is stored in the FAT file system. The file is appended once every second
and read and displayed once every ten seconds. In addition to the file system use and the screen output, if
you are using an RCM3300, RCM3700 or PowerCore FLEX development board, the application blinks
the LED on your board.
The code preceding main() brings in the required library and declares the file structure. And, as expected,
there is no #define for the macro FAT_BLOCK. At the start of main() some system variable are cre-
ated and initialized. This is followed by the code to bring up the FAT file system, which is similar to what
we examined in Section 10.2.2.1 when looking at fat_create.c, with two essential differences. One,
since we have initialized the FAT to be in non-blocking and we are making some calls to FAT functions
that must return before we can continue, we must wait for the return.
file.state = 0;
This is required before opening a file when using non-blocking mode in order to indicate that the file is not
in use. Only do this once. After you have opened the file, do not alter the contents of the file structure.
If fat_Open() succeeds we can go into the non-blocking section of the program: three costatements
inside an endless while loop. The benefit of using the non-blocking mode of the FAT file system is real-
ized when using costatements, an extension of Dynamic C that implements cooperative multitasking.
Instead of waiting while a function finishes its execution, the application can accomplish other tasks.
10.2.3.2.1 Costatement that Writes a File
The first costate is named putdata. It waits for one second and then creates a string to timestamp the
entry of a randomly generated number that is then appended to a file.
while (1){
costate putdata always_on
{
waitfor (DelaySec(1)); // Wait for one second to elapse
Note that the always_on keyword is used. This is required when using a named costatement to force it
to execute every time it is encountered in the execution thread (unless it is made inactive by a call to
CoPause()).
It is easy to suspend execution within a costate by using the waitfor keyword. The costate will relin-
quish control if the argument to waitfor (in this case a call to DelaySec()) evaluates to FALSE. The
next time the execution thread reaches putdata, waitfor will be called again. This will go on until
DelaySec() returns TRUE, i.e., when one second has elapsed from the time DelaySec() was first
called from within waitfor.
After the one second delay, the string to write to the file is placed in a buffer and a looping variable and
position pointer are initialized.
Before the buffer contents can be written to a file in the FAT file system, we must ensure that no collisions
occur since there is another costate that will attempt to read the file every ten seconds. A file can not be
read from and written to at the same time. In the following code the waitfor keyword is used with the
global variable filestate (defined at the top of the application) to implement a locking mechanism. As
soon as the file becomes available for putdata, it is marked unavailable for showdata.
The next block of code appends the latest entry into the file that was opened at the start of the application.
Again, waitfor is used to voluntarily relinquish control, this time while waiting for the write function to
complete. If an error occurs during the write operation the device is unmounted and the application exits.
Otherwise the loop counter and the buffer position pointer are advanced by the number of bytes actually
written. Since this can be less than the requested number of bytes, it is best to check in a loop such as the
while loop shown in putdata.
The last action taken by putdata is to reset filestate, indicating that the open file is available.
10.2.3.2.2 Costatement that Reads and Displays a File
The costatement named showdata waits for ten seconds. Then it waits for the open file to be available,
and when it is, immediately marks it as unavailable.
The next statement modifies the internal file position pointer. The first time this costate runs, readto is
zero, meaning the position pointer is at the first byte of the file. The variable readto is incremented
every time a record is read from the file, allowing showdata to always know where to seek to next.
The rest of showdata is a while loop inside of a while loop. The inner while loop is where each
record is read from the file into the buffer and then displayed in the Stdio window with the printf()
call. Since fat_Read() may return less than the requested number of bytes, the while loop is needed
to make sure that the function will be called repeatedly until all bytes have been read. When the full record
has been read, it will then be displayed to the Stdio window.
while (filestate){
icount = 0;
iptr = ibuf;
while (icount < REC_LEN) {
waitfor((rc = fat_Read(&file, iptr, REC_LEN-icount)) != -EBUSY);
if (rc < 0)
{
if (rc == -EEOF)
{
filestate = 0;
break;
}
printf("fat_Read: rc = %d\n",rc);
while ((rc=fat_UnmountDevice(first_part->dev)) == -EBUSY);
return rc;
}
iptr += rc;
icount += rc;
} // end of inner while loop
if (filestate)
{
printf("%s", ibuf);
readto += REC_LEN;
}
} // end of outer while loop
The other costatement in the endless while loop is the one that blinks the LED. It illustrates that while
using the file system in non-blocking mode, there is still plenty of time for other tasks.
The first parameter, my_part, points to a partition structure. This pointer must point to a mounted parti-
tion. Some of the sample programs, like fat_create.c, declare a local pointer and then search for a
partition pointer in the global array fat_part_mounted[]. Other sample programs, like
fat_shell.c, define an integer to be used as an index into fat_part_mounted[]. Both methods
accomplish the same goal of gaining access to a partition pointer.
The second parameter contains the file name, including the directory (if applicable) relative to the root
directory. All paths in Dynamic C must specify the full directory path explicitly, e.g., DIR1\\FILE.EXT
or DIR1/FILE.EXT. The direction of the slash in the pathname is a backslash by default. If you use the
default backslash for the path separator, you must always precede it with another backslash, as shown in
the above call to fat_Open(). This is because the backslash is an escape character in a Dynamic C
string. To use the forward slash as the path separator, define the macro FAT_USE_FORWARDSLASH in
your application (or in FAT.LIB to make it the system default).
The third parameter determines whether a file or directory is opened (FAT_FILE or FAT_DIR).
The fourth parameter is a flag that limits fat_Open() to the action specified. FAT_CREATE creates the
file (or directory) if it does not exist. If the file does exist, it will be opened, and the position pointer will be
set to the start of the file. If you write to the file without moving the position pointer, you will overwrite
existing data. Use FAT_MUST_CREATE if you know the file does not exist; this last option is also a fail-
safe way to avoid opening and overwriting an existing file since an -EEXIST error message will be
returned if you attempt to create a file that already exists.
The fifth parameter, &my_file, is an available file handle. After a file or directory is opened, its handle is
used to identify it when using other API functions, so be wary of using local variables as your file handle.
The final parameter is an initial byte count if the object needs to be created. It is only used if the
FAT_CREATE or FAT_MUST_CREATE flag is used and the file or directory does not already exist. The
byte count is rounded up to the nearest whole number of clusters greater than or equal to 1. On return, the
variable prealloc is updated to the number of bytes allocated. Pre-allocation is used to set aside space
for a file, or to speed up writing a large amount of data as the space allocation is handled once.
Pass NULL as the final parameter to indicate that you are opening the file for reading or that a minimum
number of bytes needs to be allocated to the file at this time. If the file does not exist and you pass NULL,
the file will be created with the minimum one cluster allocation.
Once you are finished with the file, you must close it to release its handle so that it can be reused the next
time a file is created or opened.
Remember to check the return code from fat_Close() since an error return code may indicate the loss
of data. Once you are completely finished, call fat_UnmountDevice() to make sure any data stored
in the cache is written to the flash device.
10.2.4.2.2 Read and Write Operations
Use fat_Read() to read a file.
The first parameter, &my_file, is a pointer to the file handle already opened by fat_Open(). The
parameter buf points to a buffer for reading the file. The sizeof(buf) parameter is the number of
bytes to be read into the buffer. It does not have to be the full size of the buffer. If the file contains fewer
than sizeof(buf) characters from the current position to the end-of-file marker (EOF), the transfer will
stop at the EOF. If the file position is already at the EOF, 0 is returned. The maximum number of charac-
ters read is 32767 bytes per call.
The function returns the number of characters read or an error code. Characters are read beginning at the
current position of the file. If you have just written to the file that is being read, the file position pointer
will be where the write left off. If this is the end of the file and you want to read from the beginning of the
file you must change the file position pointer. This can be done by closing the file and reopening it, thus
moving the position pointer to the start of the file. Another way to change the position pointer is to use the
fat_Seek() function. This function is explained in Section 10.2.4.2.3.
Use fat_ReadDir() to read a directory. This function is explained in Section 10.2.4.2.5.
Use fat_Write() or fat_xWrite() to write to a file. The difference between the two functions is
that fat_xWrite() copies characters from a string stored in extended memory.
The first parameter, &my_file, is a pointer to the file handle already opened by fat_Open(). Because
fat_Open() sets the position pointer to the start of the file, you will overwrite any data already in the
file. You will need to call fat_Seek() if you want to start the write at a position other than the start of
the file (see Section 10.2.4.2.3).
The second parameter contains the data to write to the file. Note that \r\n (carriage return, line feed)
appear at the end of the string in the function. This is essentially a FAT (or really, DOS) convention for text
files. It is good practice to use these standard line-end conventions. (If you only use \n, the file will read
just fine on Unix systems, but some DOS-based programs may have difficulties.) The third parameter
specifies the number of characters to write. Select this number with care since a value that is too small will
result in your data being truncated, and a value that is too large will append any data that already exists
beyond your new data.
Remember that once you are finished with a file you must close it to release its handle. You can call the
fat_Close() function, or, if you are finished using the file system on a particular partition, call
fat_UnmountPartition(), which will close any open files and then unmount the partition. If you
are finished using the device, it is best to call fat_UnmountDevice(), which will close any open FAT
fat_Tell(&my_file, &pos);
fat_Seek(&my_file, pos, SEEK_SET);
The fat_Tell() function does not change the position pointer, but reads its value (which is the number
of bytes from the beginning of the file) into the variable pointed to by &pos. Zero indicates that the posi-
tion pointer is at the start of the file. The first parameter, &my_file, is the file handle already opened by
fat_Open().
The fat_Seek() function changes the position pointer. Clusters are allocated to the file if necessary, but
the position pointer will not go beyond the original end of file (EOF) unless doing a SEEK_RAW. In all
other cases, extending the pointer past the original EOF will preallocate the space that would be needed to
position the pointer as requested, but the pointer will be left at the original EOF and the file length will not
be changed. If this occurs, the error code -EEOF is returned to indicate the space was allocated but the
pointer was left at the EOF. If the position requires allocating more space than is available on the device,
the error code -ENOSPC is returned.
The first parameter passed to fat_Seek() is the file handle that was passed to fat_Open(). The sec-
ond parameter, pos, is a long integer that may be positive or negative. It is interpreted according to the
value of the third parameter. The third parameter must be one of the following:
• SEEK_SET - pos is the byte position to seek, where 0 is the first byte of the file. If pos is less than 0,
the position pointer is set to 0 and no error code is returned. If pos is greater than the length of the file,
the position pointer is set to EOF and error code -EEOF is returned.
• SEEK_CUR - seek pos bytes from the current position. If pos is less than 0 the seek is towards the
start of the file. If this goes past the start of the file, the position pointer is set to 0 and no error code is
returned. If pos is greater than 0 the seek is towards EOF. If this goes past EOF the position pointer is
set to EOF and error code -EEOF is returned.
• SEEK_END - seek to pos bytes from the end of the file. That is, for a file that is x bytes long, the state-
ment:
fat_Seek (&my_file, -1, SEEK_END);
will cause the position pointer to be set at x-1 no matter its value prior to the seek call. If the value of
pos would move the position pointer past the start of the file, the position pointer is set to 0 (the start of
the file) and no error code is returned. If pos is greater than or equal to 0, the position pointer is set to
EOF and error code -EEOF is returned.
• SEEK_RAW - is similar to SEEK_SET, but if pos goes beyond EOF, using SEEK_RAW will set the file
length and the position pointer to pos. This adds whatever data exists on the allocated space onto the
end of the file..
rc = fat_CreateDir(my_part, "DIR1");
The first parameter, my_part, points to a partition structure. This pointer must point to a mounted parti-
tion. Some of the sample programs, like fat_create.c, declare a local pointer and then search for a
partition pointer in the global array fat_part_mounted[]. Other sample programs, like
fat_shell.c, define an integer to be used as an index into fat_part_mounted[]. Both methods
accomplish the same goal of gaining access to a partition pointer.
The second parameter contains the directory or subdirectory name relative to the root directory. If you are
creating a subdirectory, the parent directory must already exist.
Once DIR1 is created as the parent directory, a subdirectory may be created, and so on.
rc = fat_CreateDir(my_part, "DIR1/SUBDIR");
Note that a forward slash is used in the pathname instead of a backslash. Either convention may be used.
The backslash is used by default. To use a forward slash instead, define FAT_USE_FORWARDSLASH in
your application or in FAT.LIB.
A file can be created using the fat_CreateFile() function. All directories in the path must already
exist.
The first parameter, my_part, points to the static partition structure set up by fat_AutoMount().
The second parameter contains the file name, including the directories (if applicable) relative to the root
directory. All paths in the FAT library are specified relative to the root directory.
The third parameter indicates the initial number of bytes to pre-allocate. At least one cluster will be allo-
cated. If there is not enough space beyond the first cluster for the requested allocation amount, the file will
be allocated with whatever space is available on the partition, but no error code will be returned. If no clus-
ters can be allocated, the -ENOSPC error code will return. Use NULL to indicate that no bytes need to be
allocated for the file at this time. Remember that pre-allocating more than the minimum number of bytes
necessary for storage will reduce the available space on the device.
The final parameter, &my_file, is a file handle that points to an available file structure. If NULL is
entered, the file will be closed after it is created.
The first parameter specifies the directory; &dir is an open file handle. A directory is opened by a call to
fat_OpenDir() or by passing FAT_DIR in a call to fat_Open(). The second parameter, &dirent,
is a pointer to a directory entry structure to fill in. The directory entry structure must be declared in your
application, for example:
fat_dirent dirent;
Search Conditions
The last parameter, mode, determines which directory entry is being requested, a choice that is built from
a combination of the macros described below. To understand the possible values for mode, the first thing
to know is that a directory entry can be in one of three states: empty, active or deleted. This means you
must choose one of the default flags described below, or one or more of the following macros:
• FAT_INC_ACTIVE - include active entries. This is the default setting if other FAT_INC_* macros
are not specified; i.e., active files are included unless FAT_INC_DELETED, FAT_INC_EMPTY, or
FAT_INC_LNAME is set.
• FAT_INC_DELETED - include deleted entries
• FAT_INC_EMPTY - include empty entries
• FAT_INC_LNAME - include long name entries (this is included for completeness, but is not used since
long file names are not supported)
The above macros narrow the search to only those directory entries in the requested state. The search is
then refined further by identifying particular attributes of the requested entry. This is done by choosing one
or more of the following macros:
• FATATTR_READ_ONLY - include read-only entries
• FATATTR_HIDDEN - include hidden entries
• FATATTR_SYSTEM - include system entries
• FATATTR_VOLUME_ID - include label entries
• FATATTR_DIRECTORY - include directory entries
• FATATTR_ARCHIVE - include modified entries
Including a FATATTR_* macro means you do not care whether the corresponding attribute is turned on or
off. Not including a FATATTR_* macro means you only want an entry with that particular attribute turned
off. Note that the FAT system sets the archive bit on all new files as well as those written to, so including
FATATTR_ARCHIVE in your mode setting is a good idea.
For example, if mode is (FAT_INC_ACTIVE) then the next directory entry that has all of its attributes
turned off will be selected; i.e., an entry that is not read only, not hidden, not a system file, not a directory
or a label, and not archived. In other words, the next writable file that is not hidden, system or already
archived is selected.
NOTE: If you have FAT version 2.05 or earlier, you do not have access to the
FAT_FIL_* macros.
The first parameter, my_part, points to the static partition structure that was populated by
fat_AutoMount(). The second parameter is the file type, FAT_FILE or FAT_DIR, depending on
whether a file or a directory is to be deleted. The third parameter contains the file name, including the
directory (if applicable) relative to the directory root. All paths in the FAT library are specified relative to
the root directory.
Number of Sectors
RabbitCore Model Flash Device
per Cluster
The cluster size for a NAND device corresponds to its page size. Note that a file or directory takes at mini-
mum one cluster. On a NAND device the page size is 16K bytes; therefore, while it is allowable to write
very small files to the FAT file system on a NAND device, it is not space efficient. Even the smallest file
takes at least 16,000 bytes of storage. Cluster sizes for SD cards vary with the size of the card inserted. To
determine the number of sectors per cluster on an SD card, divide the size of the card by 32MB.
0x1BE
Partition 0
Partition Table
0x1CE
Partition 1
0x1DE
Partition 2
0x1EE
Partition 3
0x1FE
Signature
NOTE: Some devices are formatted without an MBR and, therefore, have no partition
table. This configuration is not currently supported in the Dynamic C FAT file system.
Partition 0 Partition 1
BPB BPB
MBR FAT Data Area FAT Data Area
ROOT ROOT
10.2.5.3.1 BPB
The fields of the BPB contain information describing the partition:
• the number of bytes per sector
• the number of sectors per cluster (see Table 13)
• the total count of sectors on the partition
• the number of root directory entries
• plus additional information not mentioned here
The memset() function is used to initialize the entry to zero. The values for starthead and endhead
should be 0xFE to indicate that the media uses LBA (Logical Block Addressing) instead of head and cylin-
der addressing. The FAT library uses LBA internally. The values for the startsector, partsecsize
and parttype fields determine where the partition starts, how many sectors it contains and what parti-
tion type it is. The number of sectors in the partition is calculated by dividing the number of raw bytes in
the partition by the sector size of the flash. The number of raw bytes in the partition includes not only
bytes for file storage, but also the space needed by the BPB and the root directory. One is added to dev-
>partsecsize to ensure an extra sector is assigned if MY_PARTITION_SIZE is not evenly divisible
by the size of a flash sector. The partition type (.parttype) is determined by the partition size: 1 indi-
cates FAT12 and 6 indicates FAT16. Fill in an mbr_part structure for each partition you are creating.
The remaining entries should be zeroed out.
When laying out partitions, there are three basic checks to make sure the partitions fit in the available
device space and do not overlap.
1. No partition can start on a sector less than 1.
2. Each partition resides on sectors from startsector through startsector+partsecsize-1.
No other partition can have a startsector value within that range.
3. No partition ending sector (startsector+partsecsize-1) can be greater than or equal to the
total sectors on the device.
The partition boundaries are validated in the call to fat_FormatDevice() and the function will return
an error if any of the partition boundaries are invalid. If fat_FormatDevice() returns success, then
call fat_AutoMount() with flags of FDDF_COND_PART_FORMAT | FDDF_MOUNT_DEV_# |
FDDF_MOUNT_PART_ALL; where # is the device number for the device being partitioned. This will for-
mat and mount the newly created partitions.
$ % ' - _ @ ~ ` ! ( ) { } ^ # &
File names passed to the file system are always converted to upper case; the original case value is lost.
The maximum size of a directory is limited by the available space. It is recommended that no more than
ten layers of directories be used with the Dynamic C FAT file system.
See the function description for fat_InitUCOSMutex() for more details, and the sample program
Samples/FileSystem/FAT_UCOS.C for a demonstration of using FAT with µC/OS-II.
#define SF_SPI_DIVISOR 5
#define SF_SPI_INVERT_RX
10.2.5.10 References
There are a number of good references regarding FAT file systems available on the Internet. Any reason-
able search engine will bring up many hits if you type in relevant terms, such as “FAT,” “file system,” “file
allocation table,” or something along those lines. At the time of this writing, the following links provided
useful information.
1. This link is to Microsoft’s “FAT32 File System Specification,” which is also applicable to FAT12 and
FAT16.
www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
The keywords debug and nodebug can be placed on the same line as #asm. Assembly code blocks are
nodebug by default. This saves space and unnecessary calls to the debugger kernel.
All blocks of assembly code within a C function are assembled in nodebug mode. The only exception to
this is when a block of assembly code is explicitly marked with debug. Any blocks marked debug will
be assembled in debug mode even if the enclosing C function is marked nodebug.
#asm
InitValues::
c start_time = 0;
c counter = 256;
ret
#endasm
?: conditional
. dot
-> points to
* dereference
11.2.1 Comments
C-style comments are allowed in embedded assembly code. The assembler will ignore comments begin-
ning with:
; text from the semicolon to the end of line is ignored.
// text from the double forward slashes to the end of line is ignored.
/* text between slash-asterisk and asterisk-slash is ignored */
#asm
myrootconstants::
db 0x40, 0x41, 0x42
#endasm
the bytes are placed somewhere in the instruction space. If separate I&D space is disabled (the default con-
dition), the bytes are placed in the base segment (aka, root segment) interspersed with code.
Therefore, so that data will be treated as data when referenced in assembly code, the const keyword
must be used when separate I&D space is enabled. For example, this won't work correctly without const:
#asm const
label::
db 0x5a
#endasm
main(){
;
#asm
ld a,(label) // ld 0x5a to reg a
#endasm
}
The assembly language keyword dw defines 16-bit words, least significant byte first. The keyword dw
should be followed immediately by numerical values:
This example defines three constants. The first two constants are literals, and the third constant is the
address of variable xyz.
The numerical values initialize sequential word locations, starting at the current code address.
#define SAVEFLAG $\
ld a,b $\
push af $\
pop bc
#asm
...
ld b,0x32
SAVEFLAG
...
#endasm
11.2.4 Labels
A label is a name followed by one or two colons. A label followed by a single colon is local, whereas one
followed by two colons is global. A local label is not visible to the code out of the current embedded
assembly segment (i.e., code before the #asm or after the #endasm directive is outside of that embbeded
assembly segment).
Unless it is followed immediately by the assembly language keyword equ, the label identifies the current
code segment address. If the label is followed by equ, the label “equates” to the value of the expression
after the keyword equ.
Because C preprocessor macros are expanded in embedded assembly code, Rabbit recommends that pre-
processor macros be used instead of equ whenever possible.
Indicates the amount of stack space (in bytes) used for stack-based
@SP
variables. This does not include arguments.
Constant for the current code location. For example:
ld hl, @PC
@PC
loads the code address of the instruction. ld hl,@PC+3 loads the address
after the instruction since it is a 3 byte instruction.
Evaluates the offset from the frame reference point to the stack space
@RETVAL reserved for the struct function returns. See Section 11.4.1.2 for
more information on the frame reference point.
@LENGTH Determines the next reference address of a variable plus its size.
Just like in the first definition of structure “s”, the assembly expression s+x evaluates to 0; s+a evaluates to
2 and s+b evaluates to 2 (both expressions evaluate to the same value because both “a” and “b” are offset
“0” from “a”); and finally, s+c evaluates to 4 because s+a evaluates to 2 and a+c evaluates to 2.
#asm xmem
main::
...
lcall fcn_in_xmem
...
lret
#endasm
#asm xmem
fcn_in_xmem::
...
lret
#endasm
#asm
main::
...
ret
#endasm
Stack Frame
SP
Last Auto Variable
Optional
Return Address
First Parameter
(pushed last)
Lower Addresses
Optional
The return address is always necessary. The presence of auto variables depends on the function definition.
The presence of arguments and structure return space depends on the function call. (The stack pointer may
actually point lower than the indicated mark temporarily because of temporary information pushed on the
stack.)
The shaded area in the stack frame is the stack storage allocated for auto variables. The assembler sym-
bol @SP represents the size of this area.
// This method works in either case because the assembler adjusts the
// constant @SP, so changing the function to nouseix with the keyword
// nouseix, or the compiler directive #nouseix will not break the code.
// But, if SP has been changed by user code, (e.g., a push) it won't work.
ld hl,(sp+@SP+lg+2)
ld b,h
ld c,L
ld hl,(sp+@SP+lg)
ex de,hl
#endasm
}
The Disassembled Code window shows the memory address on the far left, followed by the opcode bytes,
followed by the mnemonics for the instruction. The last column shows the number of cycles for the
instruction, assuming no wait states. The total cycle time for a block of instructions will be shown at the
bottom of the window when the block is selected. The total assumes one execution per instruction, so the
user must take looping and branching into consideration when evaluating execution times.
ld a,(ix+ch) ; a <-- ch
The IX+offset load instruction takes 9 clock cycles and opcode is three bytes. If the program needs to load
a four-byte variable such as lg, the IX+offset instructions are as follows.
typedef struct ss {
int f0; // first field
char f1; // second field
} xyz;
xyz my_struct;
...
my_struct = func();
...
xyz func(){
#asm
...
xor a ; clear register A.
ld hl,@SP+@RETVAL+ss+f1 ; hl <- the offset from SP to f1 field of returned struct
add hl,sp ; hl now points to f1.
ld (hl),a ; load a (now 0) to f1.
...
#endasm
}
It is crucial that @SP be added to @RETVAL because @RETVAL is an offset from the frame reference
point, not from the current SP.
The caller must unwind the stack after the function returns.
1. Recover the stack storage allocated to arguments. With no more than 6 bytes of arguments, the program
may pop data (2 bytes at time) from the stack. Otherwise, it is more efficient to compute a new SP
instead. The following code demonstrates how to unwind arguments totaling 36 bytes of stack storage.
; Note that HL is changed by this code!
; Use “ex de,hl” to save HL if HL has the return value
;;;ex de,hl ; save HL (if required)
ld hl,36 ; want to pop 36 bytes
add hl,sp ; compute new SP value
ld sp,hl ; put value back to SP
;;;ex de,hl ; restore HL (if required)
The value in IP is shown in the status bar at the bottom of the Dynamic C window. If a breakpoint is
encountered, the IP value shown on the status bar reflects the saved context of IP from just before the
breakpoint.
PERIODIC_OFS SERA_OFS
RST10_OFS SERB_OFS
RST18_OFS SERC_OFS
RST20_OFS SERD_OFS
RST28_OFS SERE_OFS
RST38_OFS SERF_OFS
SLAVE_OFS QUAD_OFS
TIMERA_OFS INPUTCAP_OFS
TIMERB_OFS
EXT0_OFS
EXT1_OFS
The following code fragment shows an I&D space compatible method for setting up the ISR for the peri-
odic interrupt in Dynamic C 7.30:
#asm xmem
;*** New method ***
ld a, 0xc3 ;jp instruction entry
ld hl, periodic_isr ;set service routine
ld (INTVEC_BASE+PERIODIC_OFS), a ;write to the interrupt table
ld (INTVEC_BASE+PERIODIC_OFS+1), hl
#endasm
When separate I&D space is enabled, INTVEC_BASE points to a proxy interrupt vector table in RAM
that is modifiable. The code above assumes that the actual interrupt vector table pointed to by the IIR is set
up to point to the proxy vector. When separate I&D space is disabled, INTVEC_BASE and the IIR point to
the same location. The code above is an example only, the default configuration for the periodic interrupt
is not modifiable.
This version of serAclose() in Dynamic C 7.30 is compatible with separate I&D space:
#asm xmem
serAclose::
ld a, 0xc9
ipset 1
ld (INTVEC_BASE + SERA_OFS), a ; ret in first byte of spaisr_start
ld a, 0x00 ; disable interrupts for port
ld (SACRShadow),a
ioi ld (SACR),a
ipres
lret
#endasm
If interrupt_vector is used multiple times for the same interrupt vector, the last one encountered by
the compiler will override all previous ones.
interrupt_vector is syntactic sugar for using the origin directives and assembly code. For example,
the line:
interrupt_vector timerb_intvec timerb_isr;
is equivalent to:
#rcodorg timerb_intvec apply
#asm
jp timerb_isr
#endasm
#rcodorg rootcode resume
• Unbalanced stack.
Ensure the stack is “balanced” when a routine returns. In other words, the SP must be same on
exit as it was on entry. From the caller’s point of view, the SP register must be identical before
and after the call instruction.
• Using the @SP approach after pushing temporary information on the stack.
The @SP approach for inline assembly code assumes that SP points to the low boundary of the
stack frame. This might not be the case if the routine pushes temporary information onto the
stack. The space taken by temporary information on the stack must be compensated for.
The following code illustrates the concept.
; SP still points to the low boundary of the call frame
push hl ; save HL
• Relocatable code.
Jump relative (JR) instructions allow easier code relocation because the jump is relative to the
current program counter. For example, RAM functions are usually written in assembly and are
relocated to RAM from flash. A jump (JP) instruction would not work in this case because the
jump would be to a flash location and not the intended RAM location. Using JR instead of JP
will jump to the intended RAM location.
abandon
Used in single-user cofunctions, abandon{} must be the first statement in the body of the cofunction.
The statements inside the curly braces will be executed only if the cofunction is forcibly abandoned and if
a call to loophead() is made in main() before calling the single-user cofunction. See
Samples\Cofunc\Cofaband.c for an example of abandonment handling.
abort
Jumps out of a costatement.
for(;;){
costate {
...
if( condition ) abort;
}
...
}
VALUE can have any (positive) integer expression or the special operands even and odd. The operand
even aligns the instruction on an even address, and odd on an odd address. Integer expressions align on
multiples of the value of the expression.
Some examples:
align odd ; This aligns on the next odd address
align 2 ; Aligns on a 16-bit (2-byte) boundary
align 4 ; Aligns on a 32-bit (4-byte) boundary
align 100h ; Aligns the code to the next address that is evenly divisible by 0x100
align sizeof(int)+4 ; Complex expression, involving sizeof and integer constant
Note that integer expressions are treated the same way as operand expressions for other asm operators, so
variable labels are resolved to their addresses, not their values.
always_on
The costatement is always active. Unnamed costatements are always on.
anymem
Allows the compiler to determine in which part of memory a function will be placed.
anymem int func(){
...
}
#memmap anymem
#asm anymem
...
#endasm
asm ld hl,0x3333
...
}
auto
A functions’s local variable is located on the system stack and exists as long as the function call does.
int func(){
auto float x;
...
}
bbram
IMPORTANT: bbram does not provide data integrity; instead, use the keyword protected to ensure integ-
rity of data across power failures.
Identifies a variable to be placed into a second root data area with global extent/scope reserved for battery-
backed RAM on boards with more than one RAM device. Generally, the battery-backed RAM is attached
to CS1 due to the low-power requirements. Other than its assigned root data location, a bbram variable is
identical to a normal root variable. In the case of a reset or power failure, the value of a bbram variable is
preserved, but not atomically like with protected variables. No software check is possible to ensure that the
RAM is battery-backed. This requirement must be enforced by the user. Note that bbram variables must
have either static or global storage.
For boards that utilize fast SRAM in addition to a battery-backed SRAM, like the RCM3200, the size of
the battery-backed root data space is specified by a BIOS macro called BBROOTDATASIZE. In version
Dynamic C 9.50 and earlier, the default value for this is 4K. Note that this macro is defined to zero for
boards with only a single SRAM.
See the Rabbit 2000 Microprocessor Designer’s Handbook or the Rabbit 3000 Microprocessor Designer’s
Handbook for information on how the second data area is reserved.
On boards with a single RAM, bbram variables will be treated the same as normal root variables. No warn-
ing will be given; the bbram keyword is simply ignored when compiling to boards with a single RAM with
the assumption that the RAM is battery-backed. Please refer to _xalloc for information on how to access
battery-backed data in xmem.
c
Use in assembly block to insert one Dynamic C instruction.
#asm
InitValues::
c start_time = 0;
c counter = 256;
ld hl,0xa0;
ret
#endasm
case
Identifies the next case in a switch statement.
switch( expression ){
case constant:
...
case constant:
...
case constant:
...
...
}
cofunc
Indicates the beginning of a cofunction.
cofunc|scofunc type [name][[dim]]([type arg1, ..., type argN])
{ [ statement | yield; | abort; | waitfor(expression);]... }{
...
}
cofunc, scofunc
The keywords cofunc or scofunc (a single-user cofunction) identify the statements enclosed in curly
braces that follow as a cofunction.
type
Whichever keyword (cofunc or scofunc) is used is followed by the data type returned (void, int,
etc.).
name
A name can be any valid C name not previously used. This results in the creation of a structure of type
CoData of the same name.
dim
The cofunction name may be followed by a dimension if an indexed cofunction is being defined.
cofunction body
A cofunction can have as many C statements, including abort, yield, waitfor, and waitfordone
statements, as needed. Cofunctions can contain calls to other cofunctions.
Example 2:
// cptr_to_i is a constant pointer to a constant integer
const int i = 3;
const int * const cptr_to_i = &i;
Example 3:
// ax is a constant 2 dimensional integer array
const int ax[2][2] = {{2,3}, {1,2}};
Example 4:
struct rec {
int a;
char b[10];
};
// zed is a constant struct
const struct rec zed = {5, “abc”};
Example 5:
// cptr is a constant pointer to an integer
typedef int * ptr_to_int;
const ptr_to_int cptr = &i;
// this declaration is equivalent to the previous one
int * const cptr = &i;
NOTE: The default storage class is auto, so the above code would have to be outside of a
function or would have to be explicitly set to static.
costate
Indicates the beginning of a costatement.
costate [ name [ state ] ] {
...
}
Name can be absent. If name is present, state can be always_on or init_on. If state is absent,
the costatement is initially off.
debug
Indicates a function is to be compiled in debug mode. This is the default case for Dynamic C functions
with the exception of pure assembly language functions.
Library functions compiled in debug mode can be single stepped into, and breakpoints can be set in them.
debug int func(){
...
}
#asm debug
...
#endasm
The debug keyword in combination with the norst keyword will give you run-time checking without
debug. For example,
debug norst foo() {
}
will perform run-time checking if enabled, but will not have rst instructions.
do
Indicates the beginning of a do loop. A do loops tests at the end and executes at least once.
do
...
while( expression );
else
The false branch of an if statement.
if( expression )
statement // “statement” executes when “expression” is true
else
statement // “statement” executes when “expression” is false
An enum can be declared in local or global scope. The tag foo is optional; but it allows further declara-
tions:
enum foo rabbits;
extern
Indicates that a variable is defined in the BIOS, later in a library file, or in another library file. Its main use
is in module headers.
/*** BeginHeader ..., var */
extern int var;
/*** EndHeader */
int var;
...
float
Declares variables, function return values, or arrays, as 32-bit IEEE floating point.
int func(){
float x, y, *p;
float PI = 3.14159265;
...
}
goto
Causes a program to go to a labeled section of code.
...
if( condition ) goto RED;
...
RED:
Use goto to jump forward or backward in a program. Never use goto to jump into a loop body or a
switch case. The results are unpredictable. However, it is possible to jump out of a loop body or
switch case.
if( expression ){
statements
If one of the expressions is true (they are evaluated in order), the statements controlled by that expression
are executed. An if statement can have zero or more else if parts. The else is optional and executes
only when none of the if or else if expressions are true (non-zero).
init_on
The costatement is initially on and will automatically execute the first time it is encountered in the execu-
tion thread. The costatement becomes inactive after it completes (or aborts).
interrupt
Indicates that a function is an interrupt service routine (ISR). All registers, including alternates, are saved
when an interrupt function is called and restored when the interrupt function returns. Writing ISRs in C is
never recommended, especially when timing is critical.
interrupt isr (){
...
}
Interrupt vector names and ISR names are found in Table 11-5 on page 183. The following code fragment
illustrates how interrupt_vector is used.
// Set up an Interrupt Service Routine for Timer B
#asm
timerb_isr::
; ISR code
...
ret
#endasm
main() {
// Variables
...
// Set up ISR
interrupt_vector timerb_intvec timerb_isr; // Compile time setup
// Code
...
}
interrupt_vector overrides run time setup. For run time setup, you would replace the
interrupt_vector statement above with:
#rcodorg <INT_VEC_NAME> apply
#asm
INTVEC_RELAY_SETUP(timerb_intvec + TIMERB_OFS)
#endasm
This results in a slower interrupt (80 clock cycles are added), but an interrupt vector that can be modified
at run time. Interrupt vectors that are set up using interrupt_vector are fast, but can’t be modified
at run time since they are set at compile time.
If you are using Dynamic C 9.30 or later, the _RK_FIXED_VECTORS macro must be used to condition-
ally compile code containing the interrupt_vector keyword. For Rabbit 3000A and later CPUs,
Dynamic C makes use of the new RAMSR capability to make in-RAM interrupt table access fast. The fol-
lowing code demonstrates the correct way to use _RK_FIXED_VECTORS so as to eliminate errors
regarding undefined interrupt vectors.
main(){
#if __SEPARATE_INST_DATA__ && (_RK_FIXED_VECTORS)
interrupt_vector inputcap_intvec ic_isr;
interrupt_vector pwm_intvec pwm_isr;
#else
SetVectIntern(0x1A, ic_isr); // set up ISR
SetVectIntern(0x17, pwm_isr); // set up ISR
#endif
main() {
foo(); // This now generates an lcall instruction
}
long
Declares variables, function return values, or array elements to be 32-bit integers. If nothing else is speci-
fied, long implies a signed integer.
long i, j, *k; // 32-bit signed
unsigned long int w; // 32-bit unsigned
long funct ( long arg ){
...
}
main
Identifies the main function. All programs start at the beginning of the main function. (main is actu-
ally not a keyword, but is a function name.)
norst
Indicates that a function does not use the RST instruction for breakpoints.
norst void func(){
...
}
The norst keyword in combination with the debug keyword will give you run-time checking without
debug. For example,
debug norst foo() {
}
will perform runtime-checking if enabled, but will not have rst instructions.
nouseix
Indicates a function does not use the IX register as a stack frame reference pointer. This is the default case.
nouseix void func(){
...
}
NULL
The null pointer. (This is actually a macro, not a keyword.) Same as (void *)0.
The call to _sysIsSoftReset checks to see if the previous board reset was due to the compiler restart-
ing the program (i.e., a soft reset). If so, then it initializes the protected variable flags and calls
sysResetChain(), a function chain that can be used to initialize any protected variables or do other
initialization. If the reset was due to a power failure or watchdog time-out, then any protected variables
that were being written when the reset occurred are restored.
A system that shares data among different tasks or among interrupt routines can find its shared data cor-
rupted if an interrupt occurs in the middle of a write to a multi-byte variable (such as type int or float).
The variable might be only partially written at its next use. Declaring a multi-byte variable shared means
that changes to the variable are atomic, i.e., interrupts are disabled while the variable is being changed.
You may declare a multi-byte variable as both shared and protected.
register
The register keyword is not currently implemented in Dynamic C, but is reserved for possible future
implementation. It is currently synonymous with the keyword auto.
root
Indicates a function is to be placed in root memory. This keyword is semantically meaningful in function
prototypes and produces more efficient code when used. Its use must be consistent between the prototype
and the function definition.
root int func(){
...
}
#memmap root
#asm root
...
#endasm
scofunc
Indicates the beginning of a single-user cofunction. See cofunc on page 189.
This example adds a segment to the function chain _GLOBAL_INIT. Using segchain is equivalent to
using the #GLOBAL_INIT directive. When this function chain executes, this and perhaps other segments
elsewhere execute. The effect in this example is to reinitialize vec[].
shared
Indicates that changes to a multi-byte variable (such as a float) are atomic. Interrupts are disabled when
the variable is being changed. Local variables cannot be shared. Note that you must be running off the
main system clock to use shared variables. This is because the atomicity of the write cannot be ensured
when running off the 32 kHz clock.
shared float x, y, z;
shared int j;
...
main(){
...
}
If i is a shared variable, expressions of the form i++ (or i = i+ 1) constitute two atomic references to
variable i, a read and a write. Be careful because i++ is not an atomic operation.
size
Declares a function to be optimized for size (as opposed to speed).
size int func (){
...
}
sizeof
A built-in function that returns the size in bytes of a variable, array, structure, union, or of a data type.
sizeof() can be used inside of assembly blocks.
int list[] = { 10, 99, 33, 2, -7, 63, 217 };
...
x = sizeof(list); // x will be assigned 14
speed
Declares a function to be optimized for speed (as opposed to size).
speed int func (){
...
}
struct
This keyword introduces a structure declaration, which defines a type.
struct {
...
int x;
int y;
int z;
} thing1; // defines the variable thing1 to be a struct
struct speed{
int x;
int y;
int z;
}; // declares a struct type named speed
struct rabbit {
struct speed fast;
struct speed faster;
}; // declares a nested struct type named rabbit
The switch statement may contain any number of cases. The constants of the case statements are com-
pared with expression. If there is a match, the statements for that case execute. The default case, if
it is present, executes if none of the constants of the case statements match expression.
If the statements for a case do not include a break, return, continue, or some means of exiting
the switch statement, the cases following the selected case will also execute, regardless of whether their
constants match the switch expression.
typedef
This keyword provides a way to create new names for existing data types.
typedef struct {
int x;
int y;
} xyz; // defines a struct type...
unsigned
Declares a variable or array to be unsigned. If nothing else is specified in a declaration, unsigned means
16-bit unsigned integer.
unsigned i, j, *k; // 16-bit, unsigned
unsigned int x; // 16-bit, unsigned
unsigned long w; // 32-bit, unsigned
unsigned funct ( unsigned arg ){
...
}
Values in a 16-bit unsigned integer range from 0 to 65,535 instead of –32768 to +32767. Values in an
unsigned long integer range from 0 to 232 – 1.
useix
Indicates that a function uses the IX register as a stack frame pointer.
useix void func(){
...
}
waitfordone
(wfd)
The waitfordone keyword can be abbreviated as wfd. It is part of Dynamic C’s cooperative multitask-
ing constructs. Used inside a costatement or a cofunction, it executes cofunctions and firsttime func-
tions. When all the cofunctions and firsttime functions in the wfd statement are complete, or one of
them aborts, execution proceeds to the statement following wfd. Otherwise a jump is made to the ending
brace of the costatement or cofunction where the wfd statement appears; when the execution thread comes
around again, control is given back to the wfd statement.
The wfd statements below are from Samples\cofunc\cofterm.c
x = wfd login(); // wfd with one cofunction
wfd may return a value. In the example above, the variable x is set to 1 if login() completes execution
normally and set to -1 if it aborts. This scheme is extended when there are multiple cofunctions inside the
wfd: if no abort has taken place in any cofunction, wfd returns 1, 2, ..., n to indicate which cofunction
inside the braces finished executing last. If an abort takes place, wfd returns -1, -2, ..., -n to indicate which
cofunction caused the abort.
xdata
Declares a block of data in extended flash memory.
xdata name { value_1, ... value_n };
The 20-bit physical address of the block is assigned to name by the compiler as an unsigned long variable.
The amount of memory allocated depends on the data type. Each char is allocated one byte, and each
int is allocated two bytes. If an integer fits into one byte, it is still allocated two bytes. Each float and
long cause four bytes to be allocated.
The value list may include constant expressions of type int, float, unsigned int, long,
unsigned long, char, and (quoted) strings. For example:
xdata name1 {'\x46','\x47','\x48','\x49','\x4A','\x20','\x20'};
xdata name2 {'R','a','b','b','i','t'};
xdata name3 {" Rules! "};
xdata name4 {1.0,2.0,(float)3,40e-01,5e00,.6e1};
The data can be viewed directly in the dump window by doing a physical memory dump using the 20-bit
address of the xdata block. See Samples\Xmem\xdata.c for more information.
or
xmem int func();
int func(){}
or
int func();
xmem int func(){}
In addition to flagging individual functions, the xmem keyword can be used with the compiler directive
#memmap to send all functions not declared as root to extended memory.
#memmap xmem
This construct is helpful if an application is running out of root code space. Another strategy is to use sepa-
rate I&D space. Using both #memmap xmem and I&D space is not advised and might cause an applica-
tion to run out of xmem, depending on the size of the application and the size of the flash.
void
This keyword conforms to ANSI C. Thus, it can be used in three different ways.
1. Parameter List - used to identify an empty parameter list (a.k.a., argument list). An empty parameter list
can also be identified by having nothing in it. The following two statements are functionally identical:
int functionName(void);
int functionName();
2. Pointer to Void - used to declare a pointer that points to something that has no type.
void *ptr_to_anything;
xstring
Declares a table of strings in extended memory. The strings are allocated in flash memory at compile time
which means they can not be rewritten directly.
The table entries are 20-bit physical addresses. The name of the table represents the 20-bit physical
address of the table; this address is assigned to name by the compiler.
xstring name { “string_1”, . . . “string_n” };
yield
Used in a costatement, this keyword causes the costatement to pause temporarily, allowing other costate-
ments to execute. The yield statement does not alter program logic, but merely postpones it.
for(;;){
costate {
...
yield;
...
}
...
}
#asm
Syntax: #asm options
Begins a block of assembly code. The available options are:
• const: When seperate I&D space is enabled, assembly constants should be placed in their own assem-
bly block (or done in C). For more information, see Section 11.2.2, “Defining Constants.”
• debug: Enables debug code during assembly.
• nodebug: Disables debug code during assembly. This is the default condition. It is still possible to
single step through assembly code as long as the assembly window is open.
• xmem: Places a block of code into extended memory, overriding any previous memory directives. The
block is limited to 4KB.
If the #asm block is unmarked, it will be compiled to root.
#class
Syntax: #class options
Controls the storage class for local variables. The available options are:
• auto: Place local variables on the stack.
• static: Place local variables in permanent, fixed storage.
#define
Syntax: #define name text or #define name (parameters . . . ) text
Defines a macro with or without parameters according to ANSI standard. A macro without parameters
may be considered a symbolic constant. Supports the # and ## macro operators. Macros can have up to 32
parameters and can be nested to 126 levels.
#endasm
Ends a block of assembly code.
#fatal
Syntax: #fatal “...”
Instructs the compiler to act as if a fatal error. The string in quotes following the directive is the message to
be printed
// This function outputs and returns the number of times it has been called.
int foo(){
char count;
#GLOBAL_INIT{
// initialize count
count = 1;
// make port A output
WrPortI(SPCR,SPCRShadow,0x84);
}
// output count
WrPortI(PADR,NULL,count);
// increment and return count
return ++count;
}
#error
Syntax: #error "…"
Instructs the compiler to act as if an error was issued. The string in quotes following the directive is the
message to be printed
#funcchain
Syntax: #funcchain chainname name
Adds a function, or another function chain, to a function chain.
These directives control conditional compilation. Combined, they form a multiple-choice if. When the
condition of one of the choices is met, the Dynamic C code selected by the choice is compiled. Code
belonging to the other choices is ignored.
main(){
#if BOARD_TYPE == 1
#define product "Ferrari"
#elif BOARD_TYPE == 2
#define product "Maserati"
#elif BOARD_TYPE == 3
#define product "Lamborghini"
#else
#define product "Chevy"
#endif
...
}
The #elif and #else directives are optional. Any code between an #else and an #endif is com-
piled if all values for constant_expression are false.
#ifdef
Syntax: #ifdef name
This directive enables code compilation if name has been defined with a #define directive. This direc-
tive must have a matching #endif.
#interleave
#nointerleave
Controls whether Dynamic C will intersperse library functions with the program’s functions during compi-
lation together, separately from the library functions.
#nointerleave forces the user-written functions to be compiled first.The #nointerleave direc-
tive, when placed at the top of application code, tells Dynamic C to compile all of the application code first
and then to compile library code called by the application code afterward, and then to compile other library
code called by the initial library code following that, and so on until finished.
Note that the #nointerleave directive can be placed anywhere in source code, with the effect of stop-
ping interleaved compilation of functions from that point on. If #nointerleave is placed in library
code, it will effectively cause the user-written functions to be compiled together starting at the statement
following the library call that invoked #nointerleave.
#makechain
Syntax: #makechain chainname
Creates a function chain. When a program executes the function chain named in this directive, all of the
functions or segments belonging to the function chain execute.
#pragma
Syntax: #pragma nowarn [warnt|warns]
Trivial warnings (warnt) or trivial and serious warnings (warns) for the next physical line of code are
not displayed in the Compiler Messages window. The argument is optional; default behavior is warnt.
Syntax: #pragma nowarn [warnt|warns] start
Trivial warnings (warnt) or trivial and serious warnings (warns) are not displayed in the Compiler Mes-
sages window until the #pragma nowarn end statement is encountered. The argument is optional;
default behavior is warnt. #pragma nowarn cannot be nested.
#undef
Syntax: #undef identifier
Removes (undefines) a defined macro.
#use
Syntax: #use pathname
Activates a library named in LIB.DIR so modules in the library can be linked with the application pro-
gram. This directive immediately reads in all the headers in the library unless they have already been read.
#warns
Syntax: #warns “...”
Instructs the compiler to act as if a serious warning was issued. The string in quotes following the directive
is the message to be printed.
#warnt
Syntax: #warnt “...”
Instructs the compiler to act as if a trivial warning was issued. The string in quotes following the directive
is the message to be printed.
#ximport
Syntax: #ximport “filename” symbol
This compiler directive places the length of filename (stored as a long) and its binary contents at the next
available place in xmem flash. filename is assumed to be either relative to the Dynamic C installation
directory or a fully qualified path. symbol is a compiler generated macro that gives the physical address
where the length and contents were stored.
The sample program ximport.c illustrates the use of this compiler directive.
although, like the minus sign, some unary operators can also be used for binary operations.
There are many kinds of operators with operator precedence. Precedence governs which operations
are performed before other operations, when there is a choice.
For example, given the expression
a = b + c * 10;
will the + or the * be performed first? Since * has higher precedence than +, it will be performed first.
The expression is equivalent to
a = b + (c * 10);
a = (b + c) * 10;
Unary operators and assignment operators associate from right to left. Most other operators associate from
left to right.
float x;
*(char*)&x = 0x17; // low byte of x gets value
When the data types for an operation are mixed, the resulting type is the more precise.
float x, y, z;
int i, j, k;
char c;
z = i / x; // same as (float)i / x
j = k + c; // same as k + (int)c
By placing a type name in parentheses in front of a variable, the program will perform type casting or type
conversion. In the example above, the term (float)i means the “the value of i converted to floating
point.”
The operators are summarized in the following pages.
+
Unary plus, or binary addition. (Standard C does not have unary plus.) Unary plus does not really do any-
thing.
a = b + 10.5; // binary addition
z = +y; // just for emphasis!
–
Unary minus, or binary subtraction.
a = b - 10.5; // binary subtraction
z = -y; // z gets the negative of y
Beware of using uninitialized pointers. Also, the indirection operator can be used in
complex ways.
int *list[10] // array of 10 pointers to integers
int (*list)[10] // pointer to array of 10 integers
float** y; // pointer to a pointer to a float
z = **y; // z gets the value of y
typedef char **stp;
stp my_stuff; // my_stuff is typed char**
/
Divide is a binary operator. Integer division truncates; floating-point division does not.
const int i = 18, const j = 7, k; float x;
k = i / j; // result is 2;
x = (float)i / j; // result is 2.591...
If the ++ operator is used with a pointer, the value of the pointer increments by the size of the object (in
bytes) to which it points. With operands other than pointers, the value increments by 1.
––
Pre- or post-decrement. If the –– precedes an operand, the operand is decremented before use. If the ––
operator follows an operand, the operand is decremented after use.
int j, a[12];
j = 12;
q = a[––j]; // j becomes 11, then q gets a[11]
r = a[––j]; // j becomes 10, then r gets a[10]
s = j––; // s = 10, then j becomes 9
j––; // j becomes 8
If the –– operator is used with a pointer, the value of the pointer decrements by the size of the object (in
bytes) to which it points. With operands other than pointers, the value decrements by 1.
%
Modulus. This is a binary operator. The result is the remainder of the left-hand operand divided by the
right-hand operand.
const int i = 13;
j = i % 10; // j gets i mod 10 or 3
const int k = -11;
j = k % 7; // j gets k mod 7 or -4
=
Assignment. This binary operator causes the value of the right operand to be assigned to the left operand.
Assignments can be “cascaded” as shown in this example.
a = 10 * b + c; // a gets the result of the calculation
+=
Addition assignment.
a += 5; // Add 5 to a. Same as a = a + 5
-=
Subtraction assignment.
a -= 5; // Subtract 5 from a. Same as a = a - 5
*=
Multiplication assignment.
a *= 5; // Multiply a by 5. Same as a = a * 5
/=
Division assignment.
a /= 5; // Divide a by 5. Same as a = a / 5
%=
Modulo assignment.
a %= 5; // a mod 5. Same as a = a % 5
<<=
Left shift assignment.
a <<= 5; // Shift a left 5 bits. Same as a = a << 5
&=
Bitwise AND assignment.
a &= b; // AND a with b. Same as a = a & b
^=
Bitwise XOR assignment.
a ^= b; // XOR a with b. Same as a = a ^ b
|=
Bitwise OR assignment.
a |= b; // OR a with b. Same as a = a | b
<<
Shift left. This is a binary operator. The result is the value of the left operand shifted by the number of bits
specified by the right operand.
int i = 0xF00F;
j = i << 4; // j gets 0x00F0
The most significant bits of the operand are lost; the vacated bits become zero.
>>
Shift right. This is a binary operator. The result is the value of the left operand shifted by the number of
bits specified by the right operand:
int i = 0xF00F;
j = i >> 4; // j gets 0xFF00
The least significant bits of the operand are lost; the vacated bits become zero for unsigned variables and
are sign-extended for signed variables.
As a binary operator, this performs the bitwise AND of two integer (char, int, or long) values.
int i = 0xFFF0;
int j = 0x0FFF;
z = i & j; // z gets 0x0FF0
^
Bitwise exclusive OR. A binary operator, this performs the bitwise XOR of two integer (8-bit, 16-bit or
32-bit) values.
int i = 0xFFF0;
int j = 0x0FFF;
z = i ^ j; // z gets 0xF00F
|
Bitwise inclusive OR. A binary operator, this performs the bitwise OR of two integer (8-bit, 16-bit or 32-
bit) values.
int i = 0xFF00;
int j = 0x0FF0;
z = i | j; // z gets 0xFFF0
~
Bitwise complement. This is a unary operator. Bits in a char, int, or long value are inverted:
int switches;
switches = 0xFFF0;
j = ~switches; // j becomes 0x000F
<
Less than. This binary (relational) operator yields a Boolean value. The result is 1 if the left operand is less
than the right operand, and 0 otherwise.
if( i < j ){
body // executes if i < j
}
<=
Less than or equal. This binary (relational) operator yields a boolean value. The result is 1 if the left oper-
and is less than or equal to the right operand, and 0 otherwise.
if( i <= j ){
body // executes if i <= j
}
OK = a <= b; // true when a <= b
>
Greater than. This binary (relational) operator yields a Boolean value. The result is 1 if the left operand is
greater than the right operand, and 0 otherwise.
if( i > j ){
body // executes if i > j
}
OK = a > b; // true when a > b
>=
Greater than or equal. This binary (relational) operator yields a Boolean value. The result is 1 if the left
operand is greater than or equal to the right operand, and 0 otherwise.
if( i >= j ){
body // executes if i >= j
}
OK = a >= b; // true when a >= b
==
Equal. This binary (relational) operator yields a Boolean value. The result is 1 if the left operand equals the
right operand, and 0 otherwise.
if( i == j ){
body // executes if i = j
}
OK = a == b; // true when a = b
Note that the == operator is not the same as the assignment operator (=). A common mistake is to write
if( i = j ){
body
}
Here, i gets the value of j, and the if condition is true when i is non-zero, not when i equals j.
!=
Not equal. This binary (relational) operator yields a Boolean value. The result is 1 if the left operand is not
equal to the right operand, and 0 otherwise.
if( i != j ){
body // executes if i != j
}
OK = a != b; // true when a != b
&&
Logical AND. This is a binary operator that performs the Boolean AND of two values. If either operand is
0, the result is 0 (FALSE). Otherwise, the result is 1 (TRUE).
||
Logical OR. This is a binary operator that performs the Boolean OR of two values. If either operand is
non-zero, the result is 1 (TRUE). Otherwise, the result is 0 (FALSE).
if( !test ){
...
}
( )
Grouping. Expressions enclosed in parentheses are performed first. Parentheses also enclose function
arguments. In the expression
a = (b + c) * 10;
[ ]
Array subscripts or dimension. All array subscripts count from 0.
int a[12]; // array dimension is 12
j = a[i]; // references the ith element
. (dot)
The dot operator joins structure (or union) names and subnames in a reference to a structure (or union) ele-
ment.
struct {
int x;
int y;
} coord;
m = coord.x;
...
m = p->x; // reference to structure element
&
Address operator, or bitwise AND. As a unary operator, this provides the address of a variable:
int x;
z = &x; // z gets the address of x
As a binary operator, this performs the bitwise AND of two integer (char, int, or long) values.
int i = 0xFFF0;
int j = 0x0FFF;
z = i & j; // z gets 0x0FF0
*
Indirection, or multiplication. As a unary operator, it indicates indirection. When used in a declaration, *
indicates that the following item is a pointer. When used as an indirection operator in an expression, * pro-
vides the value at the address specified by a pointer.
int *p; // p is a pointer to an integer
int j = 45;
p = &j; // p now points to j.
k = *p; // k gets the value to which p points, namely 45.
*p = 25; // The integer to which p points gets 25.
// Same as j = 25, since p points to j.
? :
If the first operand evaluates true (non-zero), then the result of the operation is the second operand. Other-
wise, the result is the third operand.
int i, j, k;
...
i = j < k ? j : k;
The ? : operator is for convenience. The above statement is equivalent to the following.
if( j < k )
i = j;
else
i = k;
If the second and third operands are of different type, the result of this operation is returned at the higher
precision.
(type)
The cast operator converts one data type to another. A floating-point value is truncated when converted
to integer. The bit patterns of character and integer data are not changed with the cast operator, although
high-order bits will be lost if the receiving value is not large enough to hold the converted value.
unsigned i; float x = 10.5; char c;
i = (unsigned)x; // i gets 10;
c = *(char*)&x; // c gets the low byte of x
typedef ... typeA;
typedef ... typeB;
typeA item1;
typeB item2;
...
item2 = (typeB)item1; // forces item1 to be treated as a typeB
sizeof
The sizeof operator is a unary operator that returns the size (in bytes) of a variable, structure, array, or
union. It operates at compile time as if it were a built-in function, taking an object or a type as a parameter.
typedef struct{
int x;
char y;
float z;
} record;
record array[100];
int a, b, c, d;
char cc[] = "Fourscore and seven";
char *list[] = { "ABC", "DEFG", "HI" };
Why is sizeof(list) equal to 6? list is an array of 3 pointers (to char) and pointers have two
bytes.
Why is sizeof(cc) equal to 20 and not 19? C strings have a terminating null byte appended by the
compiler.
Because of the comma operator, the initialization has two parts: (1) set i to 0 and (2) get the length of
string s. The stepping expression also has two parts: increment i and decrement j.
The comma operator exists to allow multiple expressions in loop or if conditions.
The table below shows the operator precedence, from highest to lowest. All operators grouped together
have equal precedence.
Table 13-1. Operator Precedence
14.1 Editing
A file is displayed in a text window when it is opened or created. More than one text window may be open.
If the same file is in multiple windows, any changes made to the file in one window will be reflected in all
text windows that display that file. Dynamic C supports normal Windows text editing operations.
A mouse (or other pointing device) may be used to position the text cursor, select text, or extend a text
selection. The keyboard may be used to do these same things. Text may be scrolled using the arrow keys,
the PageUp and PageDown keys, and the Home and End keys. The up, down, left and right arrow keys
move the cursor in the corresponding directions.
The Home key may be used alone or with other keys.
The Ctrl key also works in conjunction with “[” for delimiter matching. Place the cursor before the delim-
iter you are attempting to match and press “Ctrl+[”. The cursor will move to just before the matching
delimiter.
Note that delimiters in comments are also matched. For example, in the following code, <Ctrl+[> counts
commented-out braces in the matching, giving a false indication that the main function has balanced curly
braces when in fact it does not.
main()
{
{
//}
/*
}
*/
14.2 Menus
Dynamic C’s main menu has eight command menus, as well as the standard Windows system menus.
An available command can be executed from a
menu by either clicking the menu and then
clicking the command, or by pressing the Alt
key to activate the menu bar, using the left and
right arrow keys to select a menu, and then using the up or down arrow keys to select a command before
pressing the Enter key.
Open <Ctrl+O>
Presents a dialog box to specify the name of a file to
open. To select a file, type in the file name (pathnames
may be entered), or browse and select it. Unless there
is a problem, Dynamic C will present the contents of
the file in a text window. The program can then be
edited or compiled. Multiple files can be selected by
either holding down <Ctrl> then clicking the left
mouse on each filename you want to open, or by drag-
ging the selection rectangle over multiple filenames.
Save <Ctrl+S>
The Save command updates an open file to reflect changes made since the last time the file was saved.
If the file has not been saved before (i.e., the file is a new untitled file), the Save As dialog will appear
to prompt for a name. Use the Save command often while editing to protect against loss during power
failures or system crashes.
Save As
Presents a dialog box to save the file under a new name. To select a file name, type it in the File name
field. The file will be saved in the folder displayed in the Save in field. You may, of course, browse to
another location. You may also select an existing file. Dynamic C will ask you if you wish to replace
the existing file with the new one.
Close <Ctrl+F4>
Closes the active editor window. If there is an attempt to close a modified file, Dynamic C will ask you
if you wish to save the changes. The file is saved when Yes is clicked or “y” is typed. If the file is unti-
tled, there will be a prompt for a file name in the Save As dialog. Any changes to the document will be
discarded if No is clicked or “n” is typed. Choosing Cancel results in a return to Dynamic C with no
action taken.
Project
Allows a project file to be created, opened, saved, saved as a different name and closed. See
Chapter 16, “Project Files.” for all the details on project files.
Print Preview
Displays whichever file is in the active editor window in the Preview Form window, showing how the
text will look when it is printed. You can search and navigate through the printable pages and bring up
the Print dialog box.
Print
Brings up the Print dialog box, which allows you to choose a printer. Only text in an editor window can
be printed. To print the contents of debug windows the text must be copied and pasted to an editor win-
dow. (The Stdio window is an exception; its contents may be automatically written to a file, which may
then be printed.) As many copies of the text as needed may be printed. If more than one copy is
requested, the pages may be collated or uncollated.
Exit <Alt+F4>
Close Dynamic C after prompting to save any unsaved changes to open files.
Undo <Ctrl+Z>
This option undoes recent changes in the active edit win-
dow. The command may be repeated several times to undo
multiple changes. Undo operations have unlimited depth.
Two types of undo are supported—applied to a single oper-
ation and applied to a group of the same operations (2 con-
tinuous deletes are considered a single operation.
Dynamic C only discards undo information if the “Undo
after save” option is unchecked in the Editor dialog under
Environment Options.
Redo <Shift+Ctrl+Z>
Redoes changes recently undone. This command only
works immediately after one or more Undo operations.
Cut <Ctrl+X>
Removes selected text and saves to the clipboard.
Copy <Ctrl+C>
Makes a copy of text selected in a file or in a debug win-
dow. The text is saved on the clipboard.
Paste <Ctrl+V>
Pastes text from the clipboard to the current insertion point.
Nothing can be pasted in a debugging window. The contents
of the clipboard may be pasted virtually anywhere, repeat-
edly (as long as nothing new is cut or copied into the clipboard), in the same or other source files, or
even in word processing or graphics program documents.
Toggle Bookmark
Toggle one of ten bookmarks in the active edit window.
Go to Bookmark
Go to one of ten bookmarks in the active edit window. Executing this command again will take you
back to the location you were at before going to the bookmarked location.
Replace <F6>
Finds and replaces the specified text. Text may be specified by selecting it prior to opening the Replace
Text dialog box. Only one word may be selected; if more than one word is selected, the last word
selected appears as the entry for the search text. More than one word of text may be specified by typing
it in or selecting it from the available history of search text. The replacement text is typed or selected
from the available history of replacement text.
As with the Find dialog box, there are several ways to narrow or broaden the search criteria. An impor-
tant option is Prompt on replace. If this is unchecked, Dynamic C will not prompt before making the
replacement, which could be dangerous in combination with the choice to Replace All.
Go to Line Number
Positions the insertion point at the beginning of the specified line.
Compile <F5>
Compiles a program and loads it to the target or to a .bin file. When you press <F5> or select Compile
from the Compile menu, the active file will be compiled according to the current compiler options.
Compiler options are set in the Compiler tab of the Project Options dialog. When compiling directly to
the target, Dynamic C queries the attached target for board information and creates macros to automat-
ically configure the BIOS and libraries.
Any compilation errors are listed in the automatically activated Compiler Messages window. Press
<F1> to obtain more information for any error message that is highlighted in this window.
Compile to Target
Expands to one of three choices. They override any BIOS Memory Setting choice made in the Com-
piler tab of the Project Options dialog.
• Compile to Flash
• Compile to RAM
• Compile to Flash, Run in RAM
The following message will appear upon successful compilation and loading of BIOS code.
Run <F9>
Starts program execution from the current breakpoint. Registers are
restored, including interrupt status, before execution begins. If in
Edit mode, the program is compiled and downloaded.
Stop <Ctrl+Q>
The “Stop” command stops the program at the current point of exe-
cution. Usually, the debugger cannot stop within nodebug code. On
the other hand, the target can be stopped at an RST 028h instruction
if an RST 028h assembly code is inserted as inline assembly code
in nodebug code. However, the debugger will never be able to
find and place the execution cursor in nodebug code.
Close Connection
If using a serial connection, disconnects the programming serial port between PC and target so that the
target serial port and the PC serial port are both accessible to other applications.
If using a TCP/IP connection, closes the socket between the PC and the RabbitLink or between the PC
and the RabbitSys-enabled board.
If the evaluation of a watch expression causes a run-time exception, the exception will be ignored and
the value displayed in the Watches window for the watch expression will be undefined.
Starting with Dynamic C 9, structure members are displayed whenever a watch expression is set on a
struct. Prior to Dynamic C 9, separate watch expressions had to be added for each member. Introduced
in Dynamic C 8.01, the Debug Windows tab of the Environment Options menu lets you set flyover hint
evaluation of any expression that can be watched without having to explicitly set the watch expression.
See “Watch” on page 285 and “Watch Window” on page 267 for more details.
Evaluate Expression
Brings up the Evaluate Expression dialog where you can enter a single expression in the Expression
dialog. The result is displayed in the Result text box when Evaluate is clicked. Multiple Evaluate
Expression dialogs can be active at the same time.
Memory Dump windows may be scrolled. Scrolling causes the contents of other memory addresses to
appear in the window. Hotkeys ArrowUp, ArrowDown, PageUp, PageDown are active in the Memory
Dump window. The window always displays as many lines of 16 bytes and their ASCII equivalent as
will fit in the window.
Values in the Dump window are updated automatically when Dynamic C stops or comes to a break-
point. Updates only occur if the window is updateable. This can be set either by right clicking in the
Memory Dump window and toggling the updateable menu item, or by clicking on the Debug Windows
tab in Options | Environment Options. Select Memory Dump under Specific Preferences, then check
the option “Allow automatic updates.” The Memory Dump window can be updated at any time by
clicking the Update button on the tool bar or by right clicking and choosing Update from the popup
menu.
The Memory Dump window is capable of displaying three different types of dumps. A dump of a logi-
cal address ([0x]mmmm) will result in a 64k scrollable region (0x0000 - 0xffff). A dump of a physical
address ([0x]mmmmm) will result in a dump of a 1M region (0x00000 - 0xfffff). A dump of an
xpc:offset address (nn:mmmm) will result in either a 4k, 64k, or 1M dump range depending on the
option set on the Debug Windows tab under Options | Environment Options.
Note that adding a leading zero to a logical address makes it a physical address.
Any number of dump windows may be open at the same time. The type of dump or dump region for a
dump window can be changed by entering a new address in the toolbar’s text entry area. To the right of
the this area is a button that, when clicked, will cause the address in the text entry area to be the first
address in the Dump window. The toolbar for a dump window may be hidden or visible.
Environment Options
Dynamic C comes with a built-in, full-featured text editor. It may be
customized to suit your style using the Environment Options dialog
box. The dialog box has tabs for various aspects of the editor. Note
that keyboard shortcuts for some of the options have no character to
underline, so the character is shown between brackets, thus, when
the Editor menu options are visible, Alt+Q is the keyboard shortcut
for toggling the option “Cursor through tabs”.
Editor Tab
Click on the Editor tab to display the following dialog. Installation defaults are shown.
The Editor options are detailed here. All actions taken are immediately reflected in the text area at
the bottom of the dialog, and in any open editor windows.
Backspace unindents
Check this to backspace through indentation levels. If this is unchecked, the backspace will
move one character at a time.
Persistent blocks
Check this option to keep selected text selected when you move the cursor using the arrow
keys. Using the mouse to move the cursor will deselect the block of text. Using menu
commands or keyboard shortcuts will affect the entire block of selected text. For example,
pressing <Ctrl+X> will cut the selected block. But pressing the delete key will only delete one
character to the right of the cursor. If this option was unchecked, pressing the delete key would
delete all the selected text.
If this option is checked and the Find or Replace dialog is opened with a piece of text selected
in the active edit window, the search scope will default to that bit of selected text only.
Overwrite blocks
Check this option to enable overwriting a selected block of text by pressing a key on the key-
board. The block of text may be overwritten with any character, including whitespaces or by
pressing delete or backspace.
Group undo
Check this option to undo changes one group at a time. With this option unchecked, each oper-
ation is undone individually.
Disable dragging
Checking this option disables drag and drop operations: i.e., the ability to move selected text
by pressing down the left mouse button and dragging the text to a new location.
Center Bookmarks
Check this option so that when you jump to a bookmark it is centered in the editor window.
Block indent
The number of spaces used when a selected block is indented using <Ctrl+k+i> or unindented
using <Ctrl+k+u>.
Tab stops
This is a comma separated list of numbers which indicate the number of spaces per tab stop. If
only one number is entered, say “3,” then the first tab stop is 3 spaces, as is each additional tab
stop. Every additional number in the list indicates the number of spaces for all subsequent
Keymapping
The keyboard has five different default key mappings: Default, Classic, Brief, Epsilon and
Visual Studio. Change the keymapping with this pulldown menu.
Syntax extensions
Dynamic C will automatically syntax highlight the text in all files with the extensions listed
here. Syntax highlighting can also be enabled by right-clicking on an open file and selecting
the “Use Syntax Highlighting” menu item.
Editor margin
Check the Visible box to create a right-hand margin in the text window. Use the Width scroll
bar and the Color pulldown menu to set the like-named attributes of the margin line. The Style
pulldown menu displays the line choices available: a solid line and various dashed lines. The
Position scroll box is used to place the margin at the desire location in the text window.
Editor Font
This area of the dialog box is for choosing the font style and size. Check Use mono font for
fixed spacing with each character; note that this option limits the available font styles.
Special Symbols
Check the box labeled “Use” to view end of line, end of file, space and/or tab symbols in the
editor window.
Background Colors
This area of the dialog box is for choosing background colors for editor windows and the main
Dynamic C workspace. The editor window can have a different background color in edit mode
than it does in run mode. Each pulldown menu has an icon to the right that brings up addi-
tional color choices.
Element
In this text box are the different elements that may be in a file (strings, comments, integers,
etc.). For each one you may choose a foreground and a background color. You may also opt to
use the default colors: black for foreground and white for background. In the Text attribues
area of the dialog box, you may set Bold, Italic and/or Underline for the any of the elements.
As you can see, there are several predefined templates. The Edit and Delete buttons allow the like-
named operations on existing templates. The Add button gives the ability to create custom tem-
plates.
To bring up the list of defined templates, Dynamic C must be in edit mode. Then you must do one
of the following: press <Ctrl+j> or right click in the editor window and choose “Insert Code Tem-
plate” from the popup menu or choose the Edit command menu and select “Insert Code Tem-
plate.” Clicking on the desired template name inserts that template at the cursor location.
Under General Preferences is where you decide which debug windows will be opened after a suc-
cessful compile. You may choose one of the radio buttons in this category. Selecting “Open last
used windows” makes Dynamic C 8 act like Dynamic C 7.x.
Under Specific Preferences is where you customize each window. Colors and fonts are chosen
here, as well as other options.
Stdio Window
The previous screen shows the options available for the Stdio windowi. They are described
here. You may modify or check as many as you would like.
Starting with Dynamic C 9, the various Find commands available on the Edit menu can be
used directly in the Stdio window.
Show Addresses
Check this to show the logical address of the instruction in the far left column.
Show OpCodes
Check this to show the hexidecimal number corresponding to the opcode of the
instruction.
Show Clock Cycles
Check this to show the number of clock cycles needed to execute the instruction in the
far right column. Zero wait states is assumed. Two numbers are shown for conditional
return instructions. The first is the number of cycles if the return is executed, the sec-
ond is the number of cycles if the return is not executed.
Sum Clock Cycles
Check this to total the clock cycles for a block of instructions. The block of instruc-
tions must be selected and highlighted using the mouse. The total is displayed to the
right of the number of clock cycles of the last instruction in the block. This value
assumes one execution per instruction, so looping and branching must be considered
separately.
Use Syntax Highlighting
Toggle syntax highlighting. Click on the Syntax tab to set the different colors.
Show Source
Check this to display the Dynamic C statement corresponding to the assembly code.
A click of the right mouse button brings up the menu pictured above. Choosing
Change Register Value(s)... brings up a dialog where you can enter new values for any
of the registers, except SP, PC and XPC.
A click of the right mouse button on the register side will bring up the menu pictured
here. You can switch to history view or change register values for all but the SP, PC
and XPC registers.
The option New Register Value will
bring up a dialog to enter the new reg-
ister value. Hex values must have “0x”
prepended to the value. Values without
a leading “0x” are treated as decimal.
A click of the right mouse button on the flags side of the window will bring up a menu
that lets you toggle the selected flag (Ctrl+Alt+T) or switch to history view
(Ctrl+Alt+H).
The following are the options relevant to the Memory Dump window.
Apply changes to all
Changes made in this dialog will be applied to all memory dump windows.
Allow automatic updates
The memory dump window will be updated every time program execution stops
(breakpoint, single step, etc.). Starting with Dynamic C 9, each time you single step
changed data in the memory dump window is highlighted in reverse video.
Show tool bar
Each dump window has the option of a tool bar that has a button for updating the
dumped region and a text entry box to enter a new starting dump address.
Show address while scrolling
While using the scroll bar, a small popup box appears to the right of the scroll bar and
displays the address of the first byte in the window. This allows you to know exactly
where you are as you scroll.
Show current byte in hint
The address and value of the byte that is under the cursor is displayed in a small
popup box.
Show current byte in title bar
The address and value of the byte that is under the cursor is displayed in the title bar.
The Page Setup dialog works in conjunction with the Print/Alerts dialog. The Page Setup dialog is
where you define the attributes of headers, footers, page numbering and margins for the printed
page. The Print/Alerts dialog is where you enable and disable these settings. You may also change
the font family and font size that will be used by the printer. This does not apply to the fonts used
for headers and footers, those are defined in the Page Setup dialog.
There are four checkboxes in the Alerts area of this dialog. The first two signal a successful com-
pile and download, one with a visual signal, the other auditory. The third checkbox detects if a file
that is currently open in Dynamic C has been modified by an external source, i.e., a third-party
editor; and if checked, will bring up a dialog box asking if you want to reload the modified file so
Project Options
Settings used by Dynamic C to communicate with a target, and to compile and run programs are acces-
sible by using the Project Options dialog box. The dialog box has tabs for various aspects of communi-
cating with the target, the BIOS and the compiler.
Communications Tab
Connection Type
Choose either a serial connection or a TCP/IP connection.
Compiler Tab
Click on the Compiler tab to display the following dialog.
Type Checking
This menu item allows the following choices:
• Prototypes—Performs strict type checking of arguments of function calls against the
function prototype. The number of arguments passed must match the number of param-
eters in the prototype. In addition, the types of arguments must match those defined in
the prototype. Rabbit recommends prototype checking because it identifies likely run-
time problems. To use this feature fully, all functions should have prototypes (including
functions implemented in assembly).
• Demotion—Detects demotion. A demotion automatically converts the value of a larger
or more complex type to the value of a smaller or less complex type. The increasing
order of complexity of scalar types is:
char
unsigned int
int
unsigned long
long
float
A demotion deserves a warning because information may be lost in the conversion. For
example, when a long variable whose value is 0x10000 is converted to an int value,
the resulting value is 0. The high-order 16 bits are lost. An explicit type casting can
eliminate demotion warnings. All demotion warnings are considered non-serious as far
as warning reports are concerned.
• Pointer—Generates warnings if pointers to different types are intermixed without type
casting. While type casting has no effect in straightforward pointer assignments of dif-
ferent types, type casting does affect pointer arithmetic and pointer dereferences. All
pointer warnings are considered non-serious as far as warning reports are concerned.
Warning Reports
This tells the compiler whether to report all warnings, no warnings or serious warnings only. It
is advisable to let the compiler report all warnings because each warning is a potential run-
time bug. Demotions (such as converting a long to an int) are considered non-serious with
regard to warning reports.
List Files
Checking this option generates an assembly list file for each compile. A list file contains the
assembly code generated from the source file.
The list file is placed in the same directory as your program, with the name
<Program Name>.LST. The list file has the same format as the Disassembled Code win-
dow. Each C statement is followed by the generated assembly code. Each line of assembly
code is broken down into memory address, opcode, instruction and number of clock cycles.
See page 286 for a screen shot of the Disassembled Code window.
RabbitSys
This option was added in Dynamic C 9.30. Checking it allows you to compile a program to
run on top of RabbitSys. The target board must be RabbitSys-enabled, which means that it has
the necessary preloaded drivers and the RabbitSys firmware.
For more information about RabbitSys, see the RabbitSys User’s Manual.
Advanced... Button
Click on this button to reveal the Advanced Compiler Options dialog. The options are:
User Defined Lib Directory File (same as the command line compiler option “-lf”)
The Library Lookup information retrieved with <Ctrl+H> is parsed from the libraries
found in the “lib.dir” file, which is part of the Dynamic C installation. Checking the
Use box for User Defined Libraries File, allows the parsing of a user-defined replace-
ment for the “lib.dir” file. Library files must be listed in the “lib.dir” file (or its
replacement) to be available to a program.
If the function description headers are formatted correctly (See “Function Description
Headers” on page 44.), the functions in the libraries listed in the user-defined replace-
ment for the “lib.dir” file will be available with <Ctrl+H> just like the user-callable
functions that come with Dynamic C.
Watch Code
Allow any expressions in watch expressions
This option causes any compilation of a user program to pull in all the utility func-
tions used for expression evaluation.
Restricting watch expressions (May save root code space)
Choosing this option means only utility code already used in the application program
will be compiled.
Trace Level
Choose which events will be captured by the trace. Full tracing captures all debuggable state-
ments plus function entries and exits. If you don't want to include all statements, you can
choose to capture each function entry and exit only.
Dynamic C statements are debuggable by default, while assembly code is not. You can toggle
this with the debug and nodebug keywords for Dynamic C functions, and with the debug and
nodebug options of the #asm compiler directive for blocks of assembly code.
Syntax:
DEFINITION[DELIMETER DEFINITION[DELIMETER DEFINITION[...]]]
DEFINITION: MACRONAME[[WS]=[WS]VALUE]
DELIMETER: ';' or 'newline'
MACRONAME: the same as for a macro name in a source file
WS: [SPACE[SPACE[...]]]
VALUE: CHR[CHR[...]]
CHR: any character except the delimeter character ';', which is entered as the character pair
"\;"
Notes:
• Do not continue a definition in this window with '\', simply continue typing as a long line will
wrap.
• In this window hitting the Tab key will not enter a tab character (\t), but will tab to the OK
button.
• The command line compiler honors all macros defined in the project file that it is directed to
use with the project file switch, -pf, or default.dcp if -pf is not used. See command line
compiler documentation.
• A macro redefined on the command line will supersede the definition read from the project file.
RTI File
Click on this tab to open a Rabbit Target Information (RTI) file for viewing. The file is read-
only. You may not edit RTI files, but you may create one by selecting an entry in the Board
Selection list and clicking on the button Save as RTI. Or you may define a board configuration
in the Specify Parameters dialog and then save the information in an RTI file. Details follow.
Specify Parameters
This is where you may define the parameters of a controller for later use in targetless compila-
tions.
Board Selection
The list of board configurations is viewable from the Board Selection tab. The highlighted
entry in the list of board configurations is the one that will be used when the compilation uses
a defined target configuration, that is, when the Default Compile Mode on the Compiler tab is
set to “Compile defined target configuration to .bin file” and Compile or Compile to .bin file is
chosen from the Compile menu.
If you save to the list of board configurations by clicking on the button Update Board Selec-
tion, then you must fill in all fields of the dialog. The baud rate, calculated from the value in
the Base Frequency (MHz) field, only applies to debugging. The fastest baud rate for down-
loading is negotiated between the PC and the target.
To save to an RTI file only requires an entry in the CPU field. Please see Technical Note 231
for information on the specifics of the Rabbit CPU revisions.
The correct choice for the CPU field is found on the chip itself. The information is printed on
the third line from the top on the Rabbit 2000 and the second line from the top on the Rabbit
3000. The table below lists the possible values.
Rabbit Microprocessor non-RoHS RoHS
Where “#” is the revision number and the letters are associated information.
Watch
Select Watch to activate or deactivate the Watches window. The Add Watch command on the Inspect
menu will do this too. The Watches window displays watch expressions whenever Dynamic C evalu-
ates watch expressions. Starting with Dynamic C 9, a watch expression for a structure will automati-
cally include all members of the structure. Previous versions of Dynamic C required each struct
member to be added as a separate watch expression.
Keep in mind that when single stepping in assembly, the value of the watch expression may not be
valid for variables located on the stack (all auto variables). This is because the debug kernel does not
keep track of the pushes and pops that occur on the stack, and since watches of stack variables only
make sense in the context of the pushes and pops that have happened, they will not always be accurate
when assembly code is being single stepped.
Assembly (F10)
Select this option to activate or deactivate to activate or deactivate the Disassembled Code window.
The Disassembled Code window (aka., the Assembly window) displays machine code generated by the
compiler in assembly language format.
The Disassemble at Cursor or Disassemble at Address commands from the Inspect menu also acti-
vate the Disassembled Code window.
The Disassembled Code window displays Dynamic C statements followed by the assembly instruc-
tions for that statement. Each instruction is represented by the memory address on the far left, followed
by the opcode bytes, followed by the mnemonics for the instruction. The last column shows the num-
ber of cycles for the instruction, assuming no wait states. The total cycle time for a block of instruc-
tions will be shown at the lowest row in the block in the cycle-time column, if that block is selected and
highlighted with the mouse. The total assumes one execution per instruction, so the user must take
looping and branching into consideration when evaluating execution times.
Copy
Copies selected text in the Disassembled
Code window to the clipboard.
Save to File
Opens the Save As dialog to save text
selected in the Disassembled Code win-
dow to a file. If you do not specify an
extension, .dasm will be appended to the
file name.
Move to Address
Opens the Disassemble at Address dialog
so you can enter a new address.
Move to Execution Point
Highlights the assembly instruction that will execute next and displays it in the Disas-
sembled Code window.
Select ALL
Selects all text in the Disassembled Code window.
All but the last menu option of the remaining items in the popup menu toggle what is displayed in the
Disassembled Code window. The last menu option, Use Syntax Highlighting, displays the colors that
were set for the editor window in the Disassembled Code window.
To resize a column in the assembly window, move the mouse pointer to one of the vertical bars that is
between each of the column headers. For instance, if you move the mouse pointer between “Address”
and “Opcode” the pointer will change from an arrow to a vertical bar with arrows pointing to the right
and left. Hold the left mouse button down and drag to the right or left to grow or shrink the column.
Register (F11)
Select this option to activate or deactivate the Register window. This window displays the processor
register set, including the status register. Letter codes indicate the bits of the status register (also known
as the flags register). The window also shows the source-code line and column at which the snapshot of
the register was taken.
It is possible to scroll back to see the progression of successive register snapshots. Register values may
be changed when program execution is stopped Registers PC, XPC, and SP may not be edited as this
can adversely affect program flow and debugging.
See “Register Window” on page 264 for more details on this window.
The Trace window has a right-click pop-up menu. An option on this menu controls the display of an
additional column in the Trace window. If Group repeated statements is selected, the Show Repeat
Count may also be selected and will display in the rightmost column of the Trace window that comes
before the register contents column. A value displayed under Show Repeat Count is the number of
times the corresponding statement has been executed and, therefore, traced. The Timestamp column is
not updated for subsequent traces of a repeated statement.
The Group repeated statements option is useful when tracing statements inside a loop.
The rest of the pop-up menu options are more or less self-explanatory. You can choose to open the
source code for any function in the Trace window by selecting the function and choosing Open Source.
In the above screenshots, note that a trace statement for kbhit() is selected in the Trace window.
Choosing Open Source in this situation would open a window for STDIO.LIB, the library file that
contains the function kbhit().
You can also toggle auto scroll, as well as decide whether to display the complete path in the File Name
column. The last three menu options are for saving Trace window contents to another file. You can
select trace statements in the window and then using Copy selected traces or Copy with header you can
paste the selected traces anywhere you can perform a paste operation. You can also choose to copy the
entire contents of the current Trace window to a named file. This is similar to the option in the Debug-
Information
Select this option to activate the Information window, which displays how the memory is partitioned
and how well the compilation went.
The begin (base), end (top) and size of the root code area,
Root code
expressed in logical address format (16-bit).
The begin, end and size of the XMEM code area, expressed in
XMEM code
physical address format (20-bit).
The begin, end and size of the watch code area, expressed in
Watch code
logical address format (16-bit).
The begin, end and size of the run-time stack, expressed in logical
Stack
address format (16-bit).
The begin, end and size of the root data area, expressed in logical
Root data
address format (16-bit).
The begin, end and size of the root constant area, expressed in
Root constants
physical address format (20-bit).
The number of code bytes (including both root and XMEM code
Total code size
areas.
The number of data bytes (including both root and XMEM data
Total data size
areas
Lines compiled The number of lines compiled, including lines from library files.
Compile time The number of seconds taken to compile the program.
Average speed of compilation measured in lines compiled per
Compile speed
minute.
A number identifying the board type. A list of board types is at
Board ID
\Lib\default.h.
Note that some of the memory areas described here may be non-contiguous (e.g., 2 Flash compiles and the
XMEM code area with separate I&D). If an application is large enough to span into the non-contiguous
part of an area, the values presented in the Information window for that area are not accurate.
Online Documentation
Opens a browser page and displays a file with links to other manuals. When installing Dynamic C from
CD, this menu item points to the hard disk; after a Web upgrade of Dynamic C, this menu item option-
ally points to the Web.
Keywords
Opens a browser page and displays an HTML file of Dynamic C keywords, with links to their descrip-
tions in this manual.
Operators
Opens a browser page and displays an HTML file of Dynamic C operators, with links to their descrip-
tions in this manual.
Choosing a function is done in one of several ways. You may type the function name in the Function
Search entry box. Notice how both scroll areas underneath the entry box display the first function that
matches what you type. The functions to the left are listed alphabetically, while those on the right are
arranged in a tree format, displaying the libraries alphabetically with their functions collapsed under-
neath. You may scroll either of these two areas and have whatever you select in one area reflected in
the other area and in the text entry box. Click OK or press <Enter> to bring up the Function Descrip-
tion window.
If you click the Edit button, the Function Description window will close and the library that contains
the function that was in the window will open in an editor window. The cursor will be placed at the
function description of interest.
Clicking on the Browse button will open the Library Function Lookup window to allow you to search
for a new function description. Multiple Function Description windows may be open at the same time.
I/O Registers
Invokes an on-line help system that provides the bit values for all of the Rabbit I/O registers.
Keystrokes
Invokes an on-line help system and displays the keystrokes page. Although a mouse or other pointing
device may be convenient, Dynamic C also supports operation entirely from the keyboard.
Contents
Invokes an on-line help system and displays the contents page. From here view explanations of various
features of Dynamic C.
Tech Support
Opens a browser window to the Rabbit Technical Support Center web page, which contains links to
user forums, downloads for Dynamic C and information about 3rd party software vendors and devel-
opers.
Register Dynamic C
Allows you to register your copy of Dynamic C. A dialog is opened for entering your Dynamic C serial
number. From there you will be guided through the very quick registration process.
About
The About command displays the Dynamic C version number and the registered serial number.
gives a summary of switches with defaults from the default project file, default.dcp, and
dccl_cmp -pf specified_project_name.dcp
gives a summary of switches with defaults from the specified project file. All project options including the
default compile mode can be overridden with the switches described in Section 15.4.
where myProgramInputs.txt is a text file containing the inputs as separate lines, in the order in
which myProgram.c expects them.
-b
Description: Use compile mode: Compile to .bin file using attached target.
Factory Default: Compile mode: Compile to attached target.
GUI Equivalent: Compile program (F5) with Default Compile Mode set to "Compile to .bin file
using attached target" in Compiler tab of Project Options dialog.
-bf-
-br
Description: Use compile mode: Compile defined target configuration to .bin file
Factory Default: Compile mode: Compile to attached target.
GUI Equivalent: Compile program (F5) with Default Compile Mode set to "Compile defined target
configuration to .bin file" in Compiler tab of Project Options dialog.
-h-
-id+
-id-
-ini
-lf-
-mf
-mfr
Description: The BIOS and code are compiled to flash, and then the BIOS copies the flash image
to RAM to run the code.
Factory Default: Memory BIOS setting: Flash
GUI Equivalent: Select “Code and BIOS in Flash, Run in RAM” in the Project Options | Compiler
dialog box.
-mr
GUI Equivalent: Select “Code and BIOS in RAM” in the Project Options | Compiler dialog box.
Description: Null compile for errors and warnings without running the program. The program
will be downloaded to the target.
Factory Default: Program is run.
GUI Equivalent: Select Compile | Compile or use the keyboard shortcut <F5>.
-r
-rb+
-rb-
-rd+
Description: Do not include debug code when compiling to a file. This option is ignored if not
compiling to a file.
Factory Default: RST 28 instructions are included.
GUI Equivalent: This is an advanced setting, viewable by clicking on the “Advanced” radio button at
the bottom of the Project Options | Compiler dialog box. Uncheck “Include RST 28
instructions.”
-ri+
-ri-
-rp+
-rp-
-rw-
-sp
-sz
-td+
-tp+
-tp-
-tt+
-tt-
Description: Verify the processor by enabling a DSR check. This should be disabled if a check of
the DSR line is incompatible on your system for any reason.
Factory Default: Processor verification is enabled.
GUI Equivalent: Check “Enable Processor verification” in the Project Options | Communications
dialog box.
-vp-
-wa
-wn
-ws
-bf BIOSFilePathname
GUI Equivalent: This is an advanced setting, viewable by clicking on the “Advanced” radio button at
the bottom of the Project Options | Compiler dialog box. Check the box under “User
Defined BIOS File” and then fill in the pathname for the new BIOS file.
Example: dccl_cmp myProgram.c -bf MyPath\MyBIOS.lib
-clf ColdLoaderFilePathname
Description: Define macros and optionally equate to values. The following rules apply
and are shown here with examples and equivalent #define form:
Separate macros with semicolons.
dccl_cmp myProgram.c -d DEF1;DEF2
#define DEF1
#define DEF2
Description: Undefines a macro that might have been defined in the project file. If a
macro is defined in the project file read by the command line compiler and
the same macro name is redefined on the command line, the command line
definition will generate a warning. A macro previously defined must be
undefined with the -d- switch before redefining it. Undefining a macro that
has not been defined has no consequence and so is always safe although
possibly unnecessary. In the example, all compilation settings are taken
from the project file specified except that now the macro MAXCHARS was
first undefined before being redefined.
Factory Default: None.
GUI Equivalent: None.
Example: dccl_cmp myProgram.c -pf myproject -d- MAXCHARS -d MAX-
CHARS=512
-eto EthernetResponseTimeout
Description: Time in milliseconds Dynamic C waits for a response from the target on
any retry while trying to establish Ethernet communication.
Factory Default: 8000 milliseconds.
GUI Equivalent: None.
Example: dccl_cmp myProgram.c -eto 6000
-i InputsFilePathname
Description: Execute a program that requires user input by supplying the input in a text
file. Each input required should be entered into the text file exactly as it
would be when entered into the Stdio Window in dcwd.exe. Extra input
is ignored and missing input causes dccl_cmp to wait for keyboard input
at the command line.
Factory Default: None.
GUI Equivalent: Using -i is like entering inputs into the Stdio Window.
Example dccl_cmp myProgram.c -i MyInputs.txt
Description: Compile using a file found in LibrariesFilePathname which lists all libraries
to be made available to your programs.
Factory Default: Lib.dir.
GUI Equivalent: This is an advanced setting, viewable by clicking on the “Advanced” radio
button at the bottom of the Project Options | Compiler dialog box. Check
the box under “User Defined Lib Directory File” and then fill in the path-
name for the new Lib.dir.
Example dccl_cmp myProgram.c -lf MyPath\MyLibs.txt
-ne maxNumberOfErrors
-nw maxNumberOfWarnings
Description: Write header information (if specified with -h) and all program errors,
warnings and outputs to a text file. If the text file does not exist it will be
created, otherwise it will be overwritten.
Factory Default: None.
GUI Equivalent: Go to Option | Environment Options and select the Debug Windows tab.
Under “Specific Preferences” select “Stdio” and check “Log to File” under
“Options.”
Example dccl_cmp myProgram.c -o MyOutput.txt
dccl_cmp myProgram.c -o MyOutput.txt -h
dccl_cmp myProgram.c -h -o MyOutput.txt
-oa OutputFilePathname
Description: Append header information (if specified with -h) and all program errors,
warnings and outputs to a text file. If the text file does not exist it will be
created, otherwise it will be appended.
Factory Default: None.
GUI Equivalent: Go to Option | Environment Options and select the Debug Windows tab.
Under “Specific Preferences” select “Stdio” and check “Log to File” under
“Options,” then check “Append” and specify the filename.
Example dccl_cmp myProgram.c -oa MyOutput.txt
-pbf PilotBIOSFilePathname
Description: Specify a project file to read before the command line switches are read.
The environment settings are taken from the project file specified with -pf,
or default.dcp if no other project file is specified. Any switches on the
command line, regardless of their position relative to the -pf switch, will
override the settings from the project file.
Factory Default: The project file default.dcp.
GUI Equivalent: Select File | Project | Open...
Example dccl_cmp myProgram.c -ne 25 -pf myProject.dcp
dccl_cmp myProgram.c -ne 25 -pf myProject
Note: The project file extension, .dcp, may be omitted.
-pw TCPPassPhrase
Description: Enter the passphrase required for your TCP/IP connection. If no passphrase
is required this option need not be used.
Factory Default: No passphrase.
GUI Equivalent: Enter the passphrase required at the dialog prompt when compiling over a
TCP/IP connection
Example: dccl_cmp myProgram.c -pw “My passphrase”
-ret Retries
Description: Compile to a .bin file using targetless compilation parameters found in RTIFilePath-
name. The resulting compiled file will have the same pathname as the source (.c)
file being compiled, but with a .bin extension.
Factory Default: None.
GUI Equivalent:
Example: dccl_cmp myProgram.c -rf MyTCparameters.rti
dccl_cmp myProgram.c -rf “My Long Pathname\MyTCparameters.rti”
ters.rti”
-rti BoardID:CpuID:CrystalSpeed:RAMSize:FlashSize
Description: Compile to a .bin file using parameters defined in a colon separated for-
mat of BoardID:CpuID:CrystalSpeed:RAMSize:FlashSize. The resulting
compiled file will have the same pathname as the source (.c) file being
compiled, but with a .bin extension.
Description: Use serial transmission with parameters defined in a colon separated format
of Port:Baud:Stopbits:BackgroundTx.
Port: 1, 2, 3, 4, 5, 6, 7, 8
Baud: 110, 150, 300, 600, 1200, 2400, 4800, 9600, 12800, 14400,
19200, 28800, 38400, 57600, 115200, 128000, 230400, 256000
Stopbits: 1, 2
Include all serial parameters in the prescribed format even if only one is
being changed.
Factory Default: 1:115200:1:0
GUI Equivalent: Select the Communications tab of Project Options. Select the “Use Serial
Connection” radio button.
Example: Changing port from default of 1 to 2:
dccl_cmp myProgram.c -s 2:115200:1:0
-sto SerialResponseTimeout
Description: Time in milliseconds Dynamic C waits for a response from the target on
any retry while trying to establish serial communication.
Factory Default: 300 ms.
GUI Equivalent: None.
Example: dccl_cmp myProgram.c -sto 400
Description: Use TCP with parameters defined in a contiguous colon separated format of
NetAddress:TcpName:TcpPort. Include all parameters even if only one is
being changed.
netAddress: n.n.n.n
tcpName: Text name of TCP port
tcpPort: decimal number of TCP port
Factory Default: None.
GUI Equivalent: Select the Communications tab of Project Options. Select the “Use TCP/IP
Connection” radio button.
Example: dccl_cmp myProgram.c -t 10.10.6.138:TCPName:4244
15.5 Examples
The following examples illustrate using multiple command line switches at the same time. If the switches
on the command line are contradictory, such as -mr and -mf, the last switch (read left to right) will be
used.
Example 1
In this example, all current settings of default.dcp are used for the compile.
dccl_cmp samples\timerb\timerb.c
Example 2
In this example, all settings of myproject.dcp are used, except timer_b.c is compiled to
timer_b.bin instead of to the target and warnings or errors are written to myouputs.txt.
dccl_cmp samples\timerb\timer_b.c -o myoutputs.txt -b -pf myproject
The first run could have used the -oa option if myOutput.txt were known to not initially exist.
myProgram.c presumably uses a constant MAXCOUNT and contains one or more compiler directives
that react to whether or not DEF1 is defined.
where SourceFilePathName is the path name of the .bin file to load to the connected target. The
options are as follows:
-cl ColdLoaderPathName
Description: Select a new file that Dynamic C will use to externally define flash.
Default: flash.ini
RFU GUI From the “Choose File Locations...” dialog box, visible by selecting Setup |
Equivalent: File Locations, type in a pathname or click on the ellipses radio button to
browse for a file.
Example: clRFU myProgram.bin -fi myflash.ini
-pb PilotBiosPathName
-pw
Description: Select the comm port and baud rate for the serial connection.
Default: COM1 and 115,200 bps
RFU GUI From the Setup | Communications dialog box, choose values from the Baud
Equivalent: Rate and Comm Port drop-down menus.
Example: clRFU myProgram.bin -s 2:115200
-t ipAddress:tcpPort
-v
Description: Causes the RFU version number and additional status information to be dis-
played.
Default: Only error messages are displayed.
RFU GUI Status information is displayed by default and there is no option to turn it
Equivalent: off.
Example: clRFU myProgram.bin -v
-vp+
Description: Verify the presence of the processor by using the DSR line of the PC serial
connection.
Default: The processor is verified.
RFU GUI From the “Communications Options” dialog box, visible by selecting Setup
Equivalent: | Communications, check the “Enable Processor Detection” option.
Example: clRFU myProgram.bin -vp+
-usb+
-usb-
16.1.1 Factory.dcp
The environment originally shipped from the factory is kept in a project file named factory.dcp. If
Dynamic C cannot find this file, it will be recreated automatically in the Dynamic C exe path. The factory
project can be opened at any time and the environment changed and saved to another project name, but
factory.dcp will not be changed by Dynamic C.
16.1.2 Default.dcp
This default project file is originally a copy of factory.dcp and will be automatically recreated as such
in the exe path if it cannot be found when Dynamic C opens. The default project will automatically
become the active project with File | Project... | Close.
The default project is special in that the command line compiler will use it for default values unless
another project file is specified with the -pf switch, in which case the settings from the indicated project
will be used.
Please see Chapter 15 for more details on using the command line compiler.
i. If DC is started with a cwd (current working directory) other than the exe directory, the cwd will be used
instead of the one saved in the project file. This can happen if Dynamic C is started from a Windows
shortcut with a specified “starts in” directory.
Choosing File | Project | Open... will bring up a dialog box to select an existing project filename to
become the active project. The environment of the previous project is saved to its project file before it is
replaced (unless the previous project is factory.dcp). The BIOS will automatically be recompiled
prior to the compilation of a source file within the new environment, which may have a different library
directory file and/or a different BIOS file.
Choosing File | Project... | Save will save the state of the environment to the active project file, including
the state of the recently used filelist and any files open in edit windows. This selection is greyed out if the
active project is factory.dcp. This option is of limited use since any project changes will be updated
immediately to the file and the state of the recently used filelist and open edit windows will be updated
when the project is closed for any reason.
Choosing File | Project... | Save as... will bring up a dialog box to select a project file name. The file will
be created or, if it exists, it will be overwritten with the current environment settings. This environment
will also be saved to the active project file before it is closed and its copy (the newly created or overwritten
project file) will become active.
Choosing File | Project... | Close first saves the environment to the active project file (unless the active
project is factory.dcp) and then loads the Dynamic C default project, default.dcp, as the active
project. As with Open..., the BIOS will automatically be recompiled prior to the compilation of a source
file within the new environment. The new environment may have a different library directory file and/or a
different BIOS file.
shows the current state of all myProject.dcp settings. And the command:
dccl_cmp myProgram.c -ne 25 -pf myProject
reads myProject.dcp, then compiles and runs myProgram.c, showing a maximum of 25 errors.
The command line compiler, unlike Dynamic C, never updates the project file it uses. Any changes desired
to a project file to be used by the command line compiler can be made within Dynamic C or changed by
hand with an editor.
Making changes by hand should be done with caution. Use an editor that does not introduce carriage
returns or line feeds with wordwrap, which may be a problem if the global defines or any file pathnames
are lengthy strings. Be careful to not change any of the section names in brackets or any of the key phrases
up to and including the “=.”
If a macro is defined on the command line with the -d switch, any value that may have been defined within
the project file used will be overwritten without warning or error. Undefining a macro with the -d- switch
has no consequence if it was not previously defined.
To create a user-defined BIOS prior to Dynamic C 9.30, begin with a copy of RABBITBIOS.C. Starting
with Dynamic C 9.30, begin with a copy of STDBIOS.C. Modify the BIOS file. It is prudent to save
RABBITBIOS.C or STDBIOS.C as is and rename the modified file.
The Dynamic C GUI offers an option for hooking a user-defined BIOS into the system. See the description
of the “Advanced... Button” in Section 14.2.7 for details on using this GUI option. Prior to Dynamic C
9.30, this GUI option was the easiest way to accomplish the goal. If you are using Dynamic C 9.30 or later
and if you use the GUI option to hook in your BIOS, you will need to consider the configuration files and
associated macros described in Table 17-1.
To select the customized BIOS, define “MYBIOS = 1” in the Defines tab of the Options | Project Options
dialog box.
17.2 Efficiency
There are a number of methods that can be used to reduce the size of a program, or to increase its speed.
Let’s look at the events that occur when a program enters a function.
• The function saves IX on the stack and makes IX the stack frame reference pointer (if the program is in
the useix mode).
• The function creates stack space for auto variables.
• The function sets up stack corruption checks if stack checking is enabled (on).
• The program notifies Dynamic C of the entry to the function so that single stepping modes can be
resolved (if in debug mode).
The last two consume significant execution time and are eliminated when stack checking is disabled or if
the debug mode is off.
if there are.
See the Rabbit designer’s handbook for more details about the User block.
17.3.3 WriteFlash2
See the Dynamic C Function Reference Manual for a complete description.
NOTE: There is a WriteFlash() function available for writing to the first flash,
but its use is highly discouraged for reasons of forward source and binary compatibil-
ity should flash sector configuration change drastically in a product. For more infor-
mation on flash compatibility issues, see technical notes TN216 “Is your Application
Ready for Large Sector Flash?” and TN217 “Binary and Source Compatibility Issues
for 4K Flash Sector Sizes” at Rabbit’s website: rabbit.com..
• Increase DATAORG
The macro DATAORG is the beginning logical address for the data segment.
Root code space can be increased by increasing DATAORG in the BIOS (in RabbitBios.c prior to
Dynamic C version 9.30 or in StdBIOS.c thereafter) in increments of 0x1000. The default is 0x3000
when separate I&D space is on, and 0x6000 otherwise. It can be as high as 0xB000.
When separate I&D space is on, DATAORG defines the boundary between root variable data and root
constant data. In this case, increasing DATAORG increases root constant space and decreases root vari-
able space.
When separate I&D space is off, DATAORG defines the boundary between root variable data and the
combination of root code and root constant data. Note that root constants are in the base segment with
root code. In this case, increasing DATAORG increases root code and root constant space and decreases
root data space.
Both methods, const and xstring, create initialized data in flash at compile time, so the data cannot
be rewritten directly.
• Decrease DATAORG
The macro DATAORG is the beginning logical address for the data segment.
Root data space can be increased by decreasing DATAORG in the BIOS (in RabbitBios.c prior to
Dynamic C version 9.30 or in StdBIOS.c thereafter) in increments of 0x1000. At the time of this
writing, RAM compiles should be done with no less than the default value (0x6000) of DATAORG when
separate I&D space is off. This restriction is to ensure that the pilot BIOS does not overwrite itself.
When separate I&D space is on, DATAORG defines the boundary between root variable data and root
constant data. In this case, decreasing DATAORG increases root variable space and descreases root con-
stant space.
When separate I&D space is off, DATAORG defines the boundary between root variable data and the
combination of root code and root constant data. Note that root constants are in the base segment with
root code. In this case, decreasing DATAORG increases root data space and decreases root code space.
This is the debug baud rate. The baud rate can be changed in the
_BIOSBAUD_
Communications tab of Project Options.
This is read from the System ID block or defaulted to 0x100 (the
BL1810 JackRabbit board) if no System ID block is present. This can be
_BOARD_TYPE_
used for conditional compilation based on board type. Board types are
listed in boardtypes.lib.
This macro identifies the CPU type, including its revision; e.g.,
#if _CPU_ID_ >= R3000_R1
_CPU_ID_ will identify a Rabbit 3000 rev. 1 or newer chip
Look in \Lib\..\BIOSLIB\sysiodefs.lib for the constants
and mask macros that are defined for use with _CPU_ID_.
CC_VER Gives the Dynamic C version in hex, i.e., version 7.05 is 0x0705.
DC_CRC_PTR Reserved.
The compiler substitutes this macro with the date that the file was
compiled (either the BIOS or the .c file). The character string literal is of
the form Mmm dd yyyy. The days of the month are as follows: "Jan,"
__DATE__
"Feb," "Mar," "Apr," "May," "Jun," "Jul," "Aug," "Sep," "Oct," "Nov,"
"Dec." There is a space as the first character of dd if the value is less than
10.
Go to the Compiler tab of Project Options and click on the “Advanced”
button at the bottom of the dialog box. Check “Include RST 28
DEBUG_RST
instructions” to set DEBUG_RST to 1. Debug code will be included
even if #nodebug precedes the main function in the program.
The compiler substitutes this macro with the current source code file
__FILE__
name as a character string literal.
_FAST_RAM_ These are used for conditional compilation of the BIOS to distinguish
between the three options:
_FLASH_ • compiling to and running in flash
• compiling to and running in RAM
• compiling to flash and running in RAM
The choice is made in the Compiler tab of Project Options. The default
is compiling to and running in flash.
The BIOS defines FAST_RAM_COMPILE, FLASH_COMPILE and
RAM_COMPILE. These macros are defined to 0 or 1 as opposed to the
corresponding compiler-defined macros which are either defined or not
_RAM_ defined. This difference makes possible statements such as:
#if FLASH_COMPILE || FAST_RAM_COMPILE
330 rabbit.com
A.2 Macros Defined in the BIOS or Configuration Libraries
This is not a comprehensive list of configuration macros, but rather, a short list of those found to be com-
monly used by Dynamic C programmers. Most default conditions can be overridden by defining the macro
in the “Defines” tab of the “Project Options” dialog.
All the configuration macros listed here were defined in RabbitBIOS.c prior to Dynamic C 9.30. Since
then many of them have been moved to configuration libraries while RabbitBIOS.c has become a
wrapper file that permits a choice of which BIOS to compile. See Section 17.1 for more information on the
reorganization of the BIOS that occured with Dynamic C 9.30.
CLOCK_DOUBLED
Determines whether or not to use the clock doubler. The default condition is to use the clock doubler,
defined in \BIOSLIB\sysconfig.lib. Override the default condition by defining
CLOCK_DOUBLED to “0” in an application or in the project.
DATAORG
Defines the beginning logical address for the data segment. Defaults are defined in the BIOS: 0x3000 if
separate I&D space enabled, 0x6000 otherwise. Users can override the defaults in the Defines tab of
Project Options dialog.
WATCHCODESIZE
Specifies the number of root RAM bytes for watch code. Defaults are defined in the BIOS: 0x200 bytes if
watch expressions are enabled, zero bytes otherwise. The defaults cannot be overridden by an application.
USE_TIMERA_PRESCALE
Uncomment this macro in \BIOSLIB\sysconfig.c to run the peripheral clock at the same frequency
as the CPU clock instead of the standard “CPU clock/2.” This feature is not compatible with the Rabbit
2000.
USE_2NDFLASH_CODE
Uncomment this macro in \BIOSLIB\sysconfig.c only if you have a a board with two 256K
flashes, and you want to use the second flash for extra code space. The file system (FS2) is not compatible
with using the second flash for code.
dc_timestamp
This internally-defined long is the number of seconds that have passed since 00:00:00 January 1, 1980,
Greenwich Mean Time (GMT) adjusted by the current time zone and daylight savings of the PC on which
the program was compiled. The recorded time indicates when the program finished compiling. The follow-
ing program will use dc_timestamp to help calculate the date and time.
printf("The date and time: %lx\n", dc_timestamp);
main(){
struct tm t;
printf("dc_timestamp = %lx\n", dc_timestamp);
mktm(&t, dc_timestamp);
printf("%2d/%02d/%4d %02d:%02d:%02d\n",
t.tm_mon,t.tm_mday,t.tm_year + 1900, t.tm_hour,t.tm_min,
t.tm_sec);
}
OPMODE
This is a char. It can have the following values:
• 0x88 = debug mode
• 0x80 = run mode
SEC_TIMER
This unsigned long variable is initialized to the value of the real-time clock (RTC). If the RTC is set cor-
rectly, this is the number of seconds that have elapsed since the reference date of January 1, 1980. The
periodic interrupt updates SEC_TIMER every second. This variable is initialized by the Virtual Driver
when a program starts.
MS_TIMER
This unsigned long variable is initialized to zero. The periodic interrupt updates MS_TIMER every milli-
second. This variable is initialized by the Virtual Driver when a program starts.
TICK_TIMER
This unsigned long variable is initialized to zero. The periodic interrupt updates TICK_TIMER 1024
times per second. This variable is initialized by the Virtual Driver when a program starts.
332 rabbit.com
A.4 Exception Types
These macros are defined in errors.lib:
B.1 Grammar
<mapfile>: <memmap section> <function section> <global section>
<memmap section>: <memmapreg>+
<memmapreg>: <register var> = <8-bit const>
<register var>: XPC|SEGSIZE|DATASEG
<function section>: <function descripton>+
<function description>: <identifier> <address> <size>
<address>: <logical address> | <physical address>
<logical address>: <16-bit constant>
<physical address: <8-bit constant>:<16-bit constant>
<size>: <20-bit constant>
<global section>: <global description>+
<global description>: <scoped name> <address>
<scoped name>: <global>| <local static>
<global>: <identifier>
<local static>: <identifier>:<identifier>
Comments are C++ style (// only).
C.1.1 AES
Advanced Encryption Standard (AES) is an implementation of the Rijndael Advanced Encryption Stan-
dard cipher with 128 bit key. This is useful for encrypting sensitive data to be sent over unsecured network
paths.
C.1.2 SSL
Secure Sockets Layer (SSL) is a security protocol that transforms a typical reliable transport protocol
(such as TCP) into a secure communications channel for conducting sensitive transactions. The SSL proto-
col defines the methods by which a secure communications channel can be established—it does not indi-
cate which cryptographic algorithms to use. SSL supports many different algorithms, and serves as a
framework whereby cryptography can be used in a convenient and distributed manner.
Complete instructions are available by clicking on the Help button in the upper righthand corner of the
program window. Context-sensitive help is accessed by positioning the cursor over the desired subject and
then pressing <F1>.
The list of files to be encrypted may be edited if desired. Notice that if anything is entered in the lower
window, a new button named “Encrypt” appears. Two entries in the window change it to “Encrypt All”.
Clicking this button causes the utility to encrypt the file(s) listed in the lower window.
338 rabbit.com
C.2.1.2 File Extension
Encrypted files will be saved with the same pathname but with the extension supplied. Dynamic C will use
encrypted and non-encrypted files seamlessly, so the choice of extension is for one’s own file manage-
ment.
The remaining 4 functions are included for compression support for FS2 files:
OpenOutputCompressedFile() - open FS2 file for use with CompressFile().
CloseOutputCompressedFile() - close file and deallocate memory buffers.
CompressFile() - compress an FS2 file, placing the result in a second FS2 file.
DecompressFile() - decompress an FS2 file, placing the result in a second FS2 file.
Complete descriptions are available for these functions in the Dynamic C Function Reference Manual and
also via the Function Lookup facility (Ctrl+H or Help menu).
There are several macros associated with the file compression utility:
• ZIMPORT_MASK - Used to determine if the imported file is compressed (#zimport) or not
(#ximport).
• OUTPUT_COMPRESSION_BUFFERS (default = 0) - Number of 24K buffers for compression (com-
pression also requires a 4K input buffer, which is allocated automatically for each output buffer that is
defined).
• INPUT_COMPRESSION_BUFFERS (default = 1) Number of 4KB internal buffers (in RAM) used for
decompression.
Each compressed file has an associated file descriptor of type ZFILE. All fields in this structure are used
internally and must not be changed by an application program.
To replace Zcompress.exe as the utility used by Dynamic C for compression, open your project file
and edit the filename.
340 rabbit.com
The compression utility must reside in the same directory as the Dynamic C compiler executable. Dynamic
C expects the program to behave as follows:
• Take as input a file name relative to the Dynamic C installation directory or a fully qualified path.
• Produce an output file of the same name as the input file with the extension .DCZ at the end. E.g.,
test.txt becomes test.txt.dcz.
• Exit with zero on success, non-zero on failure.
If the utility does not meet these criteria, or does not exist, a compile-time error will be generated.
or by cut and pasting from name_me.lib directly into the application file.
The RFU enables those without Dynamic C to update their Rabbit-based board with a few files installed on
the computer and the appropriate connection to the target board.
where SourceFilePathName is the path name of the .bin file to load to the connected target. The
options are as follows:
-s port:baudrate
Description: Select the comm port and baud rate for the serial connection.
Default: COM1 and 115,200 bps
RFU GUI From the Setup | Communications dialog box, choose values from the Baud
Equivalent: Rate and Comm Port drop-down menus.
Example: clRFU myProgram.bin -s 2:115200
342 rabbit.com
-t ipAddress:tcpPort
-v
Description: Causes the RFU version number and additional status information to be dis-
played.
Default: Only error messages are displayed.
RFU GUI Status information is displayed by default and there is no option to turn it
Equivalent: off.
Example: clRFU myProgram.bin -v
-cl ColdLoaderPathName
-pb PilotBiosPathName
Description: Select a new file that Dynamic C will use to externally define flash.
Default: flash.ini
RFU GUI From the “Choose File Locations...” dialog box, visible by selecting the
Equivalent: menu option Setup | File Locations, type in a pathname or click on the
ellipses radio button to browse for a file.
Example: clRFU myProgram.bin -fi myflash.ini
-vp+
Description: Verify the presence of the processor by using the DSR line of the PC serial
connection.
Default: The processor is verified.
RFU GUI From the “Communications Options” dialog box, visible by selecting
Equivalent: Setup | Communications, check the “Enable Processor Detection” option.
Example: clRFU myProgram.bin -vp+
-vp-
-usb+
344 rabbit.com
-usb-
-d
Description: Run Ethernet discovery. Don’t load the .bin file. This option is for infor-
mation gathering and must appear by itself with no other options and no
binary image file name.
RFU GUI From the Setup | Communications dialog box, click on the “Use TCP/IP
Equivalent: Connection” radio button, then on the “Discover” button.
Example: clRFU -d
350 rabbit.com
6. Disclaimer of Warranty. You expressly acknowledge and agree that the use of the Software and its
documentation is at Your sole risk. THE SOFTWARE, DOCUMENTATION, AND TECHNICAL
SUPPORT ARE PROVIDED ON AN "AS IS" BASIS AND WITHOUT WARRANTY OF ANY
KIND. Information regarding any third party services included in this package is provided as a conve-
nience only, without any warranty by Rabbit, and will be governed solely by the terms agreed upon
between You and the third party providing such services. RABBIT AND ITS LICENSORS
EXPRESSLY DISCLAIM ALL WARRANTIES, EXPRESS, IMPLIED, STATUTORY OR OTHER-
WISE, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANT-
ABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD
PARTY RIGHTS. RABBIT DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN
THE SOFTWARE WILL MEET YOUR REQUIREMENTS, OR THAT THE OPERATION OF THE
SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE
SOFTWARE WILL BE CORRECTED. FURTHERMORE, RABBIT DOES NOT WARRANT OR
MAKE ANY REPRESENTATIONS REGARDING THE USE OR THE RESULTS OF THE SOFT-
WARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. NO
ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY RABBIT OR ITS AUTHORIZED
REPRESENTATIVES SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE
SCOPE OF THIS WARRANTY. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF
IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO YOU.
7. Limitation of Liability. YOU AGREE THAT UNDER NO CIRCUMSTANCES, INCLUDING NEG-
LIGENCE, SHALL RABBIT BE LIABLE FOR ANY INCIDENTAL, SPECIAL OR CONSEQUEN-
TIAL DAMAGES (INCLUDING DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS
INTERRUPTION, LOSS OF BUSINESS INFORMATION AND THE LIKE) ARISING OUT OF
THE USE AND/OR INABILITY TO USE THE SOFTWARE, EVEN IF RABBIT OR ITS AUTHO-
RIZED REPRESENTATIVE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OR EXCLUSION OF LIABILITY
FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES SO THE ABOVE LIMITATION OR
EXCLUSION MAY NOT APPLY TO YOU. IN NO EVENT SHALL RABBIT’S TOTAL LIABILITY
TO YOU FOR ALL DAMAGES, LOSSES, AND CAUSES OF ACTION (WHETHER IN CON-
TRACT, TORT, INCLUDING NEGLIGENCE, OR OTHERWISE) EXCEED THE AMOUNT PAID
BY YOU FOR THE SOFTWARE.
8. Termination. This License is effective for the duration of the copyright in the Software unless termi-
nated. You may terminate this License at any time by destroying all copies of the Software and its docu-
mentation. This License will terminate immediately without notice from Rabbit if You fail to comply
with any provision of this License. Upon termination, You must destroy all copies of the Software and
its documentation. Except for Section 2 ("License"), all Sections of this Agreement shall survive any
expiration or termination of this License.
352 rabbit.com
Index
Symbols A
# and ## (operators) ................................................ 19 abandon ................................................................ 185
#asm ...................................................... 163, 213, 323 abort ...................................................................... 185
#debug ................................................... 201, 214, 323 about Dynamic C .................................................. 293
#define ...................................................... 18, 19, 214 abstract data types .................................................. 26
#elif ....................................................................... 216 adc (add-with-carry) ............................................. 163
#else ...................................................................... 216 add-on modules .................................................... 337
#endasm ................................................ 163, 167, 214 address space .................................................... 4, 117
#endif .................................................................... 216 Advanced button ................................................... 274
#error ..................................................................... 215 AES encryption .................................................... 337
#fatal ..................................................................... 214 aggregate data types ............................................... 27
#funcchain ....................................................... 36, 215 align ...................................................................... 186
#if .......................................................................... 216 ALT key
#ifdef ..................................................................... 216 See keystrokes
#ifndef ................................................................... 217 always_on ............................................................. 186
#include anymem ................................................................ 186
absence of ........................................................... 38 application program ................................................ 38
#interleave ............................................................. 217 argument passing .................... 31, 170, 171, 175, 177
#makechain ..................................................... 36, 217 modifying value .................................................. 31
#memmap ..................................................... 218, 325 arrange icons ......................................................... 285
#nodebug ............................................... 201, 214, 323 arrays .......................................................... 27, 28, 31
#nointerleave ......................................................... 217 characters ............................................................ 22
#nouseix ................................................................ 220 subscripts ............................................................ 27
#undef ..................................................................... 21 arrow keys .................................................... 237, 238
#use ........................................................... 38, 39, 219 asm ........................................................................ 187
#useix .................................................................... 220 assembly ........................................... 3, 163–184, 247
#warns ................................................................... 220 blocks in xmem ................................................. 169
#warnt ................................................................... 220 embedding C statements ................................... 164
#ximport ................................................................ 220 stand-alone ........................................................ 169
#zimport ................................................................ 221 window ..................................................... 173, 286
*.* ........................................................................... 39 assignment operators ............................................ 227
@LENGTH ........................................................... 167 associativity .................................................. 223, 224
@PC ...................................................................... 167 attributes of a file .................................................. 141
@RETVAL ................................................... 167, 175 auto ....................................................... 168, 169, 187
@SP .............................. 167, 171, 174, 175, 176, 184 storage of variables ........................................... 170
\\ ............................................................................ 148
\n ........................................................................... 138 B
\r ............................................................................ 138 back slash ............................................................. 148
_GLOBAL_INIT .................................................. 204 backslash (\)
{ } curly braces ....................................................... 24 character literals ............................................ 19, 23
µC/OS-II compatibility ......................................... 160 continuation in directives .................................. 213
Numerics basic unit of a C program ....................................... 25
baud rate ............................................................... 270
2nd copy of FAT ................................................... 158 BCDE ................................................... 169, 175, 177
BeginHeader ..................................................... 40, 41
binary operators .................................................... 223
V
variables
auto ....................................................................187
global ...................................................................29
static ..................................................................206
vertical tiling .........................................................285
virtual watchdogs ..................................................108
void ........................................................................211
volatile ...................................................................212
W
waitfor ...................................................................209
waitfordone ...........................................................209