=========================================================== DDDDD DDDDD SSSS D Dunfield D D S D D D Development SSSS D D D D Systems DDDDD DDDDD SSSS =========================================================== MM MM IIIIIII CCCC RRRRRR OOO CCCC M M M M I C C R R O O C C M M M I C R R O O C M M I C RRRRRR O O ----- C M M I C R R O O C M M I C C R R O O C C M M IIIIIII CCCC R R OOO CCCC =========================================================== A 'C' compiler for The CUBIX/6809 system Technical Manual Release 3.23 Revised 01-Nov-02 Dunfield Development Systems ---------------------------- High quality tools for Embedded Development at low prices. https://dunfield.themindfactory.com Copyright 1988-2005 Dave Dunfield All rights reserved MICRO-C TABLE OF CONTENTS Page 1. INTRODUCTION 1 1.1 Document conventions 1 1.2 Code Portability 1 1.3 Setting up MICRO-C 2 1.4 The compiling process 3 2. THE MICRO-C PROGRAMMING LANGUAGE 4 2.1 Constants 4 2.2 Symbols 4 2.3 Arrays & Pointers 8 2.4 Functions 8 2.5 Structures & Unions 9 2.6 Control Statements 13 2.7 Expression Operators 15 2.8 Inline Assembly Language 17 2.9 Preprocessor Commands 19 2.10 Error Messages 20 2.11 Quirks 25 3. ADVANCED TOPICS 29 3.1 Conversion Rules 29 3.2 Assembly Language Interface 30 4. THE MICRO-C PREPROCESSOR 32 4.1 The MCP command 33 4.2 Preprocessor Commands 34 4.3 Predefined symbols 37 4.4 Error messages 37 4.5 String Concatenation 40 5. THE MICRO-C COMPILER 41 5.1 The MCC09 command 41 5.2 Symbol Information Comments 42 6. THE MICRO-C OPTIMIZER 43 6.1 The MCO09 command 43 7. THE SOURCE LINKER 44 7.1 The SLINK Command 44 7.2 Multiple Input Files 45 MICRO-C Table of Contents Page 7.3 The Library File 46 7.4 Source file information 47 8. The MICRO-C/CUBIX Library 48 MICRO-C Page: 1 1. INTRODUCTION This complete development package includes all tools and utilities you need to develop 'C' and 'ASM' code for the 6809 processor under the CUBIX operating system. 1.1 Document conventions The following conventions are used in this document: - Text shown in angle braces indicates data or options that the user MUST supply. [text] - Text shown in square braces indicates data or options that are optional. ... - Multiple options may be specified. 1.2 Code Portability With few exceptions, this compiler follows the syntax of the "standard" UNIX compiler. Programs written in MICRO-C should compile with few changes under other "standard" compilers. 1.2.1 Unsupported Features: MICRO-C does not currently support the following features of standard 'C': Long/Double/Float/Enumerated types, Typedef and Bit fields. * 32 bit "long" number functions are supported via library calls. These may easily be modified to support larger numbers. 1.2.2 Additional Features MICRO-C provides a few additional features which are not always included in "standard" 'C' compilers: Unsigned character variables, Nested comments, C++ comments, 16 bit character constants, Inline assembly code capability. #message/#error/#file/#forget preprocessor directives. MICRO-C Page: 2 1.3 Setting up MICRO-C Install the toolset by copying the files from the distribution diskette onto your CUBIX system diskette. The normal location for these files is the [MC] directory, although you can use a different location if you edit the CC.@ command file which runs the compiler. It is also a good idea to install the CC.@ file it to your [SYSTEM] directory so that you can execute it from any location. You may wish to edit CC.@ and make the following changes: - If you wish the compiler files to reside on a different diskette from your "current" direcrtory, add drive specifiers to all file and directory references. - If you wish to change the compiler options, or remove steps from the compile process (such a Preprocess or Optimize steps). - If the ASM assembler and BUILD utility are not in your [SYSTEM] directory, then adjust these commands accordingly. Once everything is installed and configured correctly, you are ready to begin using the compiler. Try compiling the HELLO.C example program with the command: CC HELLO MICRO-C Page: 3 1.4 The compiling process There are five programs which work together to completely compile a MICRO-C program: The PREPROCESSOR (MCP) takes the original 'C' source file, performs MACRO expansion and incorporates the contents of INCLUDE files to get a "pure" 'C' output file. A less powerful preprocessor is also contained inside the COMPILER, which allows this step to be skipped for programs which use only simple preprocessor functions. The COMPILER (MCC09) reads a file containing a 'C' source program, and translates it into an equivalent assembly language program. The OPTIMIZER (MCO09) reads the assembly language output from the compiler identifying and replacing "general" code produced by the compiler with more efficient code which is equivalent in specific cases. This step is optional, allowing you to choose between faster compile time and greater program efficiency. The SOURCE LINKER (SLINK) combines the assembly language output from the compiler with any required routines from the runtime library, to create an entire program file. The ASSEMBLER (ASM) assembles this program file into a downloadable binary image in either INTEL or MOTOROLA hex file format. MICRO-C Page: 4 2. THE MICRO-C PROGRAMMING LANGUAGE The following pages contain a brief summary of the features and constructs implemented in MICRO-C. 2.1 Constants The following forms of constants are supported by the compiler: - Decimal number (0 - 65535) 0 - Octal number (0 - 0177777) 0x - Hexadecimal number (0x0 - 0xffff) '' - Character (1 or 2 chars) "" - Address of literal string. The following "special" characters may be used within character constants or strings: \n - Newline (line-feed) (0x0a) \r - Carriage Return (0x0d) \t - Tab (0x09) \f - Formfeed (0x0c) \b - Backspace (0x08) \ - Octal value (Max. three digits) \x - Hex value (Max. two digits) \ - Protect character from input scanner. 2.2 Symbols Symbol names may include the characters 'a'-'z', 'A'-'Z', '0'-'9', and '_'. The characters '0'-'9' may not be used as the first character in the symbol name. Symbol names may be any length, however, only the first 15 characters are significant. The "char" modifier may be used to declare a symbol as an 8 bit value, otherwise it is assumed to be 16 bits. eg: char input_char; The "int" modifier may be used to declare a symbol as a 16 bit wide value. This is assumed if neither "int" or "char" is given. eg: int abc; The "unsigned" modifier may be used to declare a symbol as an unsigned positive only value. Note that unlike some 'C' compilers, this modifier may be applied to a character (8 bit) variable. eg: unsigned char count; The "extern" modifier causes the compiler to be aware of the existence and type of a global symbol, but not generate a definition for that symbol. This allows the module being compiled to reference a symbol which is defined in another module. This modifier may not be used with local symbols. eg: extern char getch(); MICRO-C Page: 5 A symbol declared as external may be redeclared as a non-external at a later point in the code, in which case a definition for it will be generated. This allows "extern" to be used to inform the compiler of a function or variable type so that it can be referenced properly before that symbol is actually defined. The "static" modifier causes global symbols to be available only in the file where they are defined. Variables or functions declared as "static" will not be accessible as "extern" declarations in other object files, nor will they cause conflicts with duplicate names in those files. eg: static int variable_name; When applied to local symbols, the "static" modifier causes those variables to be allocated in a reserved area of memory, instead of on the processor stack. This has the effect that the contents of the variable will be retained between calls to the function. It also means that the variable may be initialized at compile time (just like global variables). The "register" modifier indicates to the code generator that this is a high priority variable, and should be kept where it is easy to get at. See "Functions" for a special use of "register" when defining a function. eg: register unsigned count; The "const" modifier indicates to the compiler that this value should never be modified. In the case of a pointer, it means that the data pointed to should never be modified (but the pointer itself can be). The compiler will attempt to detect any direct modifications and issue an error message. (Note that constant data can still be modified indirectly through a non-const pointer without an error message). Symbols declared with a preceding '*' are assumed to be 16 bit pointers to the declared type. eg: int *pointer_name; Symbol names declared followed by square brackets are assumed to be arrays with a number of dimensions equal to the number of '[]' pairs that follow. The size of each dimension is identified by a constant value contained within the corresponding square brackets. eg: char array_name[5][10]; MICRO-C Page: 6 Under certain conditions, the first (highest) array dimension may be left unspecified. Only the first dimension may be unspecified, because the compiler needs to know all other dimensions to perform indexing calculations. These conditions are: - If the array is initialized in the declaration, the compiler will automatically set the unspecified first dimension to the lowest value which will cause the array to be large enough to hold all of the initial values. - If the array is a function argument, no actual storage is reserved, and the first dimension in not needed. The compiler will correctly calculate all indexes, but since the actual first dimension in not known, the sizeof() function will always return zero. - If the array is prototyped with "extern", the first dimension is also not needed. the compiler will correctly calculate all indexes, but since the actual first dimension is not known, the sizeof() function will always return zero. If the array is subsequently redeclared with a first dimension, the compiler will update it's internal tables, and sizeof() will return the actual size of the array thereafter. (Note that this is the only case where you may redeclare an array with a dimension which differs from the original prototype). The "void" modifier is a special case which means this symbol should never be used as a value. It is usually used to indicate a function which returns nothing, or a pointer which is never dereferenced. eg: void *pointer; 2.2.1 Global Symbols Symbols declared outside of a function definition are considered to be global and will have memory permanently reserved for them. Global symbols are defined by name in the output file, allowing other modules to access them. Global variables may be initialized with one or more values, which are expressed as a single array of integers REGARDLESS of the size and shape of the variable. If more than one value is expressed, '{' and '}' must be used. eg: int i = 10, j[2][2] = { 1, 2, 3, 4 }; When arrays are initialized in the declaration, a null dimension may be used as the first dimension size, in which case the size of the array will be determined by the initial values. eg: int array[] = { 1, 2, 3 }; MICRO-C Page: 7 Initialized global variables are automatically saved within the code image, insuring that the initial values will be available at run time. Any non-initialized elements of an array which has been partly initialized will be set to zero. Non-initialized global variables are not preset in any way, and will be undefined at the beginning of program execution. 2.2.2 Local Symbols Symbols declared within a function definition are allocated on the stack, and exist only during the execution of the function. To simplify the allocation and deallocation of stack space, all local symbols must be declared at the beginning of the function before any code producing statements are encountered. MICRO-C does not support initialization of non-static local variables in the declaration statement. Since local variables have to be initialized every time the function is entered, you can get the same effect using assignment statements at the beginning of the function. No type is assumed for arguments to functions. Arguments must be explicitly declared, otherwise they will be undefined within the scope of the function definition. 2.2.3 More Symbol Examples /* Global variables are defined outside of any function */ char a; /* 8 bit signed */ unsigned char b; /* 8 bit unsigned */ int c; /* 16 bit signed */ unsigned int d; /* 16 bit unsigned */ unsigned e; /* also 16 bit unsigned */ extern char f(); /* external function returning char */ static int g; /* 16 bit signed, local to file */ int h[2] = { 1, 2 }; /* initialized array (2 ints) */ main(a, b) /* "int" function containing code */ /* Function arguments are defined between function name and body */ int a; /* 16 bit signed */ unsigned char *b; /* pointer to 8 bit unsigned */ { /* Local variables are defined inside the function body */ /* Note that in MICRO-C, only "static" locals can be initialized */ unsigned c; /* 16 bit unsigned */ static char d[5]; /* 8 bit signed, reserved in memory */ static int e = 1; /* 16 bit signed, initial value is 1 */ /* Function code goes here ... */ c = 0; /* Initialize 'c' to zero */ strcpy(d, "Name"); /* Initialize 'd' to "name" */ } MICRO-C Page: 8 2.3 Arrays & Pointers When MICRO-C passes an array to a function, it actually passes a POINTER to the array. References to arrays which are arguments are automatically performed through the pointer. This allows the use of pointers and arrays to be interchangeable through the context of a function call. Ie: An array passed to a function may be declared and used as a pointer, and a pointer passed to a function may be declared and used as an array. 2.4 Functions Functions are essentially initialized global symbols which contain executable code. MICRO-C accepts any valid value as a function reference, allowing some rather unique (although non-standard) function calls. For example: function(); /* call function */ variable(); /* call contents of a variable */ (*var)(); /* call indirect through variable */ (*var[x])(); /* call indirect through indexed array */ 0x5000(); /* call address 0x5000 */ MICRO-C accepts both the "classic" and "modern" formats of argument definition for a function. In the "classic" format, only the argument names are placed in the brackets following the function name. The closing bracket is followed by formal declarations for the arguments (in any order): eg: int function(a, b, c) int a, c; char b; { ... } If the "modern" format, complete declarations for EACH argument are enclosed in the brackets following the function name: eg: int function(int a, char b, int c) { ... } Since this is a single pass compiler, operands to functions are evaluated and pushed on the stack in the order in which they are encountered, leaving the last operand closest to the top of the stack. This is the opposite order from which many other 'C' compilers push operands. MICRO-C Page: 9 For functions with a fixed number of arguments, the order of which operands are passed is of no importance, because the compiler looks after generating the proper stack addresses to reference variables. HOWEVER, functions which use a variable number of arguments are affected for two reasons: 1) The location of the LAST arguments are known (as fixed offsets from the stack pointer) instead of the FIRST. 2) The symbols defined as arguments in the function definition represent the LAST arguments instead of the FIRST. If a function is declared as "register", it serves a special purpose and causes the accumulator to be loaded with the number of arguments passed whenever the function is called. This allows the function to know how many arguments were passed and therefore determine the location of the first argument. 2.5 Structures & Unions Combinations of other variable types can be organized into STRUCTURES or UNIONS, which allow them to be manipulated as a single entity. In a structure, the individual items occur sequentially in memory, and the total size of the structure is the sum of its elements. Structures are usually used to create "records", in which related items are grouped together. An array of structures is the common method of implementing an "in-memory" database. A union is similar to a structure, except that the individual items are overlaid in memory, and the total size of the union is the size of its largest element. Unions are usually used to allow a single block of memory to be accessed as different 'C' variable types. An example of this would be in handling a message received in memory, in which a "type" byte indicates how the remainder of the message data should be interpreted. Here are some example of how structures are defined and used (unions are defined and used in an identical manner, except that the word 'union' is substituted for 'struct'): /* Create structure named 'data' with 'a,b,c & d' as members */ struct { int a; int b; char c; char d; } data; /* Create structure template named 'mystruc'... */ /* No actual structure variable is defined */ struct mystruc { int a; int b; char c; char d; }; MICRO-C Page: 10 /* Create structure named 'data' using above template */ struct mystruc data; /* Create structure template 'mystruc', AND define a */ /* structure variable named 'data' */ struct mystruc { int a; int b; char c; char d; } data; /* Create an array of structures, a pointer to a structure */ /* and an array of pointers to a structure * struct mystruct array[10], *pointer, *parray[10]; /* To set value in structure variable/members */ data.a = 10; /* Direct access */ array[1].b = 10; /* Direct array access */ pointer->c = 'a'; /* Pointer access */ parray[2]->d = 'b'; /* Pointer array access */ /* To read value in structure variable/members */ value = data.a; /* Direct access */ value = data[1].b; /* Direct array access */ value = pointer->c; /* Pointer access */ value = parray[2]->d; /* Pointer array access */ 2.5.1 Notes on MICRO-C structure implementation: Structures and Unions as implemented in MICRO-C are similar to the implementation of the original UNIX 'C' compiler, and are bound by similar limitations, as well as a few MICRO-C specific ones. Here is a list of major differences when compared to a modern ANSI compiler: All structure and member names MUST be unique within the scope of the definition. A special case exists, where common member names may be used in multiple structure templates if they have EXACTLY the same type and offset into the structure. This also saves symbol table memory, since only one copy of the member definition is actually kept. MICRO-C does NOT pass entire structures to functions on the stack. Like arrays, MICRO-C passes structures by ADDRESS. Structure variables which are function arguments are accessed through pointers. For source code compatibility with compilers which do pass the entire structure, if you declare the argument as a direct (non-pointer) structure, the direct ('.') operator is used to dereference it, even though it is actually a pointer reference. MICRO-C Page: 11 If you MUST have a local copy of the structure, use something like: func(sptr) struct mystruc *sptr; { struct mystruc data; memcpy(data, sptr, sizeof(data)); ... } To obtain the size of a structure from its template name, use the 'struct' keyword in conjunction with the 'sizeof' operator. In the above example, you could replace 'sizeof(data)' with 'sizeof(struct mystruc)'. To obtain the size of a structure member, you must specify it in the context of a structure reference with another symbol: sizeof(variable.member) or sizeof(variable->member) NOTE: The current compiler allows almost any symbol to the left of the '.' or '->' operator in a 'sizeof', however future versions of the compiler may insist on a structure variable or a pointer to structure variable. MICRO-C is quite limited in its implementation of pointers to structures. Such pointers are internally stored as pointers to 'char', and therefore dereferencing (*), indexing ([]), and all forms of pointer arithmetic (++, --, +, -, ...) will generally not perform as you would expect them to with structures. The only meaningful things that can be done with a pointer to a structure is assign it to another variable, pass it as a function argument and apply the '->' operator to access individual members. Use this to "increment" a pointer to a structure to point to the next structure: ptr += sizeof(struct mystruc) Use this to access the 'n'th structure from the pointer: (ptr + (n * sizeof(struct mystruc)))->member The 'struct' and 'union' keywords are not accepted in a TYPECAST. This is most commonly used to setup a pointer to a structure. Since MICRO-C stores its pointers to structures as pointers to char, you can use (char*) as the typecast, and get the same functionality. A structure name by itself (without '.member') acts in a manner similar to a character array name. With no operation, the address of the structure is returned. You can also use '[]' to access individual bytes in the structure by indexing, although doing so is highly non-portable. MICRO-C Page: 12 MICRO-C allows static or global structures to be initialized in the declaration, however the initial values are read as an array of bytes, and are assigned directly to structure memory without regard for the type or size of its members: struct mystruc data = { 0, 1, 0, 2, 3, 4 }; /* A=0:1, B=0:2, C=3, D=4 */ You can use INT and CHAR to switch back and forth between word/byte initialization within the value list: struct mystruct data = { int 0, 1, char 2, 3 }; /* A=0, B=1, C=2, D=3 */ Strings encountered during structure initializations will be encoded as a series of bytes if byte initialization (char) is in effect, or as pointers into the literal pool if word initialization (int) is in effect. MICRO-C does not WORD ALIGN structure elements. When using a processor which requires word alignment, it is the programmers responsibility to maintain alignment, using filler bytes etc. when necessary. MICRO-C Page: 13 2.6 Control Statements The following control statements are implemented in MICRO-C: if(expression) statement; if(expression) statement; else statement; while(expression) statement; do statement; while expression; for(expression; expression; expression) statement; return; return expression; break; continue; switch(expression) { case constant_expression : statement; ... break; case constant_expression : statement; ... break; . . . default: statement; } label: statement; goto label; asm "..."; asm { ... } MICRO-C Page: 14 2.6.1 Notes on Control Structures 1) Any "statement" may be a single statement or a compound statement enclosed within '{' and '}'. 2) All three "expression"s in the "for" command are optional. 3) If a "case" selection does not end with "break;", it will "fall through" and execute the following case as well. 4) Expressions following 'return' and 'do/while' do not have to be contained in brackets (although this is permitted). 5) Label names may precede any statement, and must be any valid symbol name, followed IMMEDIATELY by ':' (No spaces are allowed). Labels are considered LOCAL to a function definition and will only be accessible within the scope of that function. 6) The 'asm' statement used to implement the inline assembly language capability of MICRO-C accepts two forms: asm "..."; <- Assemble single line. asm { <- Assemble multiple lines. ... } MICRO-C Page: 15 2.7 Expression Operators The following expression operators are implemented in MICRO-C: 2.7.1 Unary Operators - - Negate ~ - Bitwise Complement ! - Logical complement ++ - Pre or Post increment -- - Pre or post decrement * - Indirection & - Address of sizeof - Size of a object or type (type) - Typecast 2.7.2 Binary Operators + - Addition - - Subtraction * - Multiplication / - Division % - Modulus & - Bitwise AND | - Bitwise OR ^ - Bitwise EXCLUSIVE OR << - Shift left >> - Shift right == - Test for equality != - Test for inequality > - Test for greater than < - Test for less than >= - Test for greater than or equal to <= - Test for less than or equal to && - Logical AND || - Logical OR = - Assignment += - Add to self assignment -= - Subtract from self assignment *= - Multiply by self assignment /= - Divide by and reassign assignment %= - Modular self assignment &= - AND with self assignment |= - OR with self assignment ^= - EXCLUSIVE OR with self assignment <<= - Shift left self assignment >>= - Shift right self assignment MICRO-C Page: 16 NOTES: 1) The expression "a && b" returns 0 if "a" is zero, otherwise the value of "b" is returned. The "b" operand is NOT evaluated if "a" is zero. 2) The expression "a || b" returns the value of "a" if it is not 0, otherwise the value of "b" is returned. The "b" operand is NOT evaluated if "a" is non-zero. 2.7.3 Other Operators ; - Ends a statement. , - Allows several expressions in one statement. + Separates symbol names in multiple declarations. + Separates constants in multi-value initialization. + Separates operands in function calls. ? - Conditional expression (ternary operator). : - Delimits labels, ends CASE and separates conditionals. . - Access a structure member directly. -> - Access a structure member through a pointer. { } - Defines a BLOCK of statements. ( ) - Forces priority in expression, indicates function calls. [ ] - Indexes arrays. If fewer index values are given than the number of dimensions which are defined for the array, the value returned will be a pointer to the appropriate address. Eg: char a[5][2]; a[3] returns address of forth row of two characters. (remember indexes start from zero) a[3][0] returns the character at index [3][0]; MICRO-C Page: 17 2.8 Inline Assembly Language Although 'C' is a powerful and flexible language, there are sometimes instances where a particular operation must be performed at the assembly language level. This most often involves either some processor feature for which there is no corresponding 'C' operation, or a section of very time critical code. MICRO-C provides access to assembly language with the 'asm' statement, which has two basic forms. The first is: asm "..." ; In this form, the entire text contained between the double quote characters (") is output as a single line to the assembler. Note that a semicolon is required, just like any other 'C' statement. Since this is a standard 'C' string, you can use any of the "special" characters, and thus you could output multiple lines by using '\n' within the string. Another important characteristic of it being a string is that it will be protected from preprocessor substitution. The second form of the 'asm' statement is: asm { ... } In this form, all lines between '{' and '}' are output to the assembler. Any text following the opening '{' (on the same line) is ignored. Due to the unknown characteristics of the inline assembly code, the closing '}' will only be recognized when it is the first non-whitespace character on a line. The integral preprocessor will not perform substitution on the inline assembly code, however the external preprocessor (MCP) will substitute in this form. This allows you to create assembly language "macros" using MCP, and have parameters substituted into them when they are expanded: /* * This macro issues a 'SSR' instruction for its parameter */ #define ssr(service) asm {\ SSR service\ } /* * This macro WILL NOT WORK, since the 'service' operand to the * SSR instruction is contained within a string and is therefore * protected from substitution by the preprocessor */ #define ssr(service) asm " SETB service"; MICRO-C Page: 18 The 'asm' keyword may also be used as a expression value element. When used in this manner, the value returned is the value of the processor accumulator register following the execution of the inline assembly instructions. Note that the 'asm' keyword and the following string or {} block are considered to be a single value item by the parser. When used within a function, arguments can be passed to inline assembly using the form: asm(arg1, arg2, ...) "..."; -or- asm(arg1, arg2, ...) { ... } All arguments are pushed on the processor stack (left to right) except for the LAST one, which is passed in the accumulator. If there is only one argument it is passed in the accumulator and no values are stacked. The inline assembly code MUST remove the stacked arguments before it finishes: variable = asm(1, 2, 3) { ... ; asm-code receives last arg (3) in accumulator ... ; asm-code must retrieve args 1&2 from stack ... ; asm-code must remove 2 words from stack ... ; asm-code must set return value in accumulator }; <= Note ';' is required to terminate expression // In the following example: // asm-code receives it's only argument (a) in accumulator // asm-code does not have to remove anything from stack // asm-code must set return value in accumulator if(asm(a)"asm-code") { ... } // Do this if asm-code returns accumulator!=0 // Since asm{} needs separate lines, the following "if" // statement looks odd, but is perfectly legal. if(asm(a,b) { ... ; asm-code receives last arg (b) in accumulator ... ; asm-code must retrieve arg (a) from stack ... ; asm-code must remove 1 word from stack ... ; asm-code must set return valeu in accumulator } ) { ... } // Do this if asm-code returns accumulator !=0 MICRO-C Page: 19 2.9 Preprocessor Commands The MICRO-C compiler supports the following preprocessor commands. These commands are recognized only if they occur at the beginning of the input line. NOTE: This describes the limited preprocessor which is integral to the compiler, see also the section on the more powerful external processor (MCP). 2.9.1 #define The "#define" command allows a global name to be defined, which will be replaced with the indicated text whenever it is encountered in the input file. This occurs prior to processing by the compiler. 2.9.2 #file Sets the filename of the currently processing file to the given string. This command is used by the external preprocessor (MCP) to insure that error messages indicate the original source file. 2.9.3 #include This command causes the indicated file to be opened and read in as the source text. When the end of the new file is encountered, processing will continue with the line following "#include" in the original file. 2.9.4 #ifdef Processes the following lines (up to #else or #endif) only if the given name is defined. 2.9.5 #ifndef Processes the following lines (up to #else or #endif) only if the given name is NOT defined. 2.9.6 #else Processes the following lines (up to #endif) only if the preceding #ifdef or #ifndef was false. 2.9.7 #endif Terminates #ifdef and #ifndef NOTE: The integral preprocessor does not support nesting of the #ifdef and #idndef constructs. If you wish to nest these conditionals, you must use the external preprocessor (MCP). MICRO-C Page: 20 2.10 Error Messages When MICRO-C detects an error, it outputs an informational message indicating the type of problem encountered. The error message is preceded by the filename and line number where the error occurred: program.c(5): Syntax error In the above example, the error occurred in the file "program.c" at line 5. The following error messages are produced by the compiler: 2.10.1 Compilation aborted The preceding error was so severe than the compiler cannot proceed. 2.10.2 Constant expression required The compiler requires a constant expression which can be evaluated at compile time (ie: no variables). 2.10.3 Declaration must precede code. All local variables must be defined at the beginning of the function, before any code producing statements are processed. 2.10.4 Dimension table exhausted The compiler has encountered more active array dimensions than it can handle. 2.10.5 Duplicate local: 'name' You have declared the named local symbol more than once within the same function definition. 2.10.6 Duplicate global: 'name' You have declared the named global symbol more than once. 2.10.7 Expected '' The compiler was expecting the given token, but found something else. 2.10.8 Expression stack overflow The compiler has found a more complicated expression than it can handle. Check that it is of correct syntax, and if so, break it up into two simpler expressions. MICRO-C Page: 21 2.10.9 Expression stack underflow The compiler has made an error in parsing the expression. Check that it is of correct syntax. 2.10.10 Illegal indirection You have attempted to perform an indirect operation ('*' or '[]') on an entity which is not a pointer or array. This error will also result if you attempt to index an array with more indices than it has dimensions. 2.10.11 Illegal initialization Local variables may not be initialized in the declaration statement. Use assignments at the beginning of the function code to perform the initialization. 2.10.12 Illegal nested function You may not declare a function within the definition of another function. 2.10.13 Illegal null dimension You have attempted to declare an array using a null dimension value which is not in the first dimension position. 2.10.14 Illegal pointer operation You are attempting to perform an operation which is not allowed in pointer arithmetic. 2.10.15 Improper type of symbol: 'name' The named symbol is not of the correct type for the operation that you are attempting. Eg: 'goto' where the symbol is not a label. 2.10.16 Improper #else/#endif A #else or #endif statement is out of place. 2.10.17 Inconsistent member type/offset: 'name' The named structure member is multiply defined, and has a different type, offset or dimension than its first definition. 2.10.18 Inconsistent redeclaration: 'name' You have attempted to redefine the named external symbol with a type which does not match its previously declared type. MICRO-C Page: 22 2.10.19 Incorrect declaration A statement occurring outside of a function definition is not a valid declaration for a function or global variable. 2.10.20 Invalid '&' operation You have attempted to reference the address of something that has no address. This error also occurs when you attempt to take the address of an array without giving it a full set of indices. Since the address is already returned in this case, simply drop the '&'. (The error occurs because you are trying to take the address of an address). 2.10.21 Macro expansion too deep The compiler has encountered a nested macro reference which is too deep to be resolved. 2.10.22 Macro space exhausted The compiler has encountered more macro ("#define") text than it has room to store. Use the external MCP preprocessor which has much greater macro storage capability. 2.10.23 No active loop A "continue" or "break" statement was encountered when no loop is active. 2.10.24 No active switch A "case" or "default" statement was encountered when no "switch" statement is active. 2.10.25 Not an argument: 'name' You have declared the named variable as an argument, but it does not appear in the argument list. 2.10.26 Non-assignable You have attempted an operation which results in assignment of a value to an entity which cannot be assigned. (eg: 1 = 2); 2.10.27 Numeric constant required The compiler requires a constant expression which returns a simple numeric value. 2.10.28 String space exhausted The compiler has encountered more literal strings than it has room store. MICRO-C Page: 23 2.10.29 Symbol table full The compiler has encountered more symbol definitions than it can handle. 2.10.30 Syntax error The statement shown does not follow syntax rules and cannot be parsed. 2.10.31 Too many active cases The compiler has run out of space for storing switch/case tables. Reduce the number of active "cases". 2.10.32 Too many defines The compiler has encountered more '#define' statements than it can handle. Reduce the number of #defines. 2.10.33 Too many errors The compiler is aborting because of excessive errors. 2.10.34 Too many includes The compiler has encountered more nested "#include" files than it can handle. 2.10.35 Too many initializers You have specified more initialization values than there are locations in the global variable. 2.10.36 Too many pointer levels You have declared an item with more levels of redirection ('*'s) than the compiler can handle. 2.10.37 Type clash You have attempted to use a value in a manner which is inconsistent with its typing information. Also results from an attempt to declare a non-pointer variable with the "void" type. 2.10.38 Unable to open: 'name' A "#include" command specified the named file, which could not be opened. 2.10.39 Undefined: 'name' You have referenced a name which is not defined as a local or global symbol. MICRO-C Page: 24 2.10.40 Unknown structure/member: 'name' You have referenced a structure template or member name which is not defined. 2.10.41 Unreferenced: 'name' The named symbol was defined as a local symbol in a function, but was never used in that function. This error will occur at the end of the function definition containing the symbol declaration. It is only a warning, and will not cause the compile to abort. 2.10.42 Unresolved: 'name' The named symbol was forward referenced (Such as a GOTO label), and was never defined. This error will occur at the end of the function definition containing the reference. 2.10.43 Unterminated conditional The end of file was encountered when a "#if" or "#else" conditional block was being processed. 2.10.44 Unterminated function The end of the file was encountered when a function definition was still open. MICRO-C Page: 25 2.11 Quirks Due to its background as a highly compact and portable compiler, and its target application in embedded systems, MICRO-C deviates from standard 'C' in some areas. The following is a summary of the major infractions and quirks: PLEASE NOTE that this section should not be considered as evidence that the compiler is somehow inferior or buggy! ALL compilers have quirks. Most vendors just keep quiet and hope you won't notice. 2.11.1 Preprocessor quirks *** NOTE: The quirks in this section apply ONLY to the limited INTERNAL preprocessor. They DO NOT APPLY when the external preprocessor (MCP) is used. When using the INTERNAL preprocessor, the operands to '#' commands are parsed based on separating spaces, and any portion of the line not required is ignored. In particular, the '#define' command only accepts a definition up to the next space or tab character. eg: #define APLUSONE A+1 <-- uses "A+1" #define APLUSONE A +1 <-- uses "A" Comments are stripped by the token scanner, which occurs AFTER the '#' commands are processed. eg: #define NULL /* comment */ <-- uses "/*" Note that since comments can therefore be included in "#define" symbols, you can use "/**/" to simulate spaces between tokens. eg: #define BYTE unsigned/**/char Include filenames are not delimited by '""' or '<>' and are passed to the operating system exactly as entered. eg: #include [MC]STDIO.H 2.11.2 Function/Variable declaration quirks The appearance of a variable name in the argument list for an old style function declaration serves only to identify that variables location on the stack. MICRO-C will not define the variable unless it is explicitly declared (between the argument list and the main function body). In other words, all arguments to a function must be explicitly declared. MICRO-C does not support "complex" declarations which use brackets '()' for other than function parameters. These are most often used in establishing pointers to functions: int (*a)(); /* Pointer to function returning INT */ (*a)(); /* Call address in 'a' */ MICRO-C Page: 26 Since MICRO-C allows you to call any value by following it with '()', you can get the desired effect in the above case, by declaring 'a' as a simple pointer to int, and calling it with the same syntax: int *a; /* Pointer to INT */ (*a)(); /* Call address in 'a' */ MICRO-C will not output external declarations to the output file for any variables or functions which are declared as "extern", unless that symbol is actually referenced in the 'C' source code. This prevents "extern" declarations in system header files (such as "STDIO.H") which are used as prototypes for some library functions from causing those functions to be loaded into the object file. Therefore, any "extern" symbols which are referenced only by inline assembly code must be declared in the assembly code, not by the MICRO-C "extern" statement. Read the notes at the end of the section entitled "Structures and Unions" for information on limitations or differences from standard 'C' in MICRO-C's implementation of structures and unions. 2.11.3 Operator/Expression quirks MICRO-C is more strict about its handling of the ADDRESS operator ('&') than most other compilers. It will produce an error message if you attempt to take the address of a value which is already a fixed address (such as an array name without a full set of indices). Since an address is already produced in such cases, simply drop the '&'. The INDEXING operator '[]' is not commutative in MICRO-C. In other words 'array[index]' is NOT equivalent to 'index[array]'. The 'x' in '0x' and '\x' is accepted in lower case only. When operating on pointers, MICRO-C only scales the increment (++), decrement (--) and index ([]) operations to account for the size of the pointer: eg: char *cptr; /* pointer to character */ int *iptr; /* pointer to integer */ ++cptr; /* Advance one character */ ++iptr; /* Advance one integer */ cptr[10]; /* Access the tenth character */ iptr[10]; /* Access the tenth integer */ cptr += 10; /* Advance 10 characters */ iptr += 10; /* Advance ONLY FIVE integers */ NOTE: A portable way to advance "iptr" by integers is: iptr = &iptr[10]; /* Advance 10 integers */ MICRO-C Page: 27 Since structures are internally represented as arrays of "char", incrementing a pointer to a structure will advance only one (1) byte in memory. To advance to the "next" instance of the structure, use: ptr += sizeof(struct template); Unlike some 'C' compilers, MICRO-C will process character expressions using only BYTE values. Character values are not promoted to INT unless there is an INT value involved in the expression. This results in much more efficient code when dealing with characters, particularly on small processors which have limited 16 bit instructions. Consider the statement: return c + 1; On some compilers, this will sign extend the character variable 'c' into an integer value, and then ADD an integer 1 and return the result. MICRO-C will ADD the character variable and a character 1, and then promote the result to INT before returning it (results of expressions as operands to 'return' are always promoted to int). Unfortunately, programs have been written which rely on the automatic promotion of characters to INTs to work properly. The most common source of problems is code which attempts to treat CHAR variables as UNSIGNED values (many older compilers did not support UNSIGNED CHAR). For example: return c & 255; In a compiler which always evaluates character expressions as INT, the above statement will extract the value of 'c' as positive integer ranging from 0 to 255. In MICRO-C, ANDing a character with 255 results in the same character, which gets promoted to an integer value ranging from -128 to 127. To force the promotion within the expression, you could CAST the variable to an INT: return (int)c & 255; The same objective can be achieved in a more efficient (and correct) manner by declaring the variable 'c' as UNSIGNED CHAR, or by CASTing the variable to an UNSIGNED value: return (unsigned)c; Note that this is not only more clearly shows the intent of the programmer, but also results is more efficient generated code. MICRO-C Page: 28 A related quirk occurs because MICRO-C automatically casts untyped constants used in binary operations to match the other type used in the operation, again this is done to preserve 8 bit arithmetic wherever possible, but it can surprise you if you use a number which cannot be expressed in 8 bits in an operation with a character type. The high bits of the number will be dropped as it is truncated to a matching 8 bit type. If such a number is truly needed in an 8 bit expression, simply cast it to the appropriate 16 bit type: eg: 0x1234 / c; /* 0x34 / c (8 bit arithmetic) */ (int)0x1234 / c; /* 0x1234 / c (16 bit arithmetic) */ Most processors do not support a simple efficient method for adding or subtracting a SIGNED 8 bit quantity and a 16 bit quantity. The code generators supplied with MICRO-C make the assumption that character values being added or subtracted to/from integers will contain only POSITIVE values, and thus use UNSIGNED addition/subtraction. This allows much more efficient code to be generated, as the carry/borrow from the low order byte of the operation is simply propagated to the high order byte of the result (an operation supported in hardware by the CPU). For those rare instances where you do wish to add/subtract a potentially negative character value to/from an int, you can force the expression to be performed in less efficient fully 16 bit arithmetic by casting the character to an int. int i; char c; ... i += c; /* Very efficient... C must be positive */ i += (int)c; /* Less efficient... C can be negative */ MICRO-C Page: 29 3. ADVANCED TOPICS This section provides information on the more advanced aspects of MICRO-C, which is generally not needed for casual use of the language. 3.1 Conversion Rules MICRO-C keep track of the "type" of each value used in all expressions. This type identifies certain characteristics of the value, such as size range (8/16 bits), numeric scope (signed/unsigned), reference (value/pointer) etc. When an operation is performed on two values which have identical "types", MICRO-C assigns that same "type" to the result. When the two value "types" involved in an operation are different, MICRO-C calculates the "type" of the result using the following rules: 3.1.1 Size range If both values are direct (not pointer) references, the result will be 8 bits only if both values were 8 bits. If either value was 16 bits, the result will be 16 bits. If one value is a pointer, and the other is direct, the result will be a pointer to the same size value as the original pointer. If both values were pointers, the result will be a pointer to 16 bits only if both original pointers referenced 16 bit values. If either pointer referenced an 8 bit value, the result will reference an 8 bit value. 3.1.2 Numeric Scope The result of an expression is considered to be signed only if both original values were signed. If either value was an unsigned value, the result is unsigned. 3.1.3 Reference If either of the original values was a pointer, the result will be a pointer. One exception to this rule is the subtraction of two pointers, which yields an integer result. Note that this "calculated" result type is used for partial results within an expression. Whenever a symbol such as a variable or function is referenced, the type of that symbol is taken from its declaration, no matter what "type" of value was last stored (variable) or returned (function). The TYPECAST operation may be used to override the type calculated for the result of an expression if necessary. MICRO-C Page: 30 3.2 Assembly Language Interface Assembly language programs may be called from 'C' functions and vice versa. These programs may be in the form of inline assembly language statements in the 'C' source code, or separately linked modules. The MICRO-C runtime library includes a number of assembly language subroutines which provide various services (such as 16 bit multiplication, division, etc). These routines are well documented in the library startup code files (6809RL?.ASM), which you should examine before attempting to use assembly language within MICRO-C. Global variables defined in 'C' exist at absolute addresses and may be referenced directly by name from assembly language. Global names which are referenced by both assembly language and 'C' should not be longer than 15 characters. When MICRO-C calls any routine ('C' or assembler), it first pushes all arguments to the routine onto the processor stack, in the order in which they occur in the argument list to the function. This means that the LAST argument to the function is CLOSEST to the top of the processor stack. Arguments are always pushed as 16 bit values. Character values are extended to 16 bits, and arrays are passed as 16 bit pointers to the array. (MICRO-C knows that arrays which are arguments are actually pointers, and automatically references through the pointer). After pushing the arguments, MICRO-C then generates a machine language subroutine call, thereby executing the code of the routine. Once the called routine returns, the arguments are removed from the stack by the calling program. It is the responsibility of the called function to remove any saved registers and local variable space from the stack before it returns. If a value is to be returned to the calling program, it is expected to be in the 16 bit ACCUMULATOR. Examine the supplied library functions, as well as code produced by the compiler to gain more insight into the techniques of accessing local variables and arguments. MICRO-C Page: 31 The 16 bit accumulator is maintained in registers A:B (A=High, B=Low) Local variables in a function may be referenced as offsets from the stack pointer. Note that offsets must be adjusted for the number of additional bytes which are pushed and popped on the stack during the execution of the function. The address of a particular local variable is calculated as: "stack pointer" + (# bytes pushed during function execution) + (size of all preceding local variables in bytes) Arguments to a function may also be referenced as direct offsets from the stack pointer, in much the same way as local variables are. The address of a particular argument is calculated as: "stack pointer" + (# bytes pushed during function execution) + (size of all local variables in bytes) + (Size of return address on stack (2)) + (# arguments from LAST argument) * 2 If a function has been declared as "register", MICRO-C will load the 'D' accumulator with the number of arguments which were passed, each time the function is called. This allows the function to determine the location of the first argument, which may be calculated as: "stack pointer" + (accumulator contents) * 2 + (# bytes pushed during function execution) + (size of all local variables in bytes) + (Size of return address on stack (2)) MICRO-C Page: 32 4. THE MICRO-C PREPROCESSOR The MICRO-C Preprocessor is a source code filter, which provides greater capabilities than the preprocessor which is integral to the MICRO-C compiler. It has been implemented as a stand alone utility program which processes the source code before it is compiled. Due to the higher complexity of this preprocessor, it operates slightly slower than the the integral MICRO-C preprocessor. This is mainly due to the fact that it reads each line from the file and then copies it to a new line while performing the macro substitution. This is necessary since each macro may contain parameters which must be replaced "on the fly" when it is referenced. The integral MICRO-C preprocessor is very FAST, because it does not copy the input line. When it encounters a '#define'd symbol, it simply adjusts the input scanner pointer to point to the definition of that symbol. Keeping the extended preprocessor as a stand alone utility allows you to choose between greater MACRO capability and faster compilation. It also allows the system to continue to run on very small hardware platforms. The additional capabilities of the extended preprocessor are: - Parameterized MACROs. - Multiple line MACROs. - Nested conditionals. - Numeric conditionals (#if) - User generated errors (#error) - Ability to undefine MACRO symbols. - String concatenation - Library reference in include file names (<> and "") MICRO-C Page: 33 4.1 The MCP command The format of the MICRO-C Preprocessor command line is: MCP [input_file] [output_file] [options] [input_file] is the name of the source file containing 'C' statements to read. If no filenames are given, MCP will read from standard input. [output_file] is the name of the file to which the processed source code is written. If less than two filenames are specified, MCP will write to standard output. 4.1.1 Command Line Options MCP accepts the following command line [options], which are recognized in lower case only: -C - Instructs MCP to keep comments from the input file (except for those in '#' statements which are always removed). Normally, MCP will remove all comments. -D - Warns of duplicate macro definitions. -L - Causes the output file to contain line numbers. Each line in the output file will be prefixed with the line number of the originating line from the input file. L=path - Defines the directory path which will be taken to reference "library" files when '<>' are used around an '#include' file name. Unless otherwise specified, the path defaults to: '[MC]' -Q - Instructs MCP to be quiet, and not display the startup message when it is executed. = - Pre-defines a non-parameterized macro of the specified with the string value . NOTE: "doubled up" options (eg: -ql) are not accepted. MICRO-C Page: 34 4.2 Preprocessor Commands The following commands are recognized by the MCP utility, only if they occur at the beginning of the source file line: 4.2.1 #define (parameters) Defines a global macro name which will be replaced with the indicated wherever it occurs in the source file. Macro names may be any length, and may contain the characters 'a'-'z', 'A'-'Z', '0'-'9' and '_'. Names must not begin with the characters '0'-'9'. If the macro name is IMMEDIATELY followed by a list of up to 10 parameter names contained in brackets, those parameter names will be substituted with parameters passed to the macro when it is referenced. Parameter names follow the same rules as macro names. eg: #define min(a, b) (a < b ? a : b) If any spaces exist between the macro name and the opening '(', the macro will not be parameterized, and all following text (including '(' and ')') will be entered into the macro definition. If the very last character of a macro definition line is '\', MCP will continue the definition with the next line (The '\' is not included). Pre-processor statements included as part of a macro definition will not be processed by MCP, but will be passed on and handled by the integral MICRO-C preprocessor. 4.2.2 #undef Undefines the named macro symbol. further references to this symbol will not be replaced. NOTE: With MCP, macro definitions operate on a STACK. IE: If you define a macro symbol, and then re-define it (without '#undef'ing it first), subsequently '#undef'ing it will cause it to revert to its previous definition. A second '#undef' would then cause it to be completely undefined. MICRO-C Page: 35 4.2.3 #forget Similar to '#undef', except that the symbol and ALL SUBSEQUENTLY DEFINED SYMBOLS will be undefined. Useful for releasing any local symbols (used only within a single include file). For example: #define GLOBAL "xxx" /* first global symbol */ ... /* more globals */ #define LOCAL "xxx" /* first local symbol */ ... /* more locals */ /* body of include file goes here */ #forget LOCAL /* release locals */ 4.2.4 #if Evaluates an expression, and causes the following lines (up to '#else', '#elif' or '#endif' to be processed and included in the output file only if the result of the expression was TRUE (non-zero). The expression may contain only numeric constants, and the following operators: + Addition << Shift Left - Subtract & Unary negate >> Shift Right * Multiplication == Test equal to / Division != Test not equal to % Modulus > Test greater than & Bitwise AND >= Test greater or equal | Bitwise OR < Test less than ^ Bitwise XOR <= Test less or equal && Logical AND ~ Unary bitwise complement || Logical OR ! Unary logical complement Note that the simplified expression parser in MCP does NOT follow standard rules of operator precedence. Operations are performed from left to right in the order that they are encountered. Brackets '()' can be used to force a specific order of evaluation. Macro substitution will occur on the expression before it is evaluated, and any symbol names which are not resolved are assumed to have a value of 0 (FALSE). 4.2.5 #ifdef Causes the following lines to be processed and included in the output file only if the named symbol is defined as a macro. MICRO-C Page: 36 4.2.6 #ifndef Causes the following lines to be processed and included in the output file only if the named symbol is NOT defined as a macro. NOTE: '#if/#ifdef/#ifndef/#else/#elif/#endif' may be nested. 4.2.7 #else Causes the following lines to be processed only if the preceding '#if', '#ifdef', '#ifndef' and all '#elif' statements at this level have been false. Only one #else is allowed in each conditional block, and it must be the last section of the block. 4.2.8 #elif Behaves like '#else' followed by '#if', except that it does not create another nesting level (ie: Only one '#endif' is required). Multiple '#elif's are permitted within a single conditional block. 4.2.9 #endif Terminates an #if/#ifdef/#ifndef/#else/#elif conditional block. 4.2.10 #include Causes MCP to open the named file and include its contents as part of the input source. If the filename is contained within '"' characters, it will be opened exactly as specified, and (unless it contains a directory path) will reference a file in the current directory. If the filename is contained within the characters '<' and '>', it will be prefixed with the library path (See 'l=' option), and will therefore reference a file in that library directory. For example: #include "HEADER.H" /* from current directory */ #include /* from library directory */ 4.2.11 #error Causes MCP to issue an error message containing the specified text, and then terminate. 4.2.12 #message Outputs an informational message (does not terminate). MICRO-C Page: 37 4.2.13 #index Sets the value of the __INDEX__ count (see below). 4.3 Predefined symbols The following symbols are predefined within MCP: __FILE__ - Current file name __LINE__ - Current line number __INDEX__ - Index count, increments every time referenced 4.4 Error messages When MCP detects an error during processing, an message is displayed along with the filename and line number. MCP will terminate if more than 10 errors occur. The following errors are reported by MCP: 4.4.1 Cannot open include file A '#include' statement on the indicated line specified a file which could not be opened for reading. 4.4.2 Invalid constant in expression An expression on the indicated line does not contain a valid numeric constant at a point where one was expected. MICRO-C Page: 38 4.4.3 Invalid include file name A '#include' statement on the indicated line specified a file name which was not contained within '"' or '<>' characters. 4.4.4 Invalid macro name A '#define' statement on the indicated line contains a macro name which does not follow the name rules. 4.4.5 Invalid macro parameter A '#define' statement on the indicated line contains a macro parameter name which does not follow the name rules. A reference to a macro does not have a proper ')' character to terminate the parameter list. 4.4.6 Invalid operator in expression An expression on the indicated line does not contain a supported operator at a point where one was expected (see #if). 4.4.7 Misplaced #else / #elif / #endif The indicated conditional control statement was found in a place where it was not expected. 4.4.8 Too many errors More than 10 errors has been encountered and MCP is terminating. 4.4.9 Too many macro definitions MCP has encountered more '#define' statements than it can handle. 4.4.10 Too many macro parameters A '#define' statement on the indicated line specifies more parameters to the macro than MCP can handle. 4.4.11 Too many include files MCP has encountered more nested '#include' statements than it can handle. 4.4.12 Undefined macro A '#undef' or '#forget' statement on the indicated line references a macro name which has not been defined. MICRO-C Page: 39 4.4.13 Unterminated comment The END OF FILE has been encountered while processing a comment. 4.4.14 Unterminated string A quoted string on the indicated line has no end. To continue a string to the next line, use '\' as the last character on the line. The '\' will not be included in the string. NOTE: MCP is not aware of inline assembly code, and will therefore generate an "Unterminated string" error if it encounters unbalanced single or double quotes in the assembly comments. To prevent this, either avoid using unbalanced quotes in assembly comments, or use the 'C' form (/* ... */) for comments within the assembly blocks. MICRO-C Page: 40 4.5 String Concatenation MCP supports a non-standard-C extension which allows for very flexible concatenation of strings, including the incorporation of non-string items into adjacent strings. This works as follows: If any string is followed immediately by the '#' character, MCP does two things: - The trailing quote is removed from the preceding string. - The opening quote is removed from the next string encountered. "one"#" two" -> Results in "one two" #define NAME Dave "Hello "#NAME"!" -> Results in "Hello Dave!" Note1: EVERYTHING (including spaces) from the '#' until the start of the next string will be included in the concatenated string. Note2: If you want to append a non-string to a string, without any further appended text, you MUST use a null string ("") to supply the closing quote: "Hello "#NAME"" MICRO-C Page: 41 5. THE MICRO-C COMPILER The heart of the MICRO-C programming environment is the COMPILER. This program reads a file containing a 'C' source program, and translates it into an equivalent assembly language program. The compiler includes its own limited pre-processor, which is suitable for compiling programs requiring only non-parameterized MACRO substitution, simple INCLUDE file capability, and single-level CONDITIONAL processing. A complete description of the language accepted by MCC09, as well as error messages and other details is given in the section entitled "THE MICRO-C PROGRAMMING LANGUAGE". 5.1 The MCC09 command The format of the MICRO-C 6809 Compiler command line is: MCC09 [input_file] [output_file] [options] [input_file] is the name of the source file containing 'C' statements to read. If no filenames are given, MCC09 will read from standard input. [output_file] is the name of the file to which the generated assembly language code is written. If less than two filenames are specified, MCC09 will write to standard output. 5.1.1 Command Line Options MCC09 accepts the following command line [options], which are recognized in upper case only: -C - Include the 'C' source code as assembly comments. -F - Causes the compiler to "Fold" its literal pool. (Identical strings not contained in explicit variables occur only once in memory). -L - Enables MCC09 to accept line numbers. (At beginning of line, followed by ':'). -Q - Causes MCC09 to be quiet (no startup message). -S - Output symbolic comments (see below). NOTE: "doubled up" options (eg: -ql) are not accepted. MICRO-C Page: 42 5.2 Symbol Information Comments When the -S option is used, MCC09 outputs special comments into the listing file which provide information about symbols used in the program: #fun This indicates the start of a function named . is the amount of stack used for local variable storage, and is an value that increments with each function, and is used in a unique prefix for local static variables. #end This comment indicates the end of the function code. The addresses listed with #fun and #end can be used to determine the memory block occupied by the function code. #lcl [#dims dim...] This comment indicates a symbol local to the current function. #lcl will always occur between the #fun and a #end comments for the function in which these symbols are in scope. #gbl [#dims dim...] This comment indicates a symbol which is global to the entire program. is a 16 bit value (in decimal) which indicates type information from the compilers symbol table, in bit positions as follows: REFERENCE 0x8000 - symbol has been referenced INITED 0x4000 - symbol is initialized in declaration ARGUMENT 0x2000 - symbol is a function argument EXTERNAL 0x1000 - external attribute STATIC 0x0800 - static attribute REGISTER 0x0400 - register attribute TVOID 0x0200 - void attribute UNSIGNED 0x0100 - unsigned attribute BYTE 0x0080 - 0=16 bit, 1=8 bit CONSTANT 0x0040 - const attribute SYMTYPE 0x0030 - Symbol type (see below): 0 - A simple variable 1 - A structure member 2 - A structure template 3 - A function(global) or GOTO label(local) ARRAY 0x0008 - symbol is an array POINTER 0x0007 - level of pointer indirection is a 16 bit value with the following meaning: MICRO-C Page: 43 Global symbol: Set to zero Local (!static) variable: Offset of variable into stack frame Function argument: Offset from stack at function entry Goto label (always local): Compiler generated symbol number Static symbol: Local ref. number (unique to function) Struct/Union tag: Size (in bytes) of structure Struct/Union member: Offset of member into structure If the symbol represents an array (ARRAY bit set in ), the next value will indicate the total number of dimensions to the array. Following that are the sizes of each dimension. 6. THE MICRO-C OPTIMIZER The MICRO-C optimizer is an output code filter which examines the assembly code produced by the compiler, recognizing known patterns of inefficient code (using the "peephole" technique), and replaces them with more optimal code which performs the same function. It is entirely table driven, allowing it to be modified for virtually any processor. Due its many table lookup operations, the optimizer may perform quite slowly when processing a large file. For this reason, most people prefer not to optimize during the debugging of a program, and utilize the optimizer only when creating the final copy. 6.1 The MCO09 command The format of the MICRO-C Optimizer command line is: MCO09 [input_file] [output_file] [options] [input_file] is the name of the source file containing assembly statements to read. If no filenames are given, MCO09 will read from standard input. [output_file] is the name of the file to which the optimized assembly code is written. If less than two filenames are specified, MCO09 will write to standard output. 6.1.1 Command Line Options MCO09 accepts the following command line [options], which are recognized in upper case only: -D - Instructs MCO09 to produce a 'debug' display on standard output showing the source code which it is removing and replacing in the input file. NOTE: If you do not specify an explicit output file, you will get the debug statements intermixed with the optimized code on standard output. -Q - Instructs MCO09 to be quiet, and not display the startup message when it is executed. NOTE: "doubled up" options (eg: -qd) are not accepted. MICRO-C Page: 44 7. THE SOURCE LINKER The CUBIX system assembler does not directly support an object linker. This causes a problem with 'C' development, because the library functions must be included in the source code, with several drawbacks: 1) There is no way to automatically tell which library functions to include, therefore, you must do it manually. 2) 'C' library functions must be re-compiled every time, in order to avoid conflict between compiler generated labels. The MICRO-C Source Linker (SLINK) helps overcome these problems, by automatically joining previously compiled (assembly language) source code from the library to your programs. Only those files containing functions which you reference are joined (Taking into consideration functions called by the included library functions etc...). As the files are joined, compiler generated labels are adjusted to be unique within each file. 7.1 The SLINK Command The format of the SLINK command line is: SLINK input_file... [options] output_file "input_file" is the name of the source file containing the compiler output from your program. Several input files may be specified, in which case they will all be processed into a single output file. "output_file" is the name of the file to which the linked source code is written. MICRO-C Page: 45 7.1.1 Command line options SLINK accepts the following command line [options]: ? - Display command line help summary. -C - Removes comments from the output file l=path - Specify directory path taken to reference the library files. If not given, defaults to: '[MC]MCLIB.LIB' -Q - Inhibit display of the startup message. NOTE: "doubled up" options (eg: -cq) are not accepted. 7.2 Multiple Input Files SLINK accepts multiple input files on the command line, and processes each file to build the output file. This allows you to "link" together several previously compiled (or assembly language) modules into one final program. Any external references which are not resolvable from the library are assumed to be resolved by one of the input source files. Since SLINK does not have knowledge of the public symbols defined in the input files, the absence of an externally referenced symbol will not be detected until the assembly step (where it will cause an undefined symbol error). MICRO-C Page: 46 7.3 The Library File SLINK uses a special file as the library of code from which to resolve external symbols. This is called the LIBRARY file (see 'L=' option). This file contains blocks of code, which are identified by a line containing one of the following markers: 1) Blocks beginning with '<' indicate code blocks which are always included at the BEGINNING of the prgoram (Before your source file(s)). This the best way to include the startup code and any runtime library routines which are required at all times. 2) Blocks beginning with '^' indicate code blocks which are to be included AFTER the program and library source files, but BEFORE any uninitialized data areas are output. This is the best way to set up the location and storage class of the uninitialized data if it does not immediately follow the executable program code. 3) Blocks beginning with '>' indicate code blocks which are to be included at the END of the program, after all other information is output. This is the best way to define heap memory storage, and to provide any post-amble needed by the assembler. 4) Blocks beginning with '-' indicate optional code blocks, included only if a global symbol occuring within that block is referenced within the remainder of the program. For a complete example, please refer to the [MC]MCLIB.LIB library file included with the MICRO-C/CUBIX distribution. In summary, the output file is written from: 1 - The '<' (prefix) blocks * 2 - The program source file(s) *\ 3 - Library blocks referenced (if any) * > See note 4 - The '^' (middle) blocks */ 5 - Uninitialized data definitions (if any) 6 - The '>' (suffix) files MICRO-C Page: 47 7.4 Source file information 7.4.1 SLINK Directives SLINK interprets two "directives" which may be inserted in the input source files or library blocks to control the source linking process. These directives must be on a separate line, beginning in column 1, and must be in uppercase. They are removed by SLINK during processing, and thus will not cause conflict with the normal syntax used by the assembler. $DD: The '$DD' directive is used to define uninitialized data storage areas, which will be allocated by SLINK between the '^' (middle) and '>' (suffix) files. This allows you to allocate uninitialized data outside of the bounds of the executable image, and thus exclude it from being saved to disk. $EX: The '$EX' directive is used by SLINK to identify any symbols which are externally referenced. Whenever a '$EX' directive is found, SLINK searches the LIBRARY for the named symbol, and marks the corresponding block for inclusion in the program. Notes: - $EX directives will have no effect in MIDDLE or SUFFIX files, because the external references have already been resolved by the time they are processed. - $DD directives will have no effect in SUFFIX files, because uninitialized storage has already been allocated at that point. 7.4.2 Compiler generated labels As it processes each source file or library block, SLINK scans each line for symbols which consist of the '?' character, followed by a number. If it finds such as symbol, it inserts a two character sequence ranging from 'AA' to 'ZZ' between the '?', and the number. This sequence will be incremented for each source file processed, and thus insures that the compiler generated symbols will remain unique for each file. If you are writing assembly language programs for the library, you must be careful to avoid using identical local symbols in any of the library files, one way to do this is to use symbols which meet the above criteria. MICRO-C Page: 48 +--------------------------------------+ | | | ********************************** | | * The MICRO-C CUBIX/6809 library * | | ********************************** | | | +--------------------------------------+ 8. The MICRO-C/CUBIX Library The library functions described on the following pages are currently available in the MICRO-C CUBIX/6809 library as "standard" functions. ABORT ABORT PROTOTYPE: abort(char *message) ARGUMENTS: message - Pointer to message to display RETURN VALUE: N/A - Function never returns DESCRIPTION: This function writes the string passed as an argument to standard error, and then terminates the program. This provides a simple method of terminating a program on an error condition with a message explaining why. EXAMPLES: abort("Invalid operand\n"); ABS ABS PROTOTYPE: int abs(int number) ARGUMENTS: number - Any integer value RETURN VALUE: The absolute value of "number" DESCRIPTION: The "abs" function returns the absolute value of the argument. If "number" is a positive value, it is returned unchanged. If negative, the negate of that value is returned (giving a positive result). EXAMPLES: difference = abs(value1 - value2); ATOI ATOI PROTOTYPE: int atoi(char *string) ARGUMENTS: string - Pointer to a string containing a decimal number RETURN VALUE: 16 bit integer value DESCRIPTION: The "atoi" function converts an ASCII string containing a signed decimal number (-32768 to 32767) to a 16 bit value which is returned. An unsigned number of the range (0 to 65535) may also be used, and the result if assigned to an "unsigned" variable will be correct. EXAMPLES: value = atoi("1234"); value = atoi("-1"); CHKCH CHKCH PROTOTYPE: int chkch() ARGUMENTS: None RETURN VALUE: 0 - No character available from console input device. !0 - Character read from console input device. DESCRIPTION: The "chkch" function checks to see if a character is ready to be received from the console input device. If no character is available, a zero (0) is returned, otherwise the character is read and its value is passed back. The CARRIAGE RETURN character (0x0D) will be translated into a NEWLINE (0x0A) for compatibility with 'C'. EXAMPLES: if(chkch() == 0x1B) /* Escape KEY pressed ? */ return; CHKCHR CHKCHR PROTOTYPE: int chkchr() ARGUMENTS: None RETURN VALUE: 0-255 - Character read from console input device. -1 - No character was available. DESCRIPTION: The "chkch" function checks to see if a character is ready to be received from the console input device. If no character is available, a -1 is returned, otherwise the character is read and its "raw" value is passed back (without translation). EXAMPLES: if(chkchr() == '\r') /* Return KEY pressed ? */ return; CONCAT CONCAT PROTOTYPE: register concat(char *dest, char *source, ...) ARGUMENTS: dest - Pointer to destination string source - Pointer to source string ... - Additional sources may be given RETURN VALUE: None DESCRIPTION: The "concat" function concatenates the given source strings into one destination string. The destination string must be large enough to hold all of the source strings plus the string terminator (zero) byte. No value is returned. NOTE: This function uses a variable number of arguments, and must be declared as "register" (See stdioio.h"). EXAMPLES: concat(filename,"[DIR]", input_name); DELETE DELETE PROTOTYPE: int delete(char *pathname) ARGUMENTS: pathname- Name of file to delete RETURN VALUE: 0 if successful, otherwise an operating system error code DESCRIPTION: The "delete" function removes an existing file from the disk. Any disk space occupied by the file is released. EXAMPLES: delete("temp"); DISABLE DISABLE PROTOTYPE: disable() ARGUMENTS: None RETURN VALUE: None DESCRIPTION: The "disable" function disables the 6809 standard interrupt system, preventing the processor from servicing interrupts. It is used whenever the execution of an interrupt handler may interfere with a particular operation. When this function is used, the "enable" function should be called as soon as possible after "disable". Failure to do this may result in loss of system functions performed under interrupts. EXAMPLES: disable(); /* Disallow interrupts */ /* ... */ enable(); /* Re-allow interrupts */ DISABLEF DISABLEF PROTOTYPE: disablef() ARGUMENTS: None RETURN VALUE: None DESCRIPTION: The "disablef" function disables the 6809 fast interrupt system, preventing the processor from servicing interrupts. It is used whenever the execution of an interrupt handler may interfere with a particular operation. When this function is used, the "enablef" function should be called as soon as possible after "disablef". Failure to do this may result in loss of system functions performed under interrupts. EXAMPLES: disablef(); /* Disallow interrupts */ /* ... */ enablef(); /* Re-allow interrupts */ ENABLE ENABLE PROTOTYPE: enable() ARGUMENTS: None RETURN VALUE: None DESCRIPTION: The "enable" function enables the 6809 standard interrupt system, allowing the processor to service interrupts. It should be called as soon as possible following the use of the "disable" function. EXAMPLES: disable(); /* Disallow interrupts */ /* ... */ enable(); /* Re-allow interrupts */ ENABLEF ENABLEF PROTOTYPE: enablef() ARGUMENTS: None RETURN VALUE: None DESCRIPTION: The "enablef" function enables the 6809 fast interrupt system, allowing the processor to service interrupts. It should be called as soon as possible following the use of the "disablef" function. EXAMPLES: disablef(); /* Disallow interrupts */ /* ... */ enablef(); /* Re-allow interrupts */ EXIT EXIT PROTOTYPE: exit(unsigned char rc) ARGUMENTS: rc - Return (exit) code value RETURN VALUE: N/A - Function never returns DESCRIPTION: This function terminates the execution of the program, and returns control to the CUBIX operating system. EXAMPLES: exit(2); /* Terminate with exit code 2 */ FCLOSE FCLOSE PROTOTYPE: int fclose(FILE *fp) ARGUMENTS: fp - File pointer to an open file RETURN VALUE: 0 if successful, otherwise an operating system error code DESCRIPTION: This function closes a file which was previously opened using "fopenr" or "fopenw". The I/O buffer space used by the file is released. In the case of a file open for write ('w'), the last disk buffer is flushed and written to disk. EXAMPLES: fclose(fp); FGETB FGETB PROTOTYPE: int fgetb(char *buffer, int size, FILE *fp) ARGUMENTS: buffer - Pointer to buffer to receive data size - Number of bytes to read fp - File pointer to an input file RETURN VALUE: Number of bytes read from file DESCRIPTION: This function reads a block of data from a file and places it in memory at the address of "buffer". Data will be read in BINARY mode, which means that no translations will occur for newline or EOF. If the number of bytes returned is less than the number of bytes requested, either the end of the file was encountered or an error condition occurred (in which case the value will be zero). EXAMPLES: fgetb(block, 512, input_fp); FGETC FGETC PROTOTYPE: int fgetc(FILE *fp) ARGUMENTS: fp - File pointer to an input file RETURN VALUE: Value of a character read from the file (0-255) EOF (-1) if end of file or an error condition occurs DESCRIPTION: This function reads a single character from an input file, and returns it as a positive value in the range of 0 to 255. A full 16 bit value is returned, allowing the end of file condition to be distinct from the character value 255. The character is read in TEXT mode, which means that the CUBIX line end (carriage-return) is translated into the newline character. Also, a 0xFF character occuring in the file is treated as End-Of-File. EXAMPLES: if((c = fgetc(input_file)) == EOF) abort("End of file encountered\n"); FGETR FGETR PROTOTYPE: int fgetc(FILE *fp) ARGUMENTS: fp - File pointer to an input file RETURN VALUE: Value of a character read from the file (0-255) EOF (-1) if end of file or an error condition occurs DESCRIPTION: This function reads a single character from an input file, and returns it as a positive value in the range of 0 to 255. A full 16 bit value is returned, allowing the end of file condition to be distinct from the character value 255. The character is read in BINARY mode, which means that no data translations will occur. EXAMPLES: if((c = fgetr(input_file)) == EOF) abort("Physical End of file encountered\n"); FGETS FGETS PROTOTYPE: char *fgets(char *buffer, int size, FILE *fp) ARGUMENTS: buffer - Pointer to string to receive line size - Maximum size of line to read fp - File pointer to an input file RETURN VALUE: Pointer to "buffer", or 0 if end of file DESCRIPTION: The "fgets" function reads characters from the specified input file, and places them in the character buffer until one of three things happens: 1) A NEWLINE character is encountered. 2) The END of the file is encountered. 3) The limit of "size" character is read. The string is terminated with the standard NULL (00) character. The trailing NEWLINE '\n' character is NOT included in the input buffer. The line is read in TEXT mode, which means that the CUBIX line end (carriage-return) is translated into the newline character. Also, a 0xFF character occuring in the file is treated as End-Of-File. MICRO-C's implementation of this function differs from ANSI standards in that it does not include the NEWLINE '\n' character in the input buffer. EXAMPLES: fgets(input_line, 80, input_file); FPRINTF FPRINTF PROTOTYPE: register fprintf(FILE *fp, char *format, arg, ...) ARGUMENTS: fp - File pointer to an output file format - Pointer to format string arg - Argument as determined by format string ... - Additional arguments may be required RETURN VALUE: None DESCRIPTION: This routine performs a formatted print to a file specified by 'fp'. The 'format' string is written to the file with the arguments substituted for special "conversion characters". See "fprintf" for more information on format strings. NOTE: This function uses a variable number of arguments, and must be declared as "register" (See "stdio.h"). EXAMPLES: fprintf(stderr,"Filename='%s'\n", filename); fprintf(stdout,"Address=%04x\n", address); fprintf(outfile,"Amount: $%3u.%02u\n", amount / 100, amount % 100); FPUTB FPUTB PROTOTYPE: int fputb(char *block, int size, FILE *fp) ARGUMENTS: block - Pointer to a block of data to write size - Number of bytes to write fp - File pointer to an output file RETURN VALUE: None DESCRIPTION: This function writes a block of data to the indicated file from memory at the specified address. Data is written in BINARY mode with no newline or EOF translations. EXAMPLES: fputb(buffer, 100, fp); FPUTC FPUTC PROTOTYPE: fputc(char c, FILE *fp) ARGUMENTS: c - Any character value fp - File pointer to an output file RETURN VALUE: Operating system error code DESCRIPTION: This function writes the character 'c' to the file indicated by the file pointer 'fp' in TEXT mode. This means that the '\n' newline character is translated to the 0x0D carriage-return used by CUBIX. EXAMPLES: fputc('*', fp); fputc('\n', stderr); FPUTR FPUTR PROTOTYPE: fputr(char c, FILE *fp) ARGUMENTS: c - Any character value fp - File pointer to an output file RETURN VALUE: Operating system error code DESCRIPTION: This function writes the character 'c' to the file indicated by the file pointer 'fp' in BINARY mode. No translations are made on the character value. EXAMPLES: fputr(0x01, fp); fputr(0x0A, fp); FPUTS FPUTS PROTOTYPE: fputs(char *string, FILE *fp) ARGUMENTS: string - Pointer to a character string fp - FIle pointer to output file RETURN VALUE: Operating system error code DESCRIPTION: The "fputs" function writes the specified string to the indicated output file. The zero terminating the string is NOT written. EXAMPLES: fputs("Text message", output_file); FOPENR FOPENR PROTOTYPE: FILE *fopenr(char *filename) ARGUMENTS: filename- Name of the file to open RETURN VALUE: File pointer to the file buffer for the open file Zero (0) if file could not be opened DESCRIPTION: This function opens a file for buffered input (read) allowing subsequent I/O operations to read the file. This function automatically allocates a 522 byte File Control Block (FCB) which is used to access the file. EXAMPLES: fp1 = fopenr("input_file"); fp2 = fopenw("output_file"); FOPENW FOPENW PROTOTYPE: FILE *fopenw(char *filename) ARGUMENTS: filename- Name of the file to open RETURN VALUE: File pointer to the file buffer for the open file Zero (0) if file could not be opened DESCRIPTION: This function opens a file for buffered output (write) allowing subsequent I/O operations to write the file. This function automatically allocates a 522 byte File Control Block (FCB) which is used to access the file. EXAMPLES: fp1 = fopenr("input_file"); fp2 = fopenw("output_file"); FREE FREE PROTOTYPE: free(char *ptr) ARGUMENTS: ptr - Pointer to a previously allocated memory block RETURN VALUE: None DESCRIPTION: The "free" function releases (deallocates) a block of memory that was obtained via a call to "malloc", and returns it to the heap. This makes it available for use by other memory allocations. EXAMPLES: if(!(ptr = malloc(BUFSIZ))) /* Allocate a temporary buffer */ abort("Not enough memory"); /* ... Use the memory block ... */ free(ptr); /* Release temporary buffer */ GETCH GETCH PROTOTYPE: int getch() ARGUMENTS: None RETURN VALUE: Value of a character read from the console input device. DESCRIPTION: This function reads a single character from the console input device, and returns it as a positive value in the range of 0 to 255. If the character is a carriage return (0x0D), is translated into a NEWLINE (0x0A) for compatibility with 'C'. EXAMPLES: while(getch() != 0x1B); /* Wait for an ESCAPE character */ GETCHR GETCHR PROTOTYPE: int getchr() ARGUMENTS: None RETURN VALUE: Value of a character read from the console input device. DESCRIPTION: This function reads a single character from the console input device, and returns it as a positive value in the range of 0 to 255. The character is read in "raw" format, with no translations performed. EXAMPLES: while(getchr() != '\r'); /* Wait for a RETURN */ GETSTR GETSTR PROTOTYPE: char *getstr(char *buffer, int size) ARGUMENTS: buffer - Pointer to string to receive line size - Maximum size of line to read RETURN VALUE: The number of characters received into the line. DESCRIPTION: The "getstr" function reads a string of character from the console input device and places them into the specified character buffer until either a NEWLINE character is encountered, or the limit of "size" character is read. The string is terminated with the standard NULL (00) character. The trailing NEWLINE '\n' character is NOT included in the output buffer. The BACKSPACE or DEL keys can be used to edit the input line. EXAMPLES: getstr(input_line, 80); ISALNUM ISALNUM PROTOTYPE: int isalnum(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is alphabetic or numeric 0 if 'c' is not alphabetic or numeric DESCRIPTION: Returns TRUE (1) if the passed character 'c' is an ASCII alphabetic letter in either upper or lower case or if 'c' is a numeric digit, otherwise FALSE (0) is returned. EXAMPLES: while(isalnum(*ptr)) /* Copy over symbol name */ *name++ = *ptr++; ISALPHA ISALPHA PROTOTYPE: int isalpha(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is alphabetic 0 if 'c' is not alphabetic DESCRIPTION: Returns TRUE (1) if the passed character 'c' is an ASCII alphabetic letter in either upper or lower case, otherwise FALSE (0) is returned. EXAMPLES: flag = isalpha(input_char); ISASCII ISASCII PROTOTYPE: int isascii(int c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is an ASCII character 0 if 'c' is not an ASCII character DESCRIPTION: Returns TRUE (1) if the passed character 'c' is a valid ASCII character (0x00-0xFF), otherwise FALSE (0) is returned. EXAMPLES: if(!isascii(*ptr)) abort("Invalid character data"); ISCNTRL ISCNTRL PROTOTYPE: int iscntrl(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is a "control" character 0 if 'c' is not a "control" character DESCRIPTION: Returns TRUE (1) is the passed character 'c' is an ASCII "control" character (0x00-0x1F or 0x7F), otherwise FALSE (0) is returned. EXAMPLES: putch(iscntrl(c) ? '.' : c); /* Display controls as '.' */ ISDIGIT ISDIGIT PROTOTYPE: int isdigit(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is numeric 0 if 'c' is not numeric DESCRIPTION: Returns TRUE (1) is the passed character 'c' is an ASCII digit ('0'-'9'), otherwise FALSE (0) is returned. EXAMPLES: value = 0; while(isdigit(*ptr)) value = (value * 10) + (*ptr++ - '0'); ISGRAPH ISGRAPH PROTOTYPE: int isgraph(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is a non-space printable character 0 if 'c' is a space or not printable DESCRIPTION: Returns TRUE (1) if the passed character 'c' is a printable ASCII character other than a space character, otherwise FALSE (0) is returned. EXAMPLES: putch(isgraph(c) ? c : '.'); ISLOWER ISLOWER PROTOTYPE: int islower(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is lower case alphabetic 0 if 'c' is not lower case alphabetic DESCRIPTION: Returns TRUE (1) if the passed character 'c' is an ASCII alphabetic letter of lower case, otherwise FALSE (0) is returned. EXAMPLES: flag = islower(input_char); ISPRINT ISPRINT PROTOTYPE: int isprint(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is a printable character 0 if 'c' is not printable DESCRIPTION: Returns TRUE (1) if the passed character 'c' is a printable ASCII character, otherwise FALSE (0) is returned. EXAMPLES: while(isprint(*ptr)) ++ptr; ISPUNCT ISPUNCT PROTOTYPE: int ispunct(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is a printable non-alphanumeric character 0 if 'c' is not printable or alphanumeric DESCRIPTION: Returns TRUE (1) if the passed character 'c' is a printable ASCII character which is not a letter of the alphabet or a numeric digit, otherwise FALSE (0) is returned. EXAMPLES: while(ispunct(*ptr)) ++ptr; ISSPACE ISSPACE PROTOTYPE: int isspace(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is a space character (space, tab or newline) 0 if 'c' is not a space character DESCRIPTION: Returns TRUE (1) if the passed character 'c' is one of a space, tab or newline, otherwise FALSE (0) is returned. EXAMPLES: while(isspace(*ptr)) ++ptr; ISUPPER ISUPPER PROTOTYPE: int isupper(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is upper case alphabetic 0 if 'c' is not upper case alphabetic DESCRIPTION: Returns TRUE (1) if the passed character 'c' is an ASCII alphabetic letter of upper case, otherwise FALSE (0) is returned. EXAMPLES: flag = isupper(input_char); ISXDIGIT ISXDIGIT PROTOTYPE: int isxdigit(char c) ARGUMENTS: c - Any character value RETURN VALUE: 1 if 'c' is a hexadecimal digit 0 if 'c' is not a hexadecimal digit DESCRIPTION: Returns TRUE (1) is the passed character 'c' is an valid ASCII hexadecimal digit ('0'-'9', 'A'-'F', 'a'-'f'), otherwise FALSE (0) is returned. EXAMPLES: value = 0; while(isxdigit(*ptr)) value = (value * 16) + (isdigit(*ptr) ? *ptr++ - '0' : toupper(*ptr++) - ('A'-10)); LONGJMP LONGJMP PROTOTYPE: longjmp(int savenv[2], int rvalue) ARGUMENTS: savenv - Save area for program context rvalue - Value to be returned by "setjmp" RETURN VALUE: N/A - Function never returns DESCRIPTION: The "longjmp" function causes execution to transfer to the "setjmp" call which set up the "savenv" variable. The "setjmp" function will appear to return the value of "rvalue". NOTE-1: "longjmp" may only be used from within the function calling "setjmp" or a function which has been called "beneath" that function. IT MUST NOT BE USED AFTER THE FUNCTION CALLING "SETJMP" HAS TERMINATED. NOTE-2: If "rvalue" is zero, the function calling "setjmp" will assume that it is returning from initialization. Unless you want this unusual behavior, you should not pass a return value of zero to "longjmp". See also SETJMP. EXAMPLES: if(getch() == ('C'-'@')) /* If Control-C entered... */ longjmp(savearea, 1); /* Return to main function */ LONGMATH LONGMATH PROTOTYPES: int longadd(char *num1, char *num2); int longsub(char *num1, char *num2); longmul(char *num1, char *num2); longdiv(char *num1, char *num2); int longshr(char *num1); int longshl(char *num1); longcpy(char *num1, char *num2); longset(char *num1, unsigned value); int longtst(char *num1); int longcmp(char *num1, char *num2); extern char Longreg[]; ARGUMENTS: num1 - First LONG operand, receives result if generated num2 - Second LONG operand, is not altered value - 16 bit value to initialize LONG number RETURN VALUE: longadd - 0 = Ok, 1 = addition overflowed longsub - 0 = Ok, 1 = subtraction underflowed longshr - Carry out of shift (1/0) longshl - Carry out of shift (1/0) longtst - 0 = Number is zero, !0 = Number is not zero longcmp - 0 = (num1 == num2), 1 = (num1 > num2), -1 = (num1 < num2) DESCRIPTION: This set of functions performs basic arithmetic functions on LONG numbers: longadd(num1, num2) -> num1 += num2 longsub(num1, num2) -> num1 -= num2 longmul(num1, num2) -> num1 *= num2 , Longreg = num1 * num2 longdiv(num1, num2) -> num1 /= num2 , Longreg = num1 % num2 longshr(num1) -> num1 >>= 1 longshl(num1) -> num1 <<= 1 longcpy(num1, num2) -> num1 = num2 longset(num1, value) -> num1 = (long) value longtst(num1) -> Test (num1 != 0) longcmp(num1, num2) -> Compare num1 and num2 As shipped, a LONG number is 32 bits (4 bytes) in size, however this can be changed by altering the LONGMATH.ASM in the library. For a complete example of using these functions, refer to the LONGCALC.C example program included with the package. MALLOC MALLOC PROTOTYPE: char *malloc(int size) ARGUMENTS: size - Size of memory block to allocate (in bytes). RETURN VALUE: 0 - Memory allocation failed !0 - Pointer to allocated memory block DESCRIPTION: The "malloc" function allocates a block of memory of the specified size from the heap, and returns a pointer to it. This memory will remain allocated until it is explicitly released with the "free" function. EXAMPLES: if(!(ptr = malloc(BUFSIZ))) /* Allocate a temporary buffer */ abort("Not enough memory"); /* ... Use the memory block ... */ free(ptr); /* Release temporary buffer */ MAX MAX PROTOTYPE: int max(int value1, int value2) ARGUMENTS: value1 - Any integer value value2 - Any integer value RETURN VALUE: The greater of "value1" or "value2" DESCRIPTION: The "max" function returns the higher of its two argument values. EXAMPLES: biggest = max(a, b); MEMCPY MEMCPY PROTOTYPE: memcpy(char *dest, char *source, unsigned size) ARGUMENTS: dest - Pointer to the destination source - Pointer to the source size - Number of bytes to copy RETURN VALUE: None DESCRIPTION: The "memcpy" function will copy the specified number of bytes from the source to the destination. EXAMPLES: memcpy(buffer1, buffer2, 256); MEMSET MEMSET PROTOTYPE: memset(char *block, char value, unsigned size) ARGUMENTS: block - Pointer to a block of memory value - Value to initialize memory with size - Number of bytes to initialize RETURN VALUE: None DESCRIPTION: Sets a block of memory beginning at the pointer "block", for "size" bytes to the byte value "value". EXAMPLES: memset(buffer, 0, 100); MIN MIN PROTOTYPE: int min(int value1, int value2) ARGUMENTS: value1 - Any integer value value2 - Any integer value RETURN VALUE: The smaller of "value1" or "value2" DESCRIPTION: The "min" function returns the lower of its two argument values. EXAMPLES: least = min(a, b); NARGS NARGS PROTOTYPE: int nargs() ARGUMENTS: None RETURN VALUE: The number of arguments passed to the calling function DESCRIPTION: Returns the number of arguments passed to a "register" function. NOTE: When calling a "register" function, MICRO-C loads the accumulator with the number of arguments just prior to calling the function. This "nargs" routine is simply a null definition which returns with the same value in the accumulator as was there when it was called. Therefore "nargs" MUST BE THE FIRST ARITHMETIC ENTITY EVALUATED WITHIN THE REGISTER FUNCTION or the contents of the accumulator will be lost. Some examples of "register" definitions and the use of "nargs" may be found in the library source code. EXAMPLES: first_arg = nargs() * 2 + &arguments; PEEK PEEK PROTOTYPE: int peek(unsigned address) ARGUMENTS: address - 16 bit memory address RETURN VALUE: The 8 bit value read from the given memory address DESCRIPTION: The "peek" function reads and returns a byte (8 bits) from memory as an integer value between 0 and 255. EXAMPLES: while(peek(0)); /* Wait for flag to clear */ PEEKW PEEKW PROTOTYPE: int peekw(unsigned address) ARGUMENTS: address - 16 bit memory address RETURN VALUE: The 16 bit value read from the given memory address DESCRIPTION: The "peekw" function reads and returns a word (16 bits) from memory as an integer value between 0 and 65535 (-1). EXAMPLES: var = peekw(0)); POKE POKE PROTOTYPE: poke(unsigned address, int value) ARGUMENTS: address - 16 bit memory address value - Value to be written to memory RETURN VALUE: None DESCRIPTION: The "poke" function writes a byte (8 bit) value to memory. EXAMPLES: poke(0, 0); /* Write 0 to location 0 */ POKEW POKEW PROTOTYPE: pokew(unsigned address, int value) ARGUMENTS: address - 16 bit memory address value - Value to be written to memory RETURN VALUE: None DESCRIPTION: The "pokew" function writes a word (16 bit) value to memory. EXAMPLES: pokew(0, 0); /* Write 0 to locations 0 and 1 */ PRINTF PRINTF PROTOTYPE: register printf(char *format, arg, ...) ARGUMENTS: format - Pointer to format string arg - Argument as determined by format string ... - Additional arguments may be required RETURN VALUE: None DESCRIPTION: The "printf" function performs a formatted print to console serial port. The 'format' string is written to the serial port with the arguments substituted for special "conversion characters". These "conversion characters" are identified by a preceding '%', and may be one of the following: b - Binary number c - Character d - Decimal (signed) number o - Octal number s - String u - Unsigned decimal number x - Hexadecimal number % - A single percent sign (No argument used) A numeric "field width" specifier may be placed in between the '%' and the conversion character, in which case the value will be output in a field of that width. If the "field width" is a negative number, the output will be left justified in the field, otherwise it is right justified. If the field width contains a leading '0', then the output field will be padded with zeros, otherwise spaces are used. If no "field width" is given, the output is free format, using only as much space as required. NOTE: This function uses a variable number of arguments, and must be declared as "register" (See "6809io.h"). EXAMPLES: printf("Hello world!!!\n"); printf("%u interrupts have occurred!\n", int_count); PUTCH PUTCH PROTOTYPE: putch(char c) ARGUMENTS: c - Any character value RETURN VALUE: None DESCRIPTION: This function writes the character 'c' to the console output device. The NEWLINE character is translated into a LINE-FEED, followed by a CARRIAGE return, which causes the printing position to advance to the first column of the next line. EXAMPLES: putch('*'); putch('\n'); PUTCHR PUTCHR PROTOTYPE: putchr(char c) ARGUMENTS: c - Any character value RETURN VALUE: None DESCRIPTION: This function writes the character 'c' to the console output device. The character is written in "raw" format, with no translations of any kind. EXAMPLES: putchr('*'); putchr('\r'); PUTSTR PUTSTR PROTOTYPE: putstr(char *string) ARGUMENTS: string - Pointer to a character string RETURN VALUE: None DESCRIPTION: The "putstr" function writes the specified string to the console output device. The zero terminating the string is NOT written. EXAMPLES: putstr("Text message"); RAND RAND PROTOTYPE: unsigned rand(unsigned limit) ARGUMENTS: limit - Maximum value to return RETURN VALUE: A pseudo-random number in the range of 0 to (limit-1) DESCRIPTION: The "rand" function calculates the next value of a pseudo-random sequence, based on a 16 bit unsigned "seed" value, which it maintains in the global variable "RANDSEED". The new value is stored as the new seed value, and is then divided by the "limit" parameter, to obtain the remainder, which is returned. This results in a random number in the range of zero (0) to (limit - 1). Any particular sequence may be repeated, by resetting the "RANDSEED" value. EXAMPLES: value = rand(52); SETJMP SETJMP PROTOTYPE: int setjmp(int savenv[2]) ARGUMENTS: savenv - Save area for program context RETURN VALUE: 0 is returned when actually called Value passed to "longjmp" is returned otherwise DESCRIPTION: When called, the "setjmp" function stores the current execution state of the program into the passed integer array, and returns the value zero. The "longjmp" function may then be used to return the program to the "setjmp" call. In this case, the value returned by "setjmp" will be the value which was passed to "longjmp". This allows the function containing "setjmp" to determine which call to "longjmp" transferred execution to it. See also LONGJMP. EXAMPLES: switch(setjmp(savearea)) { case 0 : printf("Longjmp has been set up"); break; case 1 : printf("Control-C Interrupt"); break; case 2 : printf("Reset command executed"); break; default: printf("Software error trap"); break; } SPRINTF SPRINTF PROTOTYPE: register sprintf(char *dest, char *format, arg, ...) ARGUMENTS: dest - Pointer to destination string format - Pointer to format string arg - Argument as determined by format string ... - Additional arguments may be required RETURN VALUE: None DESCRIPTION: The "sprintf" routine performs a formatted print to a string in memory. The "format" string is written to the destination string with the arguments substituted for special "conversion characters". See "printf" for more information on format strings. NOTE: This function uses a variable number of arguments, and must be declared as "register" (See "6809io.h"). EXAMPLES: sprintf(header_file, "/lib/%s.h", header_name); SQRT SQRT PROTOTYPE: int sqrt(unsigned value) ARGUMENTS: value - Number for which to calculate square root RETURN VALUE: The integer square root (rounded up) of the argument value DESCRIPTION: The SQRT function returns the smallest number which when multiplied by itself will give a number equal to or larger that the argument value. EXAMPLES: /* * Draw a circle about point (x, y) of radius (r) */ circle(x, y, r) int x, y, r; { int i, j, k, rs, lj; rs = (lj = r)*r; for(i=0; i <= r; ++i) { j = k = sqrt(rs - (i*i)); do { plot_xy(x+i, y+j); plot_xy(x+i, y-j); plot_xy(x-i, y+j); plot_xy(x-i, y-j); } while(++j < lj); lj = k; } } STRCAT STRCAT PROTOTYPE: char *strcat(char *dest, char *source) ARGUMENTS: dest - Pointer to destination string source - Pointer to source string RETURN VALUE: Pointer to zero terminating destination string DESCRIPTION: This function concatenates the source string on to the tail of the destination string. The destination string must be large enough to hold the entire contents of both strings. EXAMPLES: strcat(program_name, ".c"); STRCHR STRCHR PROTOTYPE: char *strchr(char *string, char chr) ARGUMENTS: string - Pointer to a character string chr - Character to look for RETURN VALUE: Pointer to the first occurrence of 'chr' in 'string' Zero (0) if character was not found DESCRIPTION: Searches the passed string got the first occurrence of the specified character. If the character is found, a pointer to its position in the string is returned. If the character is not found, a null pointer is returned. The null (0) character is treated as valid data by this function, thus: strchr(string, 0); would return the position of the null terminator of the string. EXAMPLES: comma = strchr(buffer, ','); STRCMP STRCMP PROTOTYPE: int strcmp(char *string1, char *string2) ARGUMENTS: string1 - Pointer to first string string2 - Pointer to second string RETURN VALUE: 0 - Strings match exactly 1 - String1 is greater than string2 -1 - String2 is greater than string1 DESCRIPTION: This function compares two strings character by character. If the two strings are identical, a zero (0) is returned. If the first string is greater than the second (as far as ASCII is concerned), a one (1) is returned. If the second string is greater, a negative one (-1) is returned. EXAMPLES: if(!strcmp(command, "quit")) exit(0); STRCPY STRCPY PROTOTYPE: char *strcpy(char *dest, char *source) ARGUMENTS: dest - Pointer to destination string source - Pointer to source string RETURN VALUE: Pointer to zero terminating destination string DESCRIPTION: This function copies the source string to the destination string. All data is copied up to and including the zero byte which terminates the string. The destination string must be large enough to hold the entire source. EXAMPLES: strcpy(filename, argv[1]); STRLEN STRLEN PROTOTYPE: int strlen(char *string) ARGUMENTS: string - Pointer to a character string RETURN VALUE: The length of the string DESCRIPTION: Returns the length in character of the passed string. The length does not include the zero byte which terminates the string. EXAMPLES: length = strlen(command); TEMPMEM TEMPMEM PROTOTYPE: void *tempmem() ARGUMENTS: None RETURN VALUE: Point to temporary buffer space DESCRIPTION: Returns the memory address of the first available memory location following the last data item allocated on the heap. This can be used as a temporary buffer consisting of all available unallocated memory. Execution of a malloc() function, or any function which uses malloc() [Fopenr, Fopenw etc.] may allocate memory within the area returned by tempmem() - in general, tempmem() should be called every time the temporary memory is used to insure that heap allocation does not get corrupted. EXAMPLES: ptr = tempmem(); TOLOWER TOLOWER PROTOTYPE: char tolower(char c) ARGUMENTS: c - Any character value RETURN VALUE: The value of 'c', converted to lower case DESCRIPTION: Returns the value of 'c' converted to lower case. If 'c' is not a letter of upper case, no change is made, and the original value of 'c' is returned. EXAMPLES: input_char = tolower(getch()); TOUPPER TOUPPER PROTOTYPE: char toupper(char c) ARGUMENTS: c - Any character value RETURN VALUE: The value of 'c', converted to upper case DESCRIPTION: Returns the value of 'c' converted to upper case. If 'c' is not a letter of lower case, no change is made, and the original value of 'c' is returned. EXAMPLES: putch(toupper(output_char));