home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Vectronix 2
/
VECTRONIX2.iso
/
FILES_10
/
DEVELOP.LZH
/
DSP
/
C56K
/
MC.DOC
< prev
Wrap
Text File
|
1979-12-09
|
132KB
|
3,296 lines
MICRO-C
A compact 'C' compiler
for small systems.
Release 1.2
Revised 28-Jul-89
By
Dave Dunfield
MICRO-C Page: 1
1. INTRODUCTION
MICRO-C is a compact, portable compiler for a subset of the 'C'
language which is suitable for implementation on small 8 or 16 bit
computer systems. It may be used as a resident or cross compiler, and
is capable of generating ROMable code. It is distributed in source
form, and thus provides a learning tool for those wishing to examine
the internals of a working compiler.
The main goal in the development of MICRO-C, has been to provide a
reasonably powerful language which will be portable with little
difficulty to many target systems. The 'C' language was designed to
be portable, and provides ample programming power, hence it was
chosen. MICRO-C provides an alternative to interpreted BASIC or
assembly language programming which are often the only languages
available on small 8 bit systems.
With today's focus on larger computers and workstations, software
support for small systems and micro-controller sized devices is
getting hard to find. MICRO-C helps fills that gap, because all files
necessary to port the compiler are included on the distribution disk,
allowing it to be moved to ANY system without dependance on a
software vendor.
Also included with the compiler is the 'C' source code for some
common "library" routines, as well as code generators for a few
popular microprocessors.
Users writing new code generators and/or library routines are
encouraged to send them back to me for inclusion in the MICRO-C
distribution disk.
The MICRO-C "package" (software and documentation) is free to
anyone wishing to use it, provided that no part of it is
re-distributed for profit or other commercial purposes without my
written permission. Although no fee is charged for the use of
MICRO-C, I retain ownership and copyright of the programs, source
files and documents.
Users are encouraged to copy and re-distribute MICRO-C as long as
no fee (other than "usual" system access or disk copy service fee) is
charged. I also request that any changes to the original package be
clearly marked as such.
MICRO-C is provided on an "as is" basis, with no warranty of any
kind. In no event shall the author be liable for any damages arising
from its use or distribution.
Dave Dunfield
56 Burnetts Grove Circle,
Nepean, Ont.
K2J 1N6
(613) 825-0257
MICRO-C Page: 2
1.1 Code Portability
With a few exceptions, this compiler follows the syntax of the
"standard" UNIX compiler (within its subset of the language).
Programs written in MICRO-C should compile with few changes under
other "standard" compilers.
1.1.1 Unsupported Features:
MICRO-C does not currently support the following features of
standard 'C':
Long, Double, Float and Enumerated data types, Structures,
Unions, Typedef, Typecast and Bit fields.
1.1.2 Additional Features
MICRO-C provides a few additional features which are not
always included in "standard" 'C' compilers:
Unsigned character variables, Nested comments, 16 bit
character constants, Inline assembly code capability.
1.2 Compiler Portability
MICRO-C is written in standard 'C', and is capable of compiling
itself. This allows any system with a 'C' compiler (including
MICRO-C) to be used to port MICRO-C to another processor.
The parser makes very few assumptions about the target
processor or operating system architecture, allowing a code
generator to be written for virtually any processor and
environment.
With the exception of required I/O routines (described later),
the MICRO-C compiler uses no library functions, and relies on no
system services.
Assuming that the code generator is fairly efficent, and that
the I/O routines and code generator are not unreasonably large, a
full MICRO-C compiler may be implemented on systems with as little
as 32K of free ram and a single floppy disk.
MICRO-C Page: 3
1.3 The MCC command
When created using the 'io' routines supplied on the
distribution disk, the format of the MICRO-C Compiler command line
is:
MCC [input_file] [output_file] [options]
[input_file] is the name of the source file containing 'C'
statements to read. If no filenames are given, MCC will read from
standard input.
[output_file] is the name of the file to write the generated
assembly language code to. If less than two filenames are
specified, MCC will write to standard output.
1.3.1 Command Line Options
MCC accepts the following command line [options]:
-q - Instructs MCC to be quiet, and not display the
startup message when it is executed.
1.4 Command files
Several command (batch) files are included with MICRO-C, which
simplify operation of the compiler:
The 'CC' command file compiles and assembles a single 'C'
source file, which is then linked with the librarys to produce an
executable module. This provides a "one step" compile when only
one source file is involved.
eg: CC ONEPROG
The 'CCO' command file compiles and assembles a single 'C'
source file, producting a linkable module. This allows multiple
source files used in a single program to be compiled seperately.
eg: CCO FIRST
CCO SECOND
The 'LC' command file links one or more modules produced by
'CCO' with the libraries to produce an executable module. This
module will be given the name of the first file specified in the
argument list.
eg: LC FIRST SECOND
If you wish to make use of the enhanced preprocessor (MCP) or
the optimizer (MCO), you may modify the above file to suit your
needs.
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.
This document does not attempt to teach 'C' programming, and
assumes that the reader is familiar with the language.
2.1 Constants
The following forms of constants are supported by the compiler:
<num> - Decimal number (num = 0-65535)
0<num> - Octal number (num = 0 - 177777)
0x<num> - Hexidecimal number (num = 0-ffff)
'<char>' - Character (1 or 2 characters)
"<string>" - 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)
\<num> - Octal value <num> (Max. three digits)
\x<num> - Hex value <num> (Max. two digits)
\<char> - Protect character <char> from input scanner.
2.2 Symbols
Symbol name 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
wide 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;
MICRO-C Page: 5
The "extern" modifier causes the compiler to be aware of the
existance and type of a global symbol, but not generate any code
to define 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 getc();
A symbol declared as external may be re-declared as a
non-external at a later point in the code, in which case code 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
properly used before that symbol is actually declared.
The "static" modifier causes global variables to be generated
at the point in the code where they are defined, causing them to
be included in the object code image when it is saved to disk.
Static variables are automatically initialzed to zero by the
compiler. Static variables within a function definition are not
allowed.
eg: static int variable_name;
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. Since its interpretation depends on the code
generator, it is often ignored in simple implementations. See
"Functions" for a special use of "register" when defining a
function.
eg: register unsigned count;
Symbols declared with a preceeding '*' 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];
2.2.1 More Symbol Examples
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 */
MICRO-C Page: 6
2.2.2 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.
Note that the compiler IS case sensitive, however if the
assembler you are using is NOT, you must be careful not to
declare any global symbols with names that differ only in case.
All non-static global variables are generated at the very
end of the output file, after the literal pool is dumped. Since
non-static globals do not generate code, this allows them to be
excluded from the image file when it is saved to disk.
Global variables may be initialized with one or more values,
which are expressed as a single array of integers REGUARDLESS
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 declared, a null dimension may be used as
the dimension size, in which case the size of the array will
default to the number of initialized values.
eg: int array[] = { 1, 2, 3 };
Initialized global variables are automatically defined as
STATIC, which insures that the inital values will be saved in
the code image. Any non-initialized elements of the static
variable will be set to zero.
Non-static global variables are not initialized in any way,
and will be undefined at the beginning of program execution.
2.2.3 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 de-allocation 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 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.
MICRO-C Page: 7
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 interchangable
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. They are always "static" and are always
exist in the code image.
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 */
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 'C' compilers
push operands.
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.
MICRO-C Page: 8
2.5 Control Structures
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;
MICRO-C Page: 9
2.5.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 preceed 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 accessable within the scope
of that function.
MICRO-C Page: 10
2.6 Expression Operators
The following expression operators are implemented in MICRO-C:
2.6.1 Unary Operators
- - Negate
~ - Complement
! - Logical complement
++ - Pre or Post increment
-- - Pre or post decrement
* - Indirection
& - Address of
2.6.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
^= - XOR with self assignment
<<= - Shift left self assignment
>>= - Shift right self assignment
MICRO-C Page: 11
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.6.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.
: - Delimits labels, ends CASE and separates conditionals.
{ } - 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
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 elements.
(remember index's start from zero)
a[3][0] returns the character at index [3][0];
MICRO-C Page: 12
2.7 Preprocessor Commands
The MICRO-C compiler supports the following pre-processor
commands. These commands are recognized only if they occur at the
beginning of the input line.
2.7.1 #asm
Causes the compiler to copy all subsequent lines directly to
the output file without translation. This allows assembly
language code to be included in the 'C' program.
2.7.2 #endasm
Terminates a "#asm", and causes the compiler to resume
compiling from the input file.
2.7.3 #define <name> <replacement_text>
The "#define" command allows a global name to be defined,
which will be replaced with the inicated text whenever it is
encountered in the input file. This occurs prior to processing
by the compiler.
2.7.4 #include <filename>
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.7.5 #ifdef <name>
Processes the following lines (up to #else of #endif) only
if the given name is defined.
2.7.6 #ifndef <name>
Processes the following lines (up to #else of #endif) only
if the given name is NOT defined.
2.7.7 #else
Processes the following lines (up to #endif) only if the
preceeding #ifdef or #ifndef was false.
2.7.8 #endif
Terminates #ifdef and #ifndef
NOTE: The #ifdef and #ifndef contructs may not be nested in
MICRO-C.
MICRO-C Page: 13
2.8 Error Messages
When MICRO-C detects an error, it outputs an informational
message indicating the type of problem encountered.
The error message is preceeded by the line numbers of the line
containing the error in all files being processed.
eg: 5:3: Syntax error
In the above example, the main input file "#included" a file at
line 5, and a syntax error was discovered in that file at line 3.
The following error messages are produced by the compiler:
2.8.1 Compilation aborted
The preceeding error was so severe than the compiler cannot
proceed.
2.8.2 Constant expression required
The compiler requires a constant expression which can be
evaluated at compile time (ie: no variables).
2.8.3 Declarations must preceed code.
All local variables must be defined at the beginning of the
function, before any code producing statements are processed.
2.8.4 Dimension table exhausted
The compiler has encountered more active array dimensions
than it can handle.
2.8.5 Duplicate local: 'name'
You have declared the named local symbol more than once
within the same function definition.
2.8.6 Duplicate global: 'name'
You have declared the named global symbol more than once.
2.8.7 Expected '<token>'
The compiler was expecting the given token, but found
something else.
2.8.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: 14
2.8.9 Expression stack underflow
The compiler has made an error in parsing the expression.
Check that it is of correct syntax.
2.8.10 Illegal global statement
You have used a statement outside of a function definition
which may only be used within a function definition.
2.8.11 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 atempt to index an array with more
indices than it has dimensions.
2.8.12 Illegal nested function
You may not declare a function within the definition of
another function.
2.8.13 Illegal static local
Local variables may not be static. This error also results
if you attempt to initialize a local variable in the
declaration statement.
2.8.14 Improper #else/#endif
A #else or #endif statement is out of place.
2.8.15 Inconsistant re-declaration: 'name'
You have attempted to redefine the named external symbol
with a type which does not match its previously declared type.
2.8.16 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
indicies. 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.8.17 Macro expansion too deep
The compiler has encountered a nested macro reference which
is too deep to be resolved.
2.8.18 Macro space exhausted
The compiler has encountered more macro ("#define") text
than it has room to store.
MICRO-C Page: 15
2.8.19 Missing #endif
The end of the file was encountered when an #ifdef or
#ifndef was in effect.
2.8.20 No active loop
A "continue" or "break" statement was encountered when no
loop is active.
2.8.21 No active switch
A "case" or "default" statement was encountered when no
"switch" statement is active.
2.8.22 Not an argument: 'name'
You have declared the named variable as an argument, but it
does not appear appear in the argument list.
2.8.23 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.8.24 Numeric constant required
The compiler requires a constant expression which returns a
simple numeric value.
2.8.25 String space exhausted
The compiler has encountered more literal strings than it
can handle.
2.8.26 Symbol table full
The compiler has encountered more symbol definitions than it
can handle.
2.8.27 Syntax error
The statement shown does not follow syntax rules and cannot
be parsed.
2.8.28 Too many active cases
The compiler has run out of space for storing switch/case
tables. Reduce the number of active "cases".
2.8.29 Too many #defines
The compiler has encountered more '#define' statements than
it can handle. Reduce the number of #defines.
MICRO-C Page: 16
2.8.30 Too many errors
The compiler is aborting because of excessive errors.
2.8.31 Too many includes
The compiler has encountered more nested "#include" files
than it can handle.
2.8.32 Too many initial elements
You have specified more initialization values than there are
locations in the global variable.
2.8.33 Type clash
You have attempted to combine values which are of
incompatable types.
2.8.34 Unable to open: 'name'
A "#include" command specified the named file, which could
not be opened.
2.8.35 Undefined symbol: 'name'
You have referenced a name which is not defined as a local
or global symbol.
2.8.36 Unresolved reference: '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.8.37 Unterminated function
The end of the file was encountered when a function
definition was still open.
2.8.38 Unterminated #if
The end of file was encountered when a "#if" or "#else"
conditional block was being processed.
MICRO-C Page: 17
2.9 Quirks
In its effort to provide the maximum amount of functionality
with the minimum amount of code, MICRO-C deviates from standard
'C' in some areas. The following is a short summary of the major
infractions and quirks:
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 "/*"
Include filenames are not delimited by '""' or '<>' and are
passed to the operating system exactly as entered.
eg: #include /mclib/stdio.h
The '\0' constant occuring in a string or constant will confuse
the parser into thinking that the string has terminated. (This is
a common "BUG" in many compilers, so it is a good idea to avoid it
anyways)
Instead of: chr = '\0';
use: chr = 0;
Whenever MICRO-C encounters a quoted string (double quotes), it
places it in the literal pool, and generates the ADDRESS of the
string as a value. There is no special handling of strings for
array initialization as in standard 'C', for example, the
declaration:
char array[] = "string";
Will compile the string "string" into the literal pool, and
initialize a one element array with the low byte of its address.
In MICRO-C, to initialize an array with the CONTENTS of a string,
you must use a statement such as:
char array[] = { 's','t','r','i','n','g' };
Note that the following declaration is perfectly valid in MICRO-C,
because it is defining a pointer which will contain the address of
the string (in the literal pool):
char *ptr = "string";
MICRO-C Page: 18
The appearence of a variable name in the argument list for a
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 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 indicies). Since an address is already returned in such cases,
simply drop the '&'.
The 'x' in '0x' and '\x' is accepted in lower case only.
When using MICRO-C to create a function will which contain
assembly language code, remember that MICRO-C allocates all local
variables at the beginning of a function definition, and will not
generate the function entry code until a non-declarative statement
is encountered. In particular, since "#asm" is handled by the
pre-processor and not seen as a statement by the parser, you
should use a "null" statement (';') to ensure that the entry code
is generated prior to using "#asm" at the beginning of a function.
eg: func()
{ ; /* ensures "entry" code is generated */
#asm
assembly statements
#endasm
}
MICRO-C Page: 19
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 certin 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:
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).
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.
MICRO-C Page: 20
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 "#asm" statements
in the 'C' source code, or separately linked modules.
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
LOWEST on 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.
Since the compiler uses the ACCUMULATOR and INDEX REGISTER, it
will automatically save them (if necessary) during processing of
the arguments to the function (even if no arguments are present),
and therefore these registers do not have to be preserved by the
called routine.
NOTE that any other registers used by the code generation
routines (register variables etc) will not be saved by MICRO-C,
and should be preserved by called functions if their content is to
be relied on between function calls.
Once the called routine returns, the arguments are removed from
the stack by the calling program. This usually consists of simply
adding the number of bytes pushed as arguments to the stack
pointer.
When the called function executes, the first thing usually done
is to push any registers which must be preserved, and to reserve
space on the stack for any local variables which are required. In
some implementations, a "base" register may also be established to
provide a stable reference to the local variables and arguments.
It is the responsibility of the called function to remove any
saved registers and local variable space from the stack before it
returns.
MICRO-C Page: 21
Local variables in a function may be referenced as direct
offsets from the "base" register or stack pointer. Note that if
the stack pointer is used, offsets must be adjusted for the number
of 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:
"base register"
-
(Size of all local variables in bytes)
+
(size of all preceeding local variables in bytes)
-- OR --
"stack pointer"
+
(# bytes pushed during function execution)
+
(size of all preceeding local variables in bytes)
Arguments to a function may also be referenced as direct
offsets from the "base" register or stack pointer, in much the
same way as local variables are.
The address of a particular argument is calculated as:
"base register"
+
(# bytes pushed at entry of function to preserve registers etc.)
+
(Size of return address on stack (usually 2))
+
(# arguments from LAST argument) * 2
-- OR --
"stack pointer"
+
(# bytes pushed during function execution)
+
(size of all local variables in bytes)
+
(# bytes pushed at entry of function to preserve registers etc.)
+
(Size of return address on stack (usually 2))
+
(# arguments from LAST argument) * 2
NOTE: The (number of bytes pushed at entry of function) is a
function of the code generator, and depends on the particular
MICRO-C implementation. Examine some assembly output from the
compiler to determine the actual number on your system.
MICRO-C Page: 22
If a function has been declared as "register", MICRO-C will
load the 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.
The address of the first argument passed to a "register"
function may be calculated as:
(accumulator contents) * 2
+
"base register"
+
(# bytes pushed at entry of function to preserve registers etc.)
+
(Size of return address on stack (usually 2))
-- OR --
(accumulator contents) * 2
+
"stack pointer"
+
(# bytes pushed during function execution)
+
(size of all local variables in bytes)
+
(# bytes pushed at entry of function to preserve registers etc.)
+
(Size of return address on stack (usually 2))
Global variables exist at absolute addresses and may be
referenced directly by name from within assembler programs. Keep
in mind however, that MICRO-C uses only the first 15 characters of
a symbol's name. Also, many code generators will reduce the size
of names even further, often using an algorithm to compress the
name rather than simply truncating it. For this reason, it is a
good idea to avoid using global symbol names which are longer than
6 or 8 characters if they are to be referenced from within
assembly language programs.
MICRO-C Page: 23
3.3 Compiling for ROM
Assuming the code generator does not use such "nasty" things as
self modifying code, the output from the compiler is entirely
"clean", and may be placed in Read Only Memory (ROM).
The compiler places all "static" variables in the output file
as part of the code image. When the program is stored in ROM,
those variables are also stored in ROM, and will not be
modifiable. Note that any global variables which are initialized
in their declaration are considered "static".
When the program is to be placed in ROM, you may not initialize
any variables which you intend to modify later. Those variables
must be explicitly initialized by code executed at the beginning
of the program.
Initialized variables which you do not intend to modify (such
as tables etc.) may be initialized in the declaration, and will be
permanently saved in the ROM as part of the "static" code image.
The processor stack pointer must be set up before any
expressions are evaluated, or any function calls are performed.
This may be performed via '#asm' statements.
All non-initialized global variables must be located in RAM.
The compiler usually outputs all such variables at the very end of
the compilation, just after dumping the literal pool. The global
variables may be moved to RAM by editing the output file, and
placing an appropriate "ORG" statement at the beginning of the
globals.
If extensive compilation for ROM is being performed, you may
want to use a version of the compiler which uses an intermediate
output file (see "PORTING THE COMPILER"). This allows a separate
code generator to be customized for ROM applications, which will
output the appropriate stack setup instructions (this can be done
in "def_module()"), and locate the global variables in RAM (this
can be done at the end of "def_literal()").
MICRO-C Page: 24
4. PORTING THE COMPILER
There are two major goals to accomplish when porting the MICRO-C
compiler to a new machine. The first is to make the compiler run in
the new environment, and the second is to make it produce code for
that environment.
These two goals do not always go hand in hand. For example, it may
be desirable to implement a CROSS COMPILER which generates code for a
different system from that on which it runs.
The usual method of porting a compiler to system A when a version
of the compiler is already running on system B, is to first create a
compiler which runs on system B, producing code for system A. This
"new" compiler may then be used to create a compiler which runs on
system A.
The compiler consists of a module "compile" which contains, the
main compiler, input scanner, regular expression parser, and symbol
table management routines. This is the "static" portion of the
compiler which does not change in different implementations.
To create a working compiler, the above module must be compiled
and linked with an "io" module and "code" module. The "io" module
performs the necessary initialization and I/O to allow the compiler
to run in a particular environment (goal #1). The "code" module
generates the assembly language output code for a particular
environment (goal #2).
The compiler uses NO system library functions, and relies on NO
system services (other than those used by the "io" and "code"
modules). This allows the compiler to be ported to virtually any
system.
All fixed compiler parameters (such as table sizes etc) are
contained in the header file "compile.h".
Two additional modules are provided with the compiler. The file
"intercg.c", may be linked into the compiler in place of the code
generator, and writes a "generic" intermediate file which contains
the pseudo operation and type tokens (See Code Generator). The file
"genasm.c", may be linked with the regular "io" and "code" routines
to produce a utility program which reads the intermediate file and
produces the assembly language code for a specific processor.
MICRO-C Page: 25
Although this "split" compiling approach is slower than the usual
mode of direct assembly language production, it provides several
useful capabilities:
1) The amount of ram required to run the "separate" compiler and
genasm programs is considerably less than required to run the
"stand alone" compiler.
2) A single parser may be used to generate code for several target
systems, by simply running a different version of the "genasm"
utility, which is customized for each target.
3) Use of the intermediate file allows distribution of "portable"
programs which may be processed by "genasm" for different targets
WITHOUT distribution of the source code.
4) A program may be written which processes this intermediate file,
and "optimizes" the code. Additional "pseudo operation" codes may
be added to the output file if the target processor is capable of
operations which the more "generic" MICRO-C output does not
utilize.
4.1 The "io" module
The "io" module required by the compiler must contain the
following function definitions:
The function "main()" is called by the operating system when
the MICRO-C compiler is executed. It is responsable for parsing
any parmeters and command qualifiers, opening the input and output
files, performing any other initializations that might be
required, and then invoking the function "compile()" with no
arguments. The "compile" function is internal to the "compile.c"
module, and will never return. For UNIX and other systems which
support I/O redirection, "stdin" and "stdout" are often used for
the input and output files.
The function "exit(rc)" is called when the compiler has
finished all processing and wishes to terminate. The value "rc" is
a return code: 0 = Success, program compiled without error, -1 =
Compile was aborted due to severe errors, n = Compile finished,
but had 'n' errors.
The function "put_chr(chr, flag)" is passed a character to
output, as well as a flag. The flag will always be zero when the
character is being sent to the console terminal, or non-zero when
the character is to be written to the output file.
The function "put_str(*ptr, flag)" is passed a pointer to a
zero terminated string, which is to be written to the console or
output file as determined by "flag".
The function "put_num(number, flag)" is passed a 16 bit number,
which is to be written in printable form to the console or output
file as determined by "flag".
MICRO-C Page: 26
4.1.1 Compiler only routines
The following I/O routines are required by the compiler, but
not by the "genasm" program.
The function "get_lin(*ptr)" is passed a pointer to a
character array, and should read a single line from the
currently open input file into that array. The manifest
definition "LINE_SIZE", found in "compile.h" may be used to
determine the maximum line length acceptable to the compiler.
Return of a non-zero value indicates the end of file condition.
The function "f_open(*ptr)" is passed a pointer to a
filename. The new file should be opened, and if successful, the
old input file should remain open and be "stacked", so that it
can be later returned to. If the file was opened successfully,
a non-zero value should be returned. A zero value indicates to
the compiler that the file could not be opened.
The function "f_close()" is responsible for closing the
currently open input file, and returning to the "stacked"
previously open one. It receives no parameters. Note: multiple
"opens" may be stacked - see the manifest definition of
"INCL_DEPTH" in the "compile.h" file.
4.1.2 Genasm only routines
The following I/O routine is required by the "genasm"
program, but not by the compiler.
The function "get_char()" must return a single character
from the input file as a 16 bit number with a positive value
between 0 and 255. A '-1' is returned for the end of file
condition.
4.1.3 Notes on I/O module
1) It is the responsibility of the "put" routines to translate
the NEWLINE '\n' (0x0a) character into whatever line
termination character(s) are required by the target
operating system.
2) If unix "stdin" and "stdout" are not used, it is the
responsibility of "main" to display appropriate error
messages if the input or output file could not be opened.
Refer to the sample "io" modules distributed with the
compiler.
MICRO-C Page: 27
4.2 The "code" module
In order to insure that MICRO-C is portable to virtually any
environment, the compiler makes few assumptions about the
processor or system software of the target system. The "code"
module is relied on to produce all machine instruction and
assembler directives written to the output file.
The only two real "assumptions" made about the target processor
are:
1) It is assumed that the target processor has an "accumulator"
register in which all math operations are performed, and that
the lower 8 bits of this register may be accessed
independantly.
2) It is assumed that the target processor has one "index"
register which may be loaded with a 16 bit value, and that
memory references may be made indirectly through this register.
If the target processor does not support the above features, it
may be possible to write a code generator for it using some other
features of the processor.
For example, if an "index" register does not exist, it may
often be implemented using two bytes of reserved memory.
The code generation module required by the compiler must
contain the following function definitions:
The function "do_asm(*ptr)" is passed a pointer to a character
string, which it should write to the output file EXACTLY as
passed, followed by a '\n' NEWLINE character. This function is
used by the "#asm" directive to write directly to the output file.
The function "def_module()" is called at the beginning of
compilation, before any other code generator functions are called.
It is used to output any pre-amble needed by the assembler.
The function "end_module()" is called at the very end of
compilation, and is the last code generator function called. It is
used to output any post-amble needed by the assembler.
The function "def_static(symbol)" is passed am index into
compiler symbol tables for a global static variable, which is
about to be defined. The call to this function will be immediately
followed by a call to "init_static" or "end_static".
The "init_static(token, value, word)" function is passed a
token and value, with which it should initialize a single element
of static storage. The "word" flag will be non-zero if the value
is a 16 bit element. Only the "constant" tokens (NUMBER, STRING,
LABEL) need be handled by this routine.
MICRO-C Page: 28
The "end_static()" function is called to terminate the
definition of static storage.
NOTE: "init_static" should not rely on "def_static" being
called first, since it is also called immediately following
"def_label" to define "switch" tables (See "do_switch"). After the
table is defined, the compiler will call "end_static()" to
terminate the initialization and set up for the next.
The function "def_global(symbol, size)" is called at the end of
the compile, once for each non-static global variable which was
defined. The "size" paremeter indicates the number of BYTES of
memory to be reserved for that variable.
The function "def_extern(symbol)" is called at the end of the
compile, once for each non-resolved external symbol which was
defined. This routine should examine the type of the symbol and
output the appriopriate assembler directives to allow it to be
referenced in another module.
The "def_func(symbol, size)" routine is called to start a
function definition. The "symbol" parameter is an index into the
compiler symbol tables for the function entry being defined. The
"size" parameter indicates how many bytes of memory should be
allocated on the stack for local variables.
The "end_func()" routine is called to terminate a function
definition. It should remove anything placed on the stack
(including the local variable space allocated by "def_func"), and
terminate the function with a "return" instruction.
The "def_label(label)" function is called whenever the compiler
wants to generate a label in the output file. Each label generated
by the compiler is identified by a 16 bit unsigned number. It is
up to the code generator to generate a unique label suitable for
the target assembler.
The "def_literal(*ptr, size)" function is called at the end of
the compile, just before non-static global symbols are generated.
This routine is given a pointer to the compiler's "literal pool",
which contains all the character strings occuring during the
compilation. The "size" parameter indicates how many characters
are in the pool. This pool must be generated in the output file as
a string of byte constants.
The "call(token, value, type, clean)" function is used to
generate a machine language subroutine call to the entity
indicated by the "token, value & type" parameters (See later). The
"clean" parameter indicates how many entries were pushed onto the
stack as arguments, which should be removed following the function
call. Note: Since stack entries are TWO bytes in most
implementations, the "clean" value must be multiplied by two to
get the actual number of bytes to be removed from the stack.
MICRO-C Page: 29
The function "jump(label, ljmp)" is called to generate an
unconditional jump instruction to the indicated compiler generated
label. The "ljmp" flag will be set to zero if the jump references
code within the same expression from which it is generated, and
non-zero if one or more statements may occur between the jump and
the destination label. This allows the code generator to take
advantage of "short" jumps if they are available on the target.
The function "jump_if(cond, label, ljmp) is called to generate
a conditional jump to a compiler generated label. The "cond" value
indicates the condition: FALSE = Jump if accumulator is zero, TRUE
= jump if accumulator is non-zero. Remaining parameters are the
same as above.
The function "do_switch(label)" is passed the address of a
"switch" table, which contains 16 bit entries, and is of the
following format:
label-1, value-1, label-2, value-2, ....
label-n, value-n, 0, default_label
This routine should search the table for the value currently in
the accumulator, and if found, it should proceed to the label
associated with that value. If the value is not found before the
end of the table is encountered (identified by a label value of
zero), execution should proceed at the address identified by
"default_label".
The "index_ptr(token, value, type)" routine should load the
index register with the value of the entity indicated by the
"token, value & type" parameters. This will always be a 16 bit
wide quantity.
The "index_adr(token, value, type)" routine should load the
index register with the 16 bit address of the symbol represented
by "token, value & type".
The routine "expand(type)" is called following the evaluation
of expressions in "return" and "switch" statements, and is used to
insure that the result is a 16 bit value.
The routine "accop(oper, type)" is called to perform a "no
operand" operation on the accumulator. See "compile.h" for a list
of these operations. The "type" passed indicates the type of value
expected as a result.
The routine "accval(oper, rtype, token, value, type)" is called
to perform a "one operand" operation on the accumulator. See
"compile.h" for a list of these operations. "rtype" indicates the
type of value expected as a result. "token", "value" and "type"
indicate the location and type of operand which is being
processed.
MICRO-C Page: 30
4.2.1 Notes on code generation
The meaning of the individual bits in the 16 bit "type"
value which is passed to many of the code generation routines,
is documented in the "compile.h" file.
The meaning of "token" is documented in the "compile.h"
file. For each type of "token", "value" has a different
meaning:
Token Meaning of "value"
----------------------------------------------------------
NUMBER The numeric value of the constant.
STRING The offset into the literal pool.
LABEL The value of the compiler generated label.
SYMBOL Index into global symbol tables.
All others Undefined.
When token is a SYMBOL, the "value" passed is used to index
into the global symbol tables (contained within the "compile"
module) to determine information about the variable:
s_name[value] - Name of symbol (up to SYMBOL_SIZE characters).
s_type[value] - Type of symbol (bits defined in "compile.h").
s_index[value] - Variable index:
Global: Index indicates symbol was the n'th one defined.
Local: Index is stack offset from def_func.
Argument: Index is stack offset from last argument pushed.
When calculating the stack offset for ARGUMENTs, you must
add the number of bytes placed on the stack when the function
was called. This includes the local variables, the return
address, and any other values that "def_func" might push.
There are two popular ways of providing addressability to
local variables:
If the processor has many registers, you can reserve one as
a "base" pointer, and point it at the top of the stack when the
function is entered. This allows local variables to be
referenced as negative offsets from that "base" register, and
arguments to be referenced as positive offsets from it. This
approach also allows the stack to be restored directly from
this base register when the function terminates. See the
section on assembly language interfacing.
Another approach is to have the code generator "remember"
exactly how many bytes have been pushed onto the stack since
"def_func", and adjust the offsets it generates based on the
stack contents. This has the advantage of not tying up a
register.
It is the responsibility of the code generator to keep track
of the validity of the upper 8 bits of the accumulator.
Appropriate sign extension or clearing of high bits must be
performed as necessary to convert signed and unsigned character
values when 16 bit results when required.
MICRO-C Page: 31
To improve the effiency of conditional statements, the code
generator should keep track of the validity of the "zero" flag
in the processor's condition code register, and generate
appropriate "test" instructions only if necessary when a
conditional jump is compiled.
For processors not supporting operations with stack
contents, the "ON_STACK" and "ION_STACK" tokens may implemented
by first popping the top of the stack into a register. The
"ISTACK_TOP" token is a special case, because the address on
the top of the stack must not be lost. This token may be
efficiently implemented, because "ISTACK_TOP" is only used for
read/write operations to a stacked calculated address. For
example:
array1[i] += array2[i];
This statement calculates the address of "array1[i]" (in the
index register). Since the "index" register is again used in
calculating the address of "array2[i]", the first "index" will
be placed on the stack. Once the contents of "array2[i]" are
retrieved, it will be added using "ISTACK_TOP", and then
re-stored using "ION_STACK".
The "ISTACK_TOP" token may thus pop the address into a
register, and set a flag indicating to the code generator that
the next "ION_STACK" token is to go through that register
rather than the top of the stack. Note that since an arithmetic
operation may be performed between the two references, you must
not use a register which is modified in code generated for
arithmetic operations.
Refer to the sample code generators distributed with the
compiler.
4.3 The "compile" module
The "compile" module contains the main statement analyser,
input scanner, expression parser and symbol table managment
routines for the MICRO-C compiler. This module is common to all
implementations, and should NOT require any changes. The source
code for this module is contained in the "compile.c" file on your
distribution diskette, and may be examined for insight into the
internal operation of the compiler.
This module must be compiled and linked with your I/O and code
generation routines to generate a complete executable MICRO-C
compiler.
To test your code generator, the file "test.c" is provided
which when compiled using the new compiler, performs a number of
simple tests to verify your code generator. This is not a
comprehensive analysis, as it makes no assumptions about the
processor, however, it provides a good indication that your code
generator is on the right track.
MICRO-C Page: 32
4.4 Porting without an existing compiler
If you have the MICRO-C distribution files, but no running
compiler, it is still possible to port the compiler, using the
following steps:
1) You must write the code generator and I/O routines in another
language. Using assembly language is preferable, because you
can then follow the MICRO-C function calling conventions (See
"Assembly Language Interface under "Advanced Topics"). This
allows you to use the same routines with the compiler later.
2) The "genasm.c" program should then be re-written in the same
language as above. This should not be difficult, since it is a
fairly simple program.
3) Link "genasm" with your code generator and I/O routines, to
create the "genasm" utility which reads intermediate files and
produces assembly language output.
4) The "intermediate" file for the main "compile" module is
provided in the file "compile.i". When this file is processed
by "genasm", and the resultant file assembled, you will have
the "compile" module which may be linked with the "io" and
"code" modules to produce the final compiler.
4.5 Porting without a linker
It is possible to port MICRO-C using a system which does not
support a linker. To do this, you must concatinate all the source
files "compile.c", "code.c" and "io.c" into one large file (in
that order), and compile them all as one program.
When this is done, the ".h" include files need only be included
once, and external definitions of variables occuring in other
source files should not be used. The source programs on the
distribution disk all contain conditional compilation statements
(#ifndef), which only perform the necessary #include and external
definition statements when compiling as a single file.
MICRO-C Page: 33
4.6 Code Optimization
The MICRO-C compiler performs the following machine independant
optimizations of the output file:
1) All constant expressions are evaluated at compile time, and
expressed as a single constant value in the output code.
2) Operations which are not sensitive to order (eg: '+') may be
reversed to take advantage of partial results already in the
accumulator.
3) Redundant jumps as a result of "return" or "break" statements
are suppressed.
4) The sense of jumps in conditional statements are reversed for
logically negated expressions, code for '!' is only generated
if the value returned by that operator is actually used.
5) Jumps between code generated within a single expression are
flagged as "short".
Although the MICRO-C compiler produces fairly reasonable code
for the processor model it uses, that model is necessarily
limited, in order that it might fit a large number of physical
targets. There are several simple optimizations which may be
peformed to further enhance the code generated by the compiler in
a specific implementation.
4.6.1 Register Usage
MICRO-C assumes a single accumulator, and a single index
register. Additional terms in commplicated expressions are
handled by placeing temporary results on the processor stack,
and re-using those registers. All data placed on the stack is
accessed on a "last in - first out" basis.
If the target processor has a full compliment of general
purpose registers, an optimization may be performed by simply
selecting another general purpose register as the accumulator
or index register instead of placing the value on the stack.
The code generator must keep track of the order in which
registers are selected, and which register represents the "top"
of the stack. If the number of values "pushed" exceeds the
number of available registers, the "oldest" register should be
placed on the stack, thereby allowing it to be re-used.
MICRO-C Page: 34
4.6.2 Jump Optimization
Although MICRO-C identifies jumps to instructions which span
more than one expression as "long", often the addresses are
close enough together that short jumps may actually be used.
This optimization is particularily useful for processors such
as the 8086, which does not support "long" conditional jumps,
and therefore must simulate them with a short conditional jump
of the opposite sense around a long unconditional jump.
4.6.3 Redundant Load Elimination
Since MICRO-C evaluates and processes each statement
individually, it does not carry partial results from one
statement to another.
Consider the following statements:
a = x;
b = a + 1;
MICRO-C generates the code:
LOAD x
STORE a
LOAD a
ADD 1
STORE b
An optimization may be performed by recognizing that the
second "load" instruction is redundant, and can be eliminated.
Note: A more efficent (but less readable) way of coding the
above statements which would result in the latter code without
optimization is:
b = (a = x) + 1;
MICRO-C Page: 35
4.6.4 Peephole Optimization
Consider the statement:
a = *++ptr;
MICRO-C generates the code:
LOAD ptr
INCREMENT
STORE ptr
MOVE ACCUMULATOR TO INDEX
LOAD [INDEX]
STORE a
For a processor supporting a rich set of direct memory
addressing modes, the above sequence can be shortened to:
INCREMENT_MEMORY ptr
LOAD [ptr]
STORE a
One of the most successful techniques of optimizing the
output code is also one of the simplest. Known as "peephole"
optimization, the method consists of keeping a window of the
last few instructions generated, and scanning the list for
known patterns every time a new instruction is added to it.
As long as the instructions in the list at least partially
match one or more of the "predefined" patterns, additional
instructions are collected until either a complete pattern
match occurs, or all known patterns are eliminated.
If no matches occur, the "oldest" instruction is written to
the output file, and the next one becomes the first in the
"window".
Whenever a pattern is discovered, it is replaced by a new
series of instructions which perform the same function, but in
a more efficent manner.
The new instruction sequences are replaced on the list,
which may then be again scanned, allowing further possible
reductions to be discovered.
Handling of labels in the "window" and their corresponding
placement in the output file must be carefully done, in order
to preserve the "logical" context of the original code.
MICRO-C Page: 36
5. 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 substution. 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 enhanced 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 enhanced preprocessor are:
- Fully parameterized MACROs.
- Fully nested conditionals
- Ability to undefine MACRO symbols.
- Library reference in include file names.
MICRO-C Page: 37
5.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 write the processed
source code to. If less than two filenames are specified, MCP will
write to standard output.
5.1.1 Command Line Options
MCP accepts the following command line [options]:
-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.
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/include'
-q - Instructs MCP to be quiet, and not display the
startup message when it is executed.
MICRO-C Page: 38
5.2 Preprocesor Commands
The following commands are recognized by the MCP utility, only
if they occur at the beginning of the source file line:
5.2.1 #define <name>(parameters) <replacement text>
Defines a global macro name which will be replaced with the
indicated <replacement text> 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
definition.
5.2.2 #undef <symbol>
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.
5.2.3 #forget <symbol>
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 */
MICRO-C Page: 39
5.2.4 #ifdef <symbol>
Causes the following lines (up to '#else' of '#endif') to be
processed and included in the source file only if the named
symbol is defined as a macro.
5.2.5 #ifndef <symbol>
Causes the following lines (up to '#else' of '#endif') to be
processed and included in the source file only if the named
symbol is NOT defined as a macro.
NOTE: '#ifdef/#ifndef#else/#endif' may be nested.
5.2.6 #else
Toggles the state of the "if_flag", controlling conditional
processing. Only has effect in the highest level of suspended
processing. IE: Nested conditionals will work properly.
If the previous '#ifdef/#ifndef' failed, processing will
begin again following the '#else'.
If the previous '#ifdef/#ifndef' passed, processing will be
suspended until the '#endif' is encountered.
NOTE: Since '#else' acts as a toggle, it may be used outside
of any '#ifdef/#ifndef' to unconditionally suspend processing
up to the '#endif'.
5.2.7 #endif
Resets the "if_flag" controlling conditionals, causing
processing to resume. Only has effect in the highest level of
suspended processing. IE: Nested conditionals will work
properly.
MICRO-C Page: 40
5.2.8 #include <filename>
Causes MCP to open tha 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. The default library directory is assumed to be
'/mc/include'.
For example:
#include "header.h" /* from current directory */
#include <stdio.h> /* from library directory */
5.2.9 #asm / #endasm
The '#asm' and '#endasm' statement are not actually
recognized by MCP. They are passed through unchanged, allowing
them to be recognized and acted upon by the integral MICRO-C
preprocessor.
MICRO-C Page: 41
5.3 Error messages
When MCP detects an error during processing of an include file,
it displays an error message, which is preceeded by the line
numbers of the files in which the error occurs. If more than 10
errors are encountered, MCP will terminate.
The following error messages are reported by MCP:
5.3.1 Cannot open include file
A '#include' statement on the indicated line specified a
file which could not be opened for read.
5.3.2 Invalid include file name
A '#include' statement on the indicated line specified a
file name which was not contained within '"' or '<>'
characters.
5.3.3 Invalid macro name
A '#define' statement on the indicated line contains a macro
name which does not follow the name rules.
5.3.4 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.
5.3.5 Too many errors
More than 10 errors has been encountered and MCP is
terminating.
5.3.6 Too many maco definitions
MCP has encountered more '#define' statements than it can
handle.
5.3.7 Too many macro parameters
A '#define' statement on the indicated line specifies more
parameters parameters to the macro than MCP can handle.
5.3.8 Too many include files
MCP has encountered more nested '#include' statements than
it can handle.
MICRO-C Page: 42
5.3.9 Undefined macro
A '#undef' or '#forget' statement on the indicated line
references a macro name which has not been defined.
5.3.10 Unterminated comment
The END OF FILE has been encountered while processing a
comment.
5.3.11 Unterminated string
A quoted string string on the indicated line has no end.
MICRO-C Page: 43
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
inefficent 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 MCO command
The format of the MICRO-C Optimizer command line is:
MCO [input_file] [output_file] [options]
[input_file] is the name of the source file containing assembly
statements to read. If no filenames are given, MCO will read from
standard input.
[output_file] is the name of the file to write the optimized
assembly code to. If less than two filenames are specified, MCO
will write to standard output.
6.1.1 Command Line Options
MCO accepts the following command line [options]:
-d - Instructs MCO 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 explict output
file, you will get the debug statements
intermixed with the optimized code on
standard output.
-q - Instructs MCO to be quiet, and not display the
startup message when it is executed.
MICRO-C Page: 44
6.2 Porting to a new processor
The MICRO-C Optimizer is completely table driven, and should be
fairly easy to port to a new processor.
The peephole optimization table is called 'peep_table', and
consists of two sequential character string entries for each
optimization.
The first is the "take" entry, and represents an instruction
sequence which (if found) is to be removed from the output file.
The second is the "give" entry, and defines a sequence of
instructions to be placed in the output file at that point. NOTE
that due to the way the optimizer removes and replaces instruction
in the circular "peephole" buffer, the instructions in the "give"
entry ARE CODED IN REVERSE ORDER.
Characters in the "take" entry with the high bit set (ge:
'\200','\201') are special characters which represent any variable
string which may occur in the instruction sequence, and will be
replaced with the same string wherever that character occurs in
the "give" entry. If the same special character (eg: '\200')
occurs more than once in the "take" entry, the corresponding
strings must be exactly the same or else the entire sequence will
not be matched.
The processor will stop scanning a variable string when it
encounters the character which immediately follows the special
character in the "take" entry, or at the end of the input line.
The manifest SYMBOLS defines how many different special
characters are allowed in a peephole entry. You must not use any
special characters which have a value greater than ('\200' +
SYMBOLS - 1).
MICRO-C Page: 45
7. LIBRARY FUNCTIONS
The MICRO-C distribution disk includes a file called "library.c"
which contains the 'C' source code for a number of useful "library"
functions. These routines may be also be used as "example" programs,
providing insight into MICRO-C programming techniques.
All functions except for the lowest level I/O routines are coded
in 'C', and should compile on any MICRO-C system. Note that some low
level functions in the library (such as "fputs") are written in 'C'
using still lower level routines (in this case "putc"). Although this
makes the library highly portable and reduces the number of routines
you have to re-write for a specific system, the resultant function
will be less efficent than one which directly uses the operating
system services.
If you are implementing MICRO-C an a small system and intend to
use it for serious programming, I strongly recommend that you re-code
all of the low level library functions in assembly language.
Note that since library routines are often used, in applications
where code size and execution speed are of primary importance, it may
be desirable to recode some or all of the other library routines in
assembly language as well.
For those who intend to make only casual use of MICRO-C, or who
wish to experiment with it simply for the learning experience, the
'C' versions of the low level library functions should be more than
sufficient.
The following routines are available in "library.c" at this time:
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 with a return code of '-1'
(Indicating general failure). 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");
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 concatinates 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".
EXAMPLES:
concat(filename,"/tmp/", input_name, 0);
COPY COPY
PROTOTYPE:
copy(char *dest, char *source, unsigned size)
ARGUMENTS:
dest - Pointer to the destination
source - Pointer to the souce
size - Number of bytes to copy
RETURN VALUE:
None
DESCRIPTION:
The "copy" function will copy the specified number of bytes from
the source to the destination.
EXAMPLES:
copy(buffer1, buffer2, 256);
EXIT EXIT
PROTOTYPE:
exit(int rc);
ARGUMENTS:
rc - Termination return code.
RETURN VALUE:
N/A - Function never returns.
DESCRIPTION:
This function terminates the execution of the program and passes a
specific return code back to the operating system. A return code
value of zero is used to indicate successful program completion.
Non-zero return code values may be used to indicate a particular type
of failure. A value of '-1' is often used to indicate a non-specific
failure. Note that the "rc" value is very system specific, in
particular, some systems support only 8 bit return codes, so values
which are greater than 255 should be avoided.
EXAMPLES:
exit(0); /* success */
exit(-1); /* failure */
FCLOSE FCLOSE
PROTOTYPE:
fclose(FILE *fp);
ARGUMENTS:
fp - File pointer to an open file
RETURN VALUE:
None
DESCRIPTION:
This function closes a file which was previously opened using
"fopen". 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);
FGET FGET
PROTOTYPE:
fget(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 is read in "raw" form, with
no interpretation of "newline" characters etc. 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 occured (in which
case the value will be zero).
EXAMPLES:
fget(block, 512, input_fp);
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 strng is terminated with the standard NULL (00) character. The
trailing NEWLINE '\n' character is NOT included in the output buffer.
EXAMPLES:
fgets(input_line, 80, input_file);
FOPEN FOPEN
PROTOTYPE:
FILE *fopen(char *filename, char *options)
ARGUMENTS:
filename- Name of the file to open
options - String containing open options, valid modes are:
"r" - Open file for read
"w" - Open file for write
"rw" - Open for read/write update.
"wa" - Open for write & append
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 input ('r') or output ('w'),
allowing subsequent I/O operations to read or write the file.
EXAMPLES:
fp = fopen("input_file", "r");
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". These "conversion
characters" are identified by a precedding '%', 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 - Hexidecimal 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 zero's, 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".
EXAMPLES:
fprintf(stderr,"Filename='%s'\n", filename);
fprintf(stdout,"Address=%04x\n", address);
fprintf(outfile,"Amount: $%3u.%02u\n", amount / 100, amount % 100);
FPUT FPUT
PROTOTYPE:
fput(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:
The number of bytes written.
DESCRIPTION:
This function writes a block of data to the indicated file from
memory at the specified address. Data is written in "raw" form, with
no translations of "newline" characters etc. If the value returned is
less than the value of the "size" parameter, some error condition has
occured (Such as disk full).
EXAMPLES:
if(fput(buffer, 100, fp) < 100)
abort("File write error\n");
FPUTS FPUTS
PROTOTYPE:
fputs(char *string, FILE *fp)
ARGUMENTS:
string - Pointer to a character string
fp - FIle pointer to output file
RETURN VALUE:
None
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);
GETC GETC
PROTOTYPE:
int getc(FILE *fp)
ARGUMENTS:
fp - File pointer to an input file
RETURN VALUE:
Value of a character read from the file (0-255).
-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.
EXAMPLES:
if((c = getc(input_file)) < 0)
abort("End of file encountered\n");
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);
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 as ASCII digit
('0'-'9'), otherwise FALSE (0) is returned.
EXAMPLES:
value = 0;
while(isdigit(*ptr))
value = (value * 10) + (*ptr++ - '0');
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);
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);
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);
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.c" source code.
EXAMPLES:
first_arg = nargs() * 2 + &arguments;
PRINTF PRINTF
PROTOTYPE:
register printf(char *format, arg1, arg2, ...)
ARGUMENTS:
format - Pointer to format string
arg - Argument as determined by format string
... - Additional arguments may be required
RETURN VALUE:
None
DESCRIPTION:
The "printf" routine performs a formatted print to the console
device. The "format" string is written to the console 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".
EXAMPLES:
printf("Hello world!!!\n");
printf("File '%s', has %u lines\n", filename, num_lines);
PUTC PUTC
PROTOTYPE:
putc(char c, FILE *fp)
ARGUMENTS:
c - Any character value
fp - File pointer to an output file
RETURN VALUE:
None
DECRIPTION:
This function writes the character 'c' to the file indicated by
"fp". The "newline" (\n) character will be translated into whatever
character(s) are required by the operating system to separate records
in the file.
EXAMPLES:
putc('*', fp);
putc('\n', stderr);
SPRINTF SPRINTF
PROTOTYPE:
register sprintf(char *string, char *format, arg1, arg2, ...)
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 "fprintf" for more information on format strings.
NOTE: This function uses a variable number of arguments, and must
be declared as "register".
EXAMPLES:
sprintf(header_file, "/lib/%s.h", header_name);
STRCAT STRCAT
PROTOTYPE:
strcat(char *dest, *source)
ARGUMENTS:
dest - Pointer to destination string
source - Pointer to source string
RETURN VALUE:
None
DESCRIPTION:
This function concatinates the source string onto 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");
STRCMP STRCMP
PROTOTYPE:
strcmp(char *string1, char *string2)
ARGUMENTS:
string1 - Pointer to first string
string2 - Pointer to second string
RETURN VALUE:
0 - String 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:
strcpy(char *dest, char *source)
ARGUMENTS:
dest - Pointer to destination string
souce - Pointer to source string
RETURN VALUE:
None
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
DECRIPTION:
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);
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(getc(stdin));
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:
putc(toupper(output_char), stdout);
ZERO ZERO
PROTOTYPE:
zero(char *block, unsigned size)
ARGUMENTS:
block - Pointer to a block of memory
size - Number of bytes to zero
RETURN VALUE:
None
DESCRIPTION:
Zeros a block of memory beginning at the pointer "block", for
"size" bytes.
EXAMPLES:
zero(buffer, 100);
MICRO-C
TABLE OF CONTENTS
Page
1. INTRODUCTION 1
1.1 Code Portability 2
1.2 Compiler Portability 2
1.3 The MCC command 3
1.4 Command files 3
2. THE MICRO-C PROGRAMMING LANGUAGE 4
2.1 Constants 4
2.2 Symbols 4
2.3 Arrays & Pointers 7
2.4 Functions 7
2.5 Control Structures 8
2.6 Expression Operators 10
2.7 Preprocessor Commands 12
2.8 Error Messages 13
2.9 Quirks 17
3. ADVANCED TOPICS 19
3.1 Conversion Rules 19
3.2 Assembly Language Interface 20
3.3 Compiling for ROM 23
4. PORTING THE COMPILER 24
4.1 The "io" module 25
4.2 The "code" module 27
4.3 The "compile" module 31
4.4 Porting without an existing compiler 32
4.5 Porting without a linker 32
4.6 Code Optimization 33
5. THE MICRO-C PREPROCESSOR 36
5.1 The MCP command 37
5.2 Preprocesor Commands 38
5.3 Error messages 41
6. THE MICRO-C OPTIMIZER 43
6.1 The MCO command 43
6.2 Porting to a new processor 44
7. LIBRARY FUNCTIONS 45