home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frostbyte's 1980s DOS Shareware Collection
/
floppyshareware.zip
/
floppyshareware
/
GLEN
/
MC20.ZIP
/
MC.DOC
< prev
next >
Wrap
Text File
|
1990-08-20
|
262KB
|
7,445 lines
MICRO-C
A compact 'C' compiler
for small systems.
Technical Manual
Release 2.0
Revised 10-Aug-90
Copyright 1988,1990 Dave Dunfield
All rights reserved
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 chosen
because it was designed to be portable, and provides ample
programming power. 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 fill that gap, because all files
necessary to port and maintain the compiler are included on the
distribution disk, allowing it to be moved to ANY system without
dependance on a software vendor.
Sample code generators and runtime librarys are included for
several popular microprocessors.
Also included with the compiler is the 'C' and 'ASM' source code
for a complete library of system functions, as well several example
programs.
The MICRO-C "package" (software and documentation) is copyrighted,
and may not be re-distributed in any form without my written
permission. If you have received the "DEMO" archive, and wish to
obtain the entire MICRO-C package complete with source code, fill out
and send the order form contained in the "READ.ME" file, along with
the required payment to:
Dave Dunfield
56 Burnetts Grove Circle,
Nepean, Ont. (Canada)
K2J 1N6
If you choose not to order the complete MICRO-C package, you MUST
discontinue using MICRO-C within thirty days.
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.
MICRO-C Page: 2
1.1 Code Portability
With 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 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
-q - Instructs MCC to be quiet, and not display the
startup message when it is executed.
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 (0 - 65535)
0<num> - Octal number (0 - 0177777)
0x<num> - Hexidecimal number (0x0 - 0xffff)
'<char>' - Character (1 or 2 chars)
"<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 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
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 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 getc();
A symbol declared as external may be re-declared 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 variables to be available
only in the file where they are defined. Variables or functions
declared as "static" will not be accessable as "extern"
declarations in other object files, nor will they cause conflicts
with duplicate names in those files.
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-initialized global variables are generated at the
very end of the output file, after the literal pool is dumped.
Since non-initialized globals do not generate object 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 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.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.
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 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;
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
(type) - Typecast
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
^= - EXCLUSIVE OR 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.
NOTE: This describes the limited pre-processor which is
integral to the compiler, see also the section on the more
powerful external processor (MCP).
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 indicated 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 or #endif) only
if the given name is defined.
2.7.6 #ifndef <name>
Processes the following lines (up to #else or #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.
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 number(s) 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 Declaration 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 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.8.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.8.12 Illegal nested function
You may not declare a function within the definition of
another function.
2.8.13 Improper #else/#endif
A #else or #endif statement is out of place.
2.8.14 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.15 Incorrect declaration
A statement occuring outside of a function definition is not
a valid declaration for a function or global variable.
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 No active loop
A "continue" or "break" statement was encountered when no
loop is active.
2.8.20 No active switch
A "case" or "default" statement was encountered when no
"switch" statement is active.
2.8.21 Not an argument: 'name'
You have declared the named variable as an argument, but it
does not appear in the argument list.
2.8.22 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.23 Numeric constant required
The compiler requires a constant expression which returns a
simple numeric value.
2.8.24 String space exhausted
The compiler has encountered more literal strings than it
has room store.
2.8.25 Symbol table full
The compiler has encountered more symbol definitions than it
can handle.
2.8.26 Syntax error
The statement shown does not follow syntax rules and cannot
be parsed.
2.8.27 Too many active cases
The compiler has run out of space for storing switch/case
tables. Reduce the number of active "cases".
2.8.28 Too many defines
The compiler has encountered more '#define' statements than
it can handle. Reduce the number of #defines.
2.8.29 Too many errors
The compiler is aborting because of excessive errors.
MICRO-C Page: 16
2.8.30 Too many includes
The compiler has encountered more nested "#include" files
than it can handle.
2.8.31 Too many initializers
You have specified more initialization values than there are
locations in the global variable.
2.8.32 Type clash
You have attempted to combine values which are of
incompatible types.
2.8.33 Unable to open: 'name'
A "#include" command specified the named file, which could
not be opened.
2.8.34 Undefined: 'name'
You have referenced a name which is not defined as a local
or global symbol.
2.8.35 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.8.36 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.8.37 Unterminated conditional
The end of file was encountered when a "#if" or "#else"
conditional block was being processed.
2.8.38 Unterminated function
The end of the file was encountered when a function
definition was still open.
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 "/*"
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
NOTE: The above quirks do not apply when the external
pre-processor (MCP) is used.
The appearance 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 produced in such cases,
simply drop the '&'.
The 'x' in '0x' and '\x' is accepted in lower case only.
MICRO-C Page: 18
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 cptr; /* 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 */
When using MICRO-C to create a function which will 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
}
Also note, that 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 assembly code (using "#asm") must be declared
in assembly code, not by the MICRO-C "extern" statement.
MICRO-C Page: 19
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 efficent code when dealing
with characters, particularily 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 (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 efficent (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 efficent code generated.
MICRO-C Page: 20
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.
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).
MICRO-C Page: 21
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. If a value is to be returned to the calling program, it
is expected to be in the ACCUMULATOR register.
MICRO-C Page: 22
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: 23
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: 24
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 initialized global 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.
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: 25
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". When porting to systems
with very limited RAM (less than 64k), you may have to reduce some of
these sizes in order to get it to fit.
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.
NOTE: If your system makes a distinction between TEXT and BINARY
files, the "io.c" routines must be modified for each program so that
the intermediate file will be accessed in BINARY mode. All other
files are accessed as TEXT.
MICRO-C Page: 26
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 can be 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: 27
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 files could not be opened.
Refer to the sample "io" modules distributed with the
compiler.
MICRO-C Page: 28
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 the
compiler symbol tables for a global variable, which is about to be
initialized as static storage. 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: 29
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. When defining
"register" functions, care must be taken that the entry code
preserves the contents of the accumulator.
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-initialized 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: 30
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: 31
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.
MICRO-C Page: 32
If you intend to use the MICRO-C Source Linker (SLINK), then
you have to insure that whatever variable you use to reference
the "literal pool" qualifies as a "compiler generated" label,
allowing SLINK to adjust it when processing the source files.
Compiler generated labels normally consist of a single
character (usually '?'), followed by a decimal number. Since
the compiler begins generating its labels with the value '1',
you may safely use '0' as the literal pool variable (Ie: '?0').
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.
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.
MICRO-C Page: 33
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.
A number of fixed parameters to the compiler (such as table
sizes etc.) are contained in the header file "compile.h". The most
common reason for changing these parameters is to reduce the
memory requirements, in order to get MICRO-C to fit into very
small systems.
If any of the parameters in "compile.h" are to be changed, you
must make the changes BEFORE compiling any of the modules
(compile, io or codegen).
The file "tokens.h" contains symbolic definitions for the
tokens parsed by the compiler, the text of which is in the
character array variable "tokens". If you make any changes to this
token table, MAKE SURE that the contents of the "tokens" variable
and the "tokens.h" file agree, otherwise, you will end up with a
compiler for a very strange language.
MICRO-C Page: 34
4.4 Porting without a compiler
If you have the MICRO-C distribution files, but no running
compiler, it is still possible to port MICRO-C, 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: 35
4.6 Optimization Techniques
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
performed 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 complicated expressions are
handled by placing 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: 36
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: 37
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: 38
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 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.
- Nested conditionals.
- Ability to undefine MACRO symbols.
- Library reference in include file names.
MICRO-C Page: 39
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'
-q - Instructs MCP to be quiet, and not display the
startup message when it is executed.
<name>=<text> - Pre-defines a non-parameterized macro of the
specified <name> with the string value <text>.
MICRO-C Page: 40
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 macro
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: 41
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 '#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: 42
5.2.8 #include <filename>
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. The default library directory is assumed to be
'/mc'.
For example:
#include "header.h" /* from current directory */
#include <stdio.h> /* from library directory */
5.2.9 #asm / #endasm
The '#asm' and '#endasm' statements 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: 43
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 reading.
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 macro 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 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: 44
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 on the indicated line has no end.
MICRO-C Page: 45
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 which the optimized
assembly code is to be written. 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: 46
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 (eg:
'\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: 47
7. THE COMMAND CO-ORDINATOR
'CC' is a program which co-ordinates the other commands (MCP, MCC,
MCO, ASM and LINK), to provide a simple "one step" compilation
command.
7.1 The CC command
The format of the Command Co-ordinator command line is:
CC <name> [options]
7.1.1 Command line options
-a - produce ASSEMBLER (.ASM) output file
-l - produce LINKABLE (.OBJ) output file
-o - OPTIMIZE the output file (using MCO)
-p - use the extended PRE-PROCESSOR (MCP)
-q - QUIET mode (suppress informational messages)
-s - produce SMALL-MODEL (.EXE) output file
m=mcdir - specify MICRO-C home directory
t=mctmp - specify prefix for TEMPORARY files
When executing the sub-commands, CC will search the MICRO-C
home directory, as well as any directories specified in the
'PATH' environment variable. Libraries are accessed from the
MICRO-C home directory only.
The environment variable 'MCDIR' is examined to determine
the path to the MICRO-C home directory. If MCDIR is not
defined, CC will assume the string '\MC'. You may override this
directory by using the 'm=' option on the command line.
Intermediate results from each command are stored in
"temporary" files, which are fed as input to the next command.
Temporary files will be deleted once they are no longer needed,
except in the case where a command fails. When this happens,
any temporary file which was being used as input to that
command will not be deleted, allowing you to examine it for the
cause of the error.
The environment variable 'MCTMP' is examined to determine a
prefix to prepend to temporary file names. If MCTMP is not
defined, CC assumes the default prefix of '$'. You may override
this prefix by using the 't=' option on the command line.
Here are example 'SET' commands suitable for inclusion in
the AUTOEXEC.BAT file, of an IBM/PC based MICRO-C system which
has the home directory in 'C:\MC', and a RAMDISK as drive 'D':
SET MCDIR=C:\MC
SET MCTMP=D:\$
MICRO-C Page: 48
The '-p' option causes the extended pre-processor (MCP) to
be used. This provides greater pre-processor capability, at the
expense of longer compile time.
NOTE: If any compiler errors occur when using this option,
the line numbers contained in the error messages will refer to
the temporary file containing the output from the pre-processor
($<name>.CP). You must examine this file in order to determine
the actual cause of the error.
The '-o' option causes the optimizer (MCO) to be used, which
results in more efficent code, at the expense of longer compile
time.
The '-a' option causes CC to bypass the assembler and
linker, and produce an assembly source file (<name>.ASM) as the
output file.
The '-l' option causes CC to bypass the link stage, and
produce a linkable object module (<name>.OBJ) as the output
file.
The '-s' option causes CC to link the program using the
SMALL memory model, and produce an executable image
(<name>.EXE) as the output file. This option applies only to
those processors which have a segmented architecture and
multiple memory models such as the 8086.
If none of '-a', '-l' or '-s' is specified, CC will link the
program using the TINY memory model, and produce a command
image (<name>.COM) as the output file.
7.2 Using multiple object modules
The 'LC' command file takes one or more object modules produced
by 'CC' with the '-l' option, and links them with the libraries to
produce an executable module. This module will be given the name
of the first file specified in the argument list.
When compiling large programs which have more than one source
file, you must first compile all modules using 'CC' with the '-l'
option, and the use 'LC' to link the resultant object files into
the final executable program.
The '-s' option may be used as the FIRST argument, to cause LC
to link in the SMALL model (8086 only).
eg: CC FIRST -l
CC SECOND -l
LC FIRST SECOND <-- TINY Model
LC -s FIRST SECOND <-- SMALL Model
The exact syntax and options available for 'CC' and 'LC' may
differ in different implementations of the compiler. See the
'READ.ME' file on your distribution disk for details.
MICRO-C Page: 49
8. THE MAKE UTILITY
The MAKE utility provides a method of automating the building of
larger programs consisting of more that one object module. The main
benefit of MAKE is that it keeps track of the files that each module
is dependant on, and will rebuild a module if any of those files have
been modified since the module was last built. This frees the
programmer from the task of remembering which files have been
changed, and the commands needed to rebuild the dependant modules.
8.1 MAKEfiles
To use MAKE, you must first create a MAKEFILE, which is a text
file containing entries for each module in the program. Each entry
consists of a DEPENDANCY list, and a series of COMMANDS.
8.1.1 MAKEfile Entries
A dependency list in MAKE is a line which contains the name
of the module, followed by a ':', followed by the names of any
files on which it depends. The module name MUST begin in column
1.
When MAKE is invoked, it will process each dependancy list,
and will execute any following commands (up to another
dependancy list) if (1) the module does not exist, or (2) if
any of the files to the right of the ':' have a timestamp which
is later than that of the module. For example:
main.obj : main.c main.h \\mc\\stdio.h
\\mc\\mcc main.c main.asm
masm/ml main;
-del main.asm
In the above example, the 'main.obj'would be rebuilt (by
compiling and assembling 'main.c') if either it did not already
exist, or any of 'main.c', 'main.h' or '\mc\stdio.h' was found
to have a later timestamp.
The '-' preceeding the 'del' command prevents it from being
displayed. Unless the '-q' option is enabled, MAKE will display
any commands not preceeded by '-' as they are executed.
NOTE: To enter a single '\' in the MAKEFILE, you must use
'\\', this is because like 'C', MAKE uses '\' to "protect"
special characters which otherwise are used for special
functions (such as '\', '$' and '#'). The first '\' "protects"
the second one, allowing it to pass through as source text.
MICRO-C Page: 50
8.1.2 Macro Substitutions
Sometimes in a MAKEFILE, you have a single file or directory
path that you use over and over again. If it is a long
directory path, this may involve a lot of typing, and it
becomes inconvenient to change that name (if you want to use a
different directory etc.) because it is repeated many times.
MAKE includes a MACRO facility, which allows you to define
variable names which will be replaced with a text string when
used in subsequent MAKEfile lines. Names are defined by placing
them in the MAKEfile, followed by '=', and the text string.
Macro names being defined MUST begin in column one, and may
consist of the characters ('a'-'z', 'A'-'Z', '0'-'9', and '_').
Whenever MAKE encounters a '$' in the file, it takes the
name immediately following, and performs the macro replacement:
mcdir = \\mc
main.obj : main.c main.h $mcdir\\stdio.h
$mcdir\\mcc main.c main.asm
masm/ml main;
del main.asm
When a macro name is immediately followed by alphanumeric
text, use a single '\' to separate it from the text. This
"protects" the first character of the text from being
interpreted as part of the macro name:
mcdir = \\mc\\
main.obj : main.c main.h $mcdir\stdio.h
$mcdir\mcc main.c main.asm
masm/ml main;
del main.asm
There are several predefined macro symbols which are
available:
$* = The full name of the dependant module (name.type).
$@ = The name only of the dependany module.
$. = The full name of each file in the dependancy list,
separated from each other by a single space.
$, = The full name of each file in the dependancy list,
separated from each other by a single comma.
$: = The name only of each file in the dependancy list,
separated from each other by a single space.
$; = The name only of each file in the dependancy list,
separated from each other by a single comma.
MICRO-C Page: 51
File names in the dependancy list which are preceeded by '-'
will not be included in the '$. $, $: $;' macro expansions:
mcdir = \\mc
main.obj : main.c -main.h -$mcdir\\stdio.h
$mcdir\\mcc $. $@.ASM
masm/ml $@;
del $@.ASM
8.1.3 MAKEfile Comments
Whenever MAKE encounters the '#' character in the MAKEFILE,
it treats the remainder of the line as a comment, and does not
process it:
# Define Directories
mcdir = \\mc
# Build the MAIN module
main.obj : main.c -main.h -$mcdir\\stdio.h # Dependants
$mcdir\\mcc $. $@.ASM # Compile
masm/ml $@; # Assemble
del $@.ASM # Delete tmp
8.1.4 Ordering the MAKEfile
MAKE processes the MAKEfile is sequential fashion, with the
entries near the top being processed before the entries near
the bottom. To insure that each module is built properly, any
files appearing in the dependancy list for a module which are
themselves dependant on other files, should have MAKEfile
entries which occur BEFORE the entries for the modules which
are dependant on them:
# Define Directories
mcdir = \\mc
# Build the MAIN module
main.obj : main.c -main.h -$mcdir\\stdio.h
$mcdir\\mcc $. $@.ASM
masm/ml $@;
del $@.ASM
# Build the SUB module
sub.obj : sub.c -sub.h
$mcdir\\mcc $. $@.ASM
masm/ml $@;
del $@.ASM
# Bind the executable file
# NOTE: If either of the above modules is rebuilt,
# this entry will be guarenteed to execute.
main.exe : main.obj sub.obj
LINK $:;
MICRO-C Page: 52
8.2 Directives
Like 'C', MAKE recognizes several "directives" in the MAKEfile.
These directives are only recognized if they occur at the
beginning of the input line:
8.2.1 @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 MAKEfile.
8.2.2 @ifdef <name>
Processes the following lines (up to @else of @endif) only
if the given MACRO name is defined. NOTE: <name> should not be
preceeded by '$', otherwise its CONTENTS will be tested.
8.2.3 @ifndef <name>
Processes the following lines (up to @else of @endif) only
if the given MACRO name is NOT defined.
8.2.4 @ifeq <word1> <word2>
Processes the following lines (up to @else of @endif) only
if the following two words match exactly. This is useful for
testing the value of a defined MACRO symbol.
8.2.5 @ifne <word1> <word2>
Processes the following lines (up to @else or @endif) only
if the following two words do not match.
8.2.6 @else
Processes the following lines (up to @endif) only if the
preceeding @ifdef, @ifndef, @ifeq or @ifne was false.
8.2.7 @endif
Terminates @ifdef, @ifndef, @ifeq and @ifne.
8.2.8 @type <text>
Displays the following text.
8.2.9 @abort [text]
Terminates MAKE with an 'Aborted!' message. Any text on the
remainder of the line will be appended to the message.
MICRO-C Page: 53
8.3 The MAKE command
The format of the MAKE command line is:
MAKE [makefile] [options]
[makefile] is the name of the MAKEfile to process. If no name
is given, MAKE assumes the default name 'MAKEFILE'.
8.3.1 Command Line Options
MAKE accepts the following command line [options]:
-d - Instructs MAKE to operate in "debug" mode, and
display the commands which it would execute,
without actually executing them. This provides
a method of quickly testing the MAKEFILE.
-q - Instructs MAKE to be quiet, and not display the
informational messages and commands executed as
it progresses.
<name>=<text> - Pre-defines a macro of the specified <name>
with the string value <text>. This OVERRIDES
any definition within the MAKEfile, which may
be used to establish a "default" value.
MICRO-C Page: 54
8.4 The TOUCH command
TOUCH is a small utility program which sets the timestamp of
one or more files to the current or specified time/date. It is
useful as a method of forcing MAKE to recognize a file as
"changed", even when it has not.
For example, if you had decided to "undo" several recent
changes by restoring a backup of "main.c", the restored file will
probably have a timestamp which is older than the last module
which was built. In this case, MAKE would be unaware that the file
has changed, and would therefore not rebuild the module.
The TOUCH command could then be used to "update" the timestamp
of 'main.c' to the current time, causing MAKE to recognize it as a
changed file.
TOUCH main.c
You could also use TOUCH to force rebuilding of several files:
TOUCH main.c sub1.c sub2.c
Or even ALL '.C' files:
TOUCH *.c
TOUCH can also be used to set the timestamp of a file to an
arbritrary value, this may be useful to PREVENT a change from
causing an update:
TOUCH main.c t=0:00 d=31/10/80
NOTE: Use of the 't=' or 'd=' parameters to TOUCH allows the
possibility that a changed file will go unnoticed. CAUTION is
advised.
MICRO-C Page: 55
9. THE SOURCE LINKER
Many small development environments have assemblers which do 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 tell which library functions to include,
therefore, you must to it manually.
2) 'C' library functions must be re-compiled every time, in
order to avoid conflict between compiler generated labels.
3) Assembly language library functions must be modified to
include the "#asm" and "#endasm" directives.
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 compiler output. Only
those files containing functions which you reference are joined
(Taking into consideration of course any functions called by the
included library functions etc...). As the files are joined, compiler
generated labels are automatically adjusted to be unique within each
file.
9.1 The SLINK Command
The format of the SLINK command line is:
SLINK [input_file] [output_file] [options]
[input_file] is the name of the source file containing the
compiler output from your program. If no filenames is given, SLINK
will read from standard input.
[output_file] is the name of the file to write the linked
source code to. If less that two filenames are given, SLINK will
write to standard output.
9.1.1 Command line options
SLINK accepts the following command line [options]:
g=char - Informs SLINK of the lead-in character for
compiler generated symbols. This defaults
to '?' unless otherwise specified.
-l - Instructs SLINK to list each library file
being included in your program.
l=path - Identifies the directory path which will be
taken to reference "library" files. If this
path is not specified, it defaults to:
'/mc/slib'
-q - Instructs SLINK to be quiet, and not display
the startup message when it is executed.
MICRO-C Page: 56
9.2 The External Index File
SLINK uses a special file from the library to determine which
sysmbols are in which files. This files is called the EXTERNAL
INDEX FILE, and is found in the library directory (see 'l='
option), under the name "EXTINDEX.LIB".
This file contains entries which cross-reference external
symbols to files. Each entry is as follows:
1) Any lines beginning with '+' contain the names of files
which are to be processed and included at the beginning
of the program (Before your source file). This the best
way to include the startup code and any runtime library
routines which are required at all times. The files are
included in the order in which they appear in the index
file.
eg: +6809rl.asm
2) Any lines beginning with '-' contain the names of files
which are to be included if any of the following
symbols (Up to another '+' or '-') are referenced.
eg: -printf.asm format.asm fput.asm
NOTE: In most cases, the library functions will contain
indications of any external references that they do, in
which case SLINK will automatically include those files
even of the names are not mentioned on the '-' line. In
the example above, the following would suffice:
-printf.asm
3) The names of each symbol which may be referenced
externally must follow the '+' or '-' entry. Symbols
must occur one per line, with no leading or trailing
spaces.
eg: printf
fprintf
sprintf
4) A line beginning with '$' is used to define the pseudo-
opcode used by SLINK to reserve uninitialized data at
the end of the output file. One one line beginning with
'$' should be entered into the EXTINDEX.LIB file. The
remainder of this line, including all spaces etc. is
entered between each symbol name, and the decimal size
(in bytes) which is written to the output file.
eg: '$ RMB ' <- Quotes are for clarity
MICRO-C Page: 57
A complete example:
+6809rl.asm
-printf.asm
printf
fprintf
sprintf
-scanf.asm
scanf
fscanf
sscanf
$ RMB
9.3 Multiple source files
SLINK processes only one source file, and resolves external
references only to the library files. This is because the public
symbol information is available in the EXTINDEX.LIB files, which
contains no entries for the user supplied programs.
If you wish to use source code linking for a program which
contains multiple source files, use "#include" statements at the
end if your main program to include all of the other parts into a
single source file.
9.4 Source file information
9.4.1 Special Comments
SLINK identifies external symbol references by special
comments in the input source file. These comments must begin in
column one, and are of the form:
*EXT*<symbol>
where <symbol> is the name of the symbol being referenced.
These comments are placed into the source file by the
"def_extern" routine in the MICRO-C code generator.
MICRO-C Page: 58
SLINK also identifies uninitialized data area by special
comments placed in the input source file. The comments must
begin in column one, and are of the form:
*DAT*<symbol> <size>
where <symbol> is the name of the symbol used to reference the
data area, and <size> is the number of bytes to be reserved.
SLINK will output statements to allocate the data area at the
very end of the assembly language output file. This prevents
the uninitialized data from occupying space within the bounds
of executable code generated, and thus allows it to be excluded
from the image when it is saved to disk. These comments are
placed into the source file by the "def_global" routine in the
MICRO-C code generator.
The reserved data area are allocated at the end of the
output file, in the REVERSE order of which they are encountered
in the input files. This insures that the FIRST "*DAT*" comment
found the LAST symbol name output. This allows you to place a
"*DAT*" comment at the beginning of the startup file, and use
it to allocate heap space at the end of all other data.
If you are writing assembly language programs for the
library, be sure include "*EXT*<symbol>" comments for any
symbols which you externally reference, and "*DAT*<symbol>
<size>" comments for any uninitialized data area you may wish
to allocate. If you wish to place an assembly language comment
on the same line, make sure it is separated from the remainder
of the special comment line by at least one space or tab
character.
Note that since SLINK removes these "special" comments from
the output file, they do not have to be in a form acceptable to
the assembler. Ie: if your assembler uses ';' for comments, you
don't not have to change the code generator and SLINK, since
the assembler will not see the special comments it generates.
9.4.2 Compiler generated labels
As it processes each source file, SLINK scans each line for
symbols which consist of the '?' character (See 'g=' option),
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 be 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: 59
9.5 The SINDEX utility
SINDEX is a utility which assists in the creation of the
EXTINDEX.LIB file used by SLINK. When you run SINDEX, it examines
all of the '.ASM' files in the current directory, and writes a
EXTINDEX.LIB file which contains a '-' type entry for each file,
and external symbol entries for any labels which it finds
conforming to the 'C' naming conventions (Starts with 'a-z', 'A-Z'
or '_', and contains only 'a-z', 'A-Z', '0-9' or '_').
Once you have run SINDEX, you must manually edit the
EXTINDEX.LIB file, and remove any file or symbol entries which you
do not wish to have available as external references, as well as
change any necessary entries to the '+' type.
NOTE: There is no information recorded in the assembly source
file which indicates that a symbol was declared as "static" in
'C'. For this reason, you must manually remove any symbols which
you do not want to be accessable as external references, EVEN IF
THEY WERE ORIGINALLY DECLARED AS "static" IN 'C'.
You may instruct SINDEX to search for a file pattern other than
'*.ASM' by passing it as a command line parameter.
eg: SINDEX *.A86
MICRO-C Page: 60
10. LIBRARY FUNCTIONS
The MICRO-C distribution disk includes the 'C' and 'ASM' 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 are written in 'C' using still lower
level routines. 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.
10.1 Standard Library Functions
These routines are currently available in the MICRO-C library
as "standard" functions which should be available in all
implementations.
The exact syntax and capabilities of the system or processor
dependant functions may vary in different implementations, see
your implementation notes (READ.ME) for details. Different
possible forms of such functions are shown using (1), (2), ...
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");
CD CD
PROTOTYPE:
int cd(char *pathname)
ARGUMENTS:
pathname- Name of directory to make current
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function sets the "current" directory, causing all subsequent
file references which do not explicitly indicate a directory path to
access the path specified by "pathname".
EXAMPLES:
cd("/mc/c_source"); /* UNIX */
cd("\\mc\\l_source"); /* MS-DOS */
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" (See "stdio.h").
EXAMPLES:
concat(filename,"/tmp/", input_name);
CREATE CREATE
PROTOTYPE:
int create(char *pathname, int attrs)
ARGUMENTS:
pathname- Name of file to create
attrs - Attributes for new file
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
The "create" function creates a new file with the specified system
attributes.
The meaning of the individual bits in the "attrs" value is system
dependant, and is defined in the "file.h" header file.
EXAMPLES:
create("temp", HIDDEN);
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");
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:
int 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 string 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);
FIND_FIRST FIND_FIRST
PROTOTYPE:
int find_first(char *pattern, int mattrs, char name[], int &sizeh,
int &sizel, int &attrs, int &time, int &date)
ARGUMENTS:
pattern - File name pattern to match
mattrs - File attributes to match
name - Address of string to receive file name
&sizeh - Address of int to receive high word of size
&sizel - Address of int to receive low word of size
&attrs - Address of int to receive attributes
&time - Address of int to receive time stamp
&date - Address of int to receive date stamp
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function locates the first file on the disk which matches the
given pattern. The "mattrs" field specifies any special attributes
the files must have in order to be matched, use 0 for normal
directory searches.
Subsequent files may be located using the "find_next" function.
The above function prototype describes the MS-DOS implementation
of the function. With other operating systems, the function may have
slightly different parameters, due to differing information
available. See your implementation notes.
EXAMPLES:
if(find_first(pattern, 0, name, &sh, &sl, &a, &t, &d))
abort("No matching files found\n");
FIND_NEXT FIND_NEXT
PROTOTYPE:
int find_next(char name[], int &sizeh, int &sizel, int &attrs,
int &time, int &date)
ARGUMENTS:
name - Address of string to receive file name
&sizeh - Address of int to receive high word of size
&sizel - Address of int to receive low word of size
&attrs - Address of int to receive attributes
&time - Address of int to receive time stamp
&date - Address of int to receive date stamp
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
The function must be preceeded by a call to "find_first", and will
locate the next file on the disk which matches the pattern and
attributes given to that function call.
The above function prototype describes the MS-DOS implementation
of the function. With other operating systems, the function may have
slightly different parameters, due to differing information
available. See your implementation notes.
EXAMPLES:
do
printf("%s\n", name);
while(!find_next(name, &sh, &sl, &a, &t, &d));
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 preceeding '%', 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" (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);
FPUT FPUT
PROTOTYPE:
int 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:
0 if successful, otherwise an operating system error code
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:
0 if successful, otherwise an 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);
FREE FREE
PROTOTYPE:
free(char *ptr)
ARGUMENTS:
ptr - Pointer to a previously allocated memory block
RETURN VALUE:
None
DESCRIPTION:
The "free" function releases (de-allocates) 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");
do { /* Copy the file over */
size = fget(ptr, BUFSIZ, in_fp);
fput(ptr, size, out_fp); }
while(size == BUFSIZ);
free(ptr); /* Release temporary buffer */
FSCANF FSCANF
PROTOTYPE:
register int fscanf(FILE *fp, char *format, &arg, ...)
ARGUMENTS:
fp - File pointer to an input file
format - Pointer to format string
arg - Argument as determined by format string
... - Additional arguments may be required
RETURN VALUE:
The number of successful matches
EOF (-1) if end of file or an error condition occurs
DESCRIPTION:
This routine reads a line from the specified file and scans it for
specific values which are then assigned to the passed argument
addresses. The types of values scanned are determined by special
"conversion characters" within the "format" string. These "conversion
characters" are identified by a preceeding '%', 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)
Before scanning for any value, any leading "space" or "tab"
characters in the input line are automatically flushed.
Scanning of a particular value type will terminate when either the
end of the line or a non-applicible character is encountered.
Scanning of strings (%s) stops with a "space" or "tab" character, and
the stored string will be zero terminated.
A numeric "field width" specifier may be placed in between the '%'
and the conversion character, in which case scanning of the value
will also terminate if that many input characters have been
processed.
Scanning for characters (%c) assumes a "field width" of 1
character (%1c) unless it is otherwise specified.
Any other (non-conversion) characters in the format string will
cause the next character in the input string which is not a "space"
or "tab" to be skipped if it matches that character.
Any variable values from the input line which are not required
must be cleared by scanning them into a "dummy" variable.
The most common mistake when using "fscanf" is forgetting to use
the '&' operator with a simple variable argument. This causes
"fscanf" to store the value INDIRECTLY at the address contained in
that variable (Similar to using '*' with a pointer) instead of
storing the value into the actual variable itself. Arguments which
are array names do not require the '&' operator, since the address of
the array is already generated by reference to its name.
Unlike "fscanf" in most other compiler libraries, the MICRO-C
function always reads and scans a single line from the input file.
NOTE: This function uses a variable number of arguments, and must
be declared as "register".
EXAMPLES:
if(fscanf(infile,"%s %s %u", first_name, last_name, &age) != 3)
abort("Error in user record\n");
FSEEK FSEEK
PROTOTYPE:
(1) int fseek(FILE *fp, int h_offset, unsigned l_offset, int mode)
(2) int fseek(FILE *fp, int offset, mode)
ARGUMENTS:
fp - File pointer to an open file
h_offset- Highest 16 bits of offset value
l_offset- Lowest 16 bits of offset value
offset - 16 bit offset value
mode - Type of seek
0 = Absolute from start of file
1 = Signed offset from current position
2 = Signed offset from end of file
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function positions the operating system internal pointer into
a file so that any read or write will take place at the specified
position in the file.
Most operating systems which support files of > 64K bytes in size
will support form (1) of the function, using both high and low offset
values.
Smaller operating systems may only support form (2) of the
function, allowing seeking of only +/- 32K bytes.
Also, some implementations may not support all "modes", or may
only allow unsigned (positive) offsets.
EXAMPLES:
fseek(in_file, 0, 2); /* Advance to end of file */
FTELL FTELL
PROTOTYPE:
(1) int ftell(FILE *fp, int &h_offset, unsigned &l_offset)
(2) int ftell(FILE *fp, int &offset)
ARGUMENTS:
fp - File pointer to an open file
h_offset- Address of int to receive high word of offset
l_offset- Address of int to receive low word of offset
offset - Address of int to receive offset
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function gets the current read/write position within a file.
The position returned indicates the absolute character (byte) offset
from the start of the file where the next read or write will take
place.
Most operating systems which support files of > 64K bytes in size
will support form (1) of the function, returning both high and low
offset values.
Smaller operating systems may only support form (2) of the
function, allowing access to only the first 65535 character
positions.
EXAMPLES:
ftell(fp, &oldh, &oldl); /* Save file position */
. . . /* Perform some operations on the file */
fseek(fp, oldh, oldl, 0); /* Return to previous position */
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)
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.
EXAMPLES:
if((c = getc(input_file)) < 0)
abort("End of file encountered\n");
GETDIR GETDIR
PROTOTYPE:
int getdir(char pathname[])
ARGUMENTS:
pathname- Address of string to receive directory path
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function retreives from the system the name of the "current"
directory path.
The "pathname" string must be long enough to hold the largest
pathname supported by the system, as indicated by the "PATH_SIZE"
definition in the "file.h" header file.
EXAMPLES:
getdir(¤t_dir);
GETENV GETENV
PROTOTYPE:
int getenv(char *ename, char *dest)
ARGUMENTS:
ename - String containing name of environment variable
dest - Buffer to receive variable string value
RETURN VALUE:
1 if environment variable was found, 0 if not.
DESCRIPTION:
The GETENV function gets the value of a variable in the programs
environment, and returns it as a string. If the environment variable
is not found, zero is returned, and the destination buffer is set to
a null (zero length) string.
Use of this function allows a programs fixed parameters and
options to be specified once in environment variables, which may then
be extracted by the program, eliminating the need to specify those
values every time the program is executed.
When operating MICRO-C under operating systems which do not
support environment variables, this function will not be available.
EXAMPLES:
if(!getenv("COMSPEC", command))
abort("No command processor defined\n");
IN IN
PROTOTYPE:
int in(unsigned port)
ARGUMENTS:
port - I/O port address
RETURN VALUE:
The 8 bit value read from the given I/O port address
DESCRIPTION:
The "in" function reads and returns a byte (8 bits) from an I/O
port as an integer value between 0 and 255.
The valid range of values for "port" depends on the I/O address
space of the processor.
This function is not provided for processors which do not support
a separate I/O address space.
EXAMPLES:
while(in(0)); /* Wait for flag to clear */
INW INW
PROTOTYPE:
int inw(unsigned port)
ARGUMENTS:
port - I/O port address
RETURN VALUE:
The 16 bit value read from the given I/O port address
DESCRIPTION:
The "inw" function reads and returns a word (16 bits) from an I/O
port as an integer value between 0 and 65535 (-1).
The valid range of values for "port" depends on the I/O address
space of the processor.
This function is not provided for processors which do not support
a separate I/O address space.
EXAMPLES:
var = inw(0);
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);
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:
putc(iscntrl(c) ? '.' : c, stdout); /* 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:
putc(isgraph(c) ? c : '.', stdout);
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);
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);
LONGJMP LONGJMP
PROTOTYPE:
longjmp(int savenv[3], 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(getc(stdin) == ('C'-'@')) /* If Control-C entered... */
longjmp(savearea, 1); /* Return to main function */
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 releases with the "free"
function.
EXAMPLES:
if(!(ptr = malloc(BUFSIZ))) /* Allocate a temporary buffer */
abort("Not enough memory");
do { /* Copy the file over */
size = fget(ptr, BUFSIZ, in_fp);
fput(ptr, size, out_fp); }
while(size == BUFSIZ);
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 souce
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);
MKDIR MKDIR
PROTOTYPE:
int mkdir(char *pathname)
ARGUMENTS:
pathname- Name of directory to create
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
The "mkdir" function create a new directory on the disk under the
specified path name.
EXAMPLES:
mkdir("subdir");
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;
OUT OUT
PROTOTYPE:
out(unsigned port, int value)
ARGUMENTS:
port - I/O port address
value - Value to write to I/O port
RETURN VALUE:
None
DESCRIPTION:
The "out" function writes a byte (8 bit) value to an I/O port.
The valid range of values for "port" depends on the I/O address
space of the processor.
This function is not provided for processors which do not support
a separate I/O address space.
EXAMPLES:
out(0, 0); /* Output 0 to I/O port 0 */
OUTW OUTW
PROTOTYPE:
outw(unsigned port, int value)
ARGUMENTS:
port - I/O port address
value - Value to write to I/O port
RETURN VALUE:
None
DESCRIPTION:
The "outw" function writes a word (16 bit) value to an I/O port.
The valid range of values for "port" depends on the I/O address
space of the processor.
This function is not provided for processors which do not support
a separate I/O address space.
EXAMPLES:
outw(0, 0); /* Write 0 to I/O ports 0 and 1 */
PEEK PEEK
PROTOTYPE:
(1) int peek(unsigned address)
(2) int peek(unsigned h_addr, unsigned l_addr)
(3) int peek(unsigned segment, unsigned offset)
ARGUMENTS:
segment - Memory segment
offset - Offset into segment
h_addr - High word of memory address
l_addr - Low word of memory address
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.
Processors such as the 8080 or 6809 which address 64K of memory or
less use form (1) of the function.
Non-segmented processors addressing more than 64K such as the
68000 use form (2) of the function.
Processors employing a "segmented" architecture such as the 8086
use form (3) of the function.
EXAMPLES:
while(peek(0)); /* Wait for flag to clear */
PEEKW PEEKW
PROTOTYPE:
(1) int peekw(unsigned address)
(2) int peekw(unsigned h_addr, unsigned l_addr)
(3) int peekw(unsigned segment, unsigned offset)
ARGUMENTS:
segment - Memory segment
offset - Offset into segment
h_addr - High word of memory address
l_addr - Low word of memory address
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).
Processors such as the 8080 or 6809 which address 64K of memory or
less use form (1) of the function.
Non-segmented processors addressing more than 64K such as the
68000 use form (2) of the function.
Processors employing a "segmented" architecture such as the 8086
use form (3) of the function.
EXAMPLES:
var = peekw(0));
POKE POKE
PROTOTYPE:
(1) poke(unsigned address, int value)
(2) poke(unsigned h_addr, unsigned l_addr, int value)
(3) poke(unsigned segment, unsigned offset, int value)
ARGUMENTS:
segment - Memory segment
offset - Offset into segment
h_addr - High word of memory address
l_addr - Low word of memory address
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.
Processors such as the 8080 or 6809 which address 64K of memory or
less use form (1) of the function.
Non-segmented processors addressing more than 64K such as the
68000 use form (2) of the function.
Processors employing a "segmented" architecture such as the 8086
use form (3) of the function.
EXAMPLES:
poke(0, 0); /* Write 0 to location 0 */
POKEW POKEW
PROTOTYPE:
(1) pokew(unsigned address, int value)
(2) pokew(unsigned h_addr, unsigned l_addr, int value)
(3) pokew(unsigned segment, unsigned offset, int value)
ARGUMENTS:
segment - Memory segment
offset - Offset into segment
h_addr - High word of memory address
l_addr - Low word of memory address
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.
Processors such as the 8080 or 6809 which address 64K of memory or
less use form (1) of the function.
Non-segmented processors addressing more than 64K such as the
68000 use form (2) of the function.
Processors employing a "segmented" architecture such as the 8086
use form (3) of the function.
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" routine performs a formatted print to the standard
output device (usually system console). The "format" string is
written 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:
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:
0 if successful, otherwise an operating system error code
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);
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 "RAND_SEED". 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 reseting the
"RAND_SEED" value.
EXAMPLES:
value = rand(52);
RENAME RENAME
PROTOTYPE:
int rename(char *pathname, char *newname)
ARGUMENTS:
pathname- Name of file to rename
newname - New name for file
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function changes the name of an existing file to the ASCII
string specified by newname.
EXAMPLES:
rename("output.dat", "output.bak");
REWIND REWIND
PROTOTYPE:
int rewind(FILE *fp)
ARGUMENTS:
fp - File pointer to an open file
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function resets the operating system internal file pointer so
than any subsequent read or writes will be at the beginning of the
file.
EXAMPLES:
rewind(input_file);
RMDIR RMDIR
PROTOTYPE:
int rmdir(char *pathname)
ARGUMENTS:
pathname- Name of directory to delete
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function removes a directory from the disk. On most systems,
the directory must be empty (contains no files) otherwise the
function will fail.
EXAMPLES:
rmdir("subdir");
SCANF SCANF
PROTOTYPE:
register int scanf(char *format, arg1, arg2, ...)
ARGUMENTS:
format - Pointer to format string
arg - Argument as determined by format string
... - Additional arguments may be required
RETURN VALUE:
The number of successful matches
EOF (-1) if end of file or an error condition occurs
DESCRIPTION:
The "scanf" function reads and scans a line from the standard
input device (usually system console) for specific values. Values are
read and assigned to the passed argument addresses according to
special "conversion characters" in the "format" string.
See "fscanf" for more information on format strings.
NOTE: This function uses a variable number of arguments, and must
be declared as "register".
EXAMPLES:
do
printf("Please enter your First & Last names, and your age?");
while(scanf("%s %s %u", first_name, last_name, &age) != 3);
SETJMP SETJMP
PROTOTYPE:
int setjmp(int savenv[3])
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" transfered
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 "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:
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 radus (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; }
}
SSCANF SSCANF
PROTOTYPE:
register int sscanf(char *input, char *format, arg1, arg2, ...)
ARGUMENTS:
input - Pointer to input string
format - Pointer to format string
arg - Argument as determined by format string
... - Additional arguments may be required
RETURN VALUE:
The number of successful matches
DESCRIPTION:
The "sscanf" function scans a string in memory for specific
values. Values are read and assigned to the passed argument addresses
according to special "conversion characters" in the "format" string.
See "fscanf" for more information on format strings.
NOTE: This function uses a variable number of arguments, and must
be declared as "register".
EXAMPLES:
sscanf(pathname,"/%s/%s", directory, filename);
STRBEG STRBEG
PROTOTYPE:
int strbeg(char *string1, char *string2)
ARGUMENTS:
string1 - Pointer to character string to test
string2 - Pointer to character string to check for
RETURN VALUE:
0 - String1 does not begin with string2
1 - String1 begins with string2
DECRIPTION:
Tests the passed "string1" to determine if it begins with the same
data as is contained within "string2".
EXAMPLES:
if(strbeg(command, "delete"))
delete_file(&command[6]);
STRCAT STRCAT
PROTOTYPE:
char *strcat(char *dest, *source)
ARGUMENTS:
dest - Pointer to destination string
source - Pointer to source string
RETURN VALUE:
Pointer to zero terminating destination string
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");
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 occurance of 'chr' in 'string'
Zero (0) if character was not found
DECRIPTION:
Searches the passed string got the first occurance 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
souce - 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
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);
STRNCAT STRNCAT
PROTOTYPE:
char *strncat(char *dest, *source, unsigned length)
ARGUMENTS:
dest - Pointer to destination string
source - Pointer to source string
length - Maximum number of characters to copy
RETURN VALUE:
Pointer to zero terminating destination string
DESCRIPTION:
This function concatinates the source string onto the tail of the
destination string. If the source string exceeds "length" bytes in
size, only that many characters are copied.
EXAMPLES:
strncat(path, filename, 64);
STRNCMP STRNCMP
PROTOTYPE:
int strncmp(char *string1, char *string2, unsigned length)
ARGUMENTS:
string1 - Pointer to first string
string2 - Pointer to second string
length - Number of bytes to compare
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 until
either a difference is detected, or "length" characters have been
compared. If the two string portions 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:
len = strlen(buffer) - 3;
for(i=1; i < len; ++i)
if(strncmp(&buffer[i], "***", 3)
abort("Found three stars\n");
STRNCPY STRNCPY
PROTOTYPE:
strncpy(char *dest, char *source, unsigned length)
ARGUMENTS:
dest - Pointer to destination string
souce - Pointer to source string
length - Number of bytes to copy
RETURN VALUE:
None
DESCRIPTION:
This function copies "length" characters from the source string to
the destination string. If the source string is shorter than
"length", the destination string is padded with nulls. If the source
string is longer than "length", only that many characters are copied,
and the destination string is NOT zero terminated.
EXAMPLES:
strncpy(filename, argv[1], 64);
STRSTR STRSTR
PROTOTYPE:
char *strstr(char *string1, char *string2)
ARGUMENTS:
string1 - Pointer to character string to test
string2 - Pointer to substring to search for
RETURN VALUE:
Pointer to substring, or 0 if not found
DECRIPTION:
Searches the passed "string1" for the first occurance of the
passed "string2". If found, a pointer to the beginning of that
substring within "string1" is returned.
EXAMPLES:
if(ptr = strstr(command, "delete"))
delete_file(&ptr[6]);
SYSTEM SYSTEM
PROTOTYPE:
int system(char *command)
ARGUMENTS:
command - A system command to be executed
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
The SYSTEM function accepts any operating system command as a
string parameter, and passes that command to the operating system to
be executed.
When the command has terminated, execution will resume in the
MICRO-C program, with the statement following the call to SYSTEM.
EXAMPLES:
system("DEL *.TMP");
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);
MICRO-C Page: 61
10.2 IBM-PC/MS-DOS Library Functions
The following functions are available only under the MS-DOS operating
system, on IBM-PC or compatible systems.
These routines perform operations which are closely tied to the 8086
family of processors, the IBM-PC hardware, or the MS-DOS operating system,
and are therefore impractical to implement on a "general" basis.
ALLOC_SEG ALLOC_SEG
PROTOTYPE:
int alloc_seg(int size)
ARGUMENTS:
size - Number of 16 byte paragraphs to allocate
RETURN VALUE:
0 - Not enough free memory available
!0 - The segment address of the allocated memory
DESCRIPTION:
The "alloc_seg" function allocates a 'segment' of memory from
DOS. The size is given in 16 byte "paragraphs". The allocated
memory will be outside of the data memory available to the MICRO-C
program, and therefore must be accessed via assembly lenguage
functions, or with the "peek" and "poke" library functions.
EXAMPLES:
if(!(aseg = alloc(4096))) /* Get a 64K data segment */
abort("Not enough memory!!!");
set_es(aseg); /* Set up extra segment */
asm_func(); /* Invoke assembler function */
CCLOSE CCLOSE
PROTOTYPE:
Cclose()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function closes the communications port previously opened
using "Copen". The system interrupt vectors and all other hooks to
the comm port are restored.
This function must be called before any program using "Copen"
terminates, otherwise the communications port will be left in an
indeterminate state. If this is not done, there will be a
possibility of system crash if an interrupt is received from the
port after the program has terminated.
EXAMPLES:
Cclose(); /* Close comm port */
exit(0); /* And terminate */
CGETC CGETC
PROTOTYPE:
int Cgetc()
ARGUMENTS:
None
RETURN VALUE:
Character read from the communications port
DESCRIPTION:
This function reads a single character from the communications
port previously opened with "Copen". If no character is available,
"Cgetc" will wait for one.
EXAMPLES:
/* Get a string from the comm port */
while((c = cgetc()) != '\r')
*ptr++ = c;
*ptr = 0;
COPEN COPEN
PROTOTYPE:
int Copen(int port, int speed, int mode, int modem)
ARGUMENTS:
port - Comm port to use (1 or 2)
speed - Baudrate divisor to set
mode - Communications parameters to set
modem - Modem control lines to set
RETURN VALUE:
0 - Successful open
!0 - Requested comm port is not available
DESCRIPTION:
The "Copen" function opens a serial communications port on the
IBM PC for access. An independant interrupt handler and I/O
drivers are installed, which allow high speed full duplex
operation of the serial port with optional XON/XOFF flow control
of both receive and transmit streams.
Only one serial port may be accessed at a time using these
functions, If "Copen" is called more than once, it will
automatically close the last port before opening the new one.
The meaning of the "speed", "mode", and "modem" parameters if
documented in the "comm.h" header file.
An external "char" variable "Cflags" may be accessed to enable
or disable transparency of the serial channel. When "transparent"
is selected, XON/XOFF flow control is disabled, and all data is
sent and received with no changes. When operating in this mode,
you must insure that "Cgetc" is called frequently enough that the
256 byte internal receive buffer will not overflow.
Since "Cflags" is used by the interrupt handler, you should
disable and enable interrupts around any accessed to it.
EXAMPLES:
#include comm.h /* Get comm port defintions */
extern char Cflags;
/*
* Program to read & echo characters on the serial port
* in transparent mode. (Until ESCAPE char is received)
*/
main()
{
char c;
if(Copen(1, _2300, PAR_NO|DATA_8|STOP_1, SET_RTS|SET_DTR))
abort("Cannot access COM1");
disable(); /* Disable interrupts */
Cflags |= TRANSPARENT; /* Set transparency */
enable(); /* Re-enable interrupts */
while((c = Cgetc()) != 0x1B) /* Do until ESCAPE */
Cputc(c);
Cclose(); /* Close the serial port */
}
COPY_SEG COPY_SEG
PROTOTYPE:
copy_seg(int dseg, int doffset, int sseg, int soffset, int size)
ARGUMENTS:
dseg - Destination segment
doffset - Destination offset
sseg - Source segment
soffset - Source offset
size - Number of bytes to copy
RETURN VALUE:
None
DESCRIPTION:
This function perform a copy between 80X86 processor memory
segments. A number of bytes equal to "size" is copied from the
source segment and offset to the destination segment and offset.
EXAMPLES:
/* Save the video display contents */
copy_seg(get_ds(), buffer, _V_BASE, 0, (25*80)*2);
CPU CPU
PROTOTYPE:
int cpu()
ARGUMENTS:
None
RETURN VALUE:
0 - CPU is 8088 or 8086
1 - CPU is 80188 or 80186
2 - CPU is 80286
3 - CPU is 80386 or 80486
DESCRIPTION:
This function returns a simple integer which identifies the
processor type on which the program is currently executing.
EXAMPLES:
if(cpu() < 2)
abort("This program requires at least an 80286");
CPUTC CPUTC
PROTOTYPE:
Cputc(char c)
ARGUMENTS:
c - Character to write to communciation port
RETURN VALUE:
None
DESCRIPTION:
The "Cputc" function writes the given character to the
communcinations port previously opened by "Copen".
EXAMPLES:
while(*ptr) /* Write a string to comm port */
Cputc(*ptr++);
CSIGNALS CSIGNALS
PROTOTYPE:
int Csignals()
ARGUMENTS:
None
RETURN VALUE:
The modem input signals read from the open comm port
DESCRIPTION:
This function reads the modem input signals (DSR, CD, RI etc)
from the serial communication port previously opened by "Copen",
and returns them as an integer value.
The meaning of the individual bits in the value returned by
"Csignals" is documented in the "comm.h" header file.
EXAMPLES:
if(!(Csignals() & DSR)) {
Cclose();
abort("Modem not ready"); }
CTESTC CTESTC
PROTOTYPE:
int Ctestc()
ARGUMENTS:
None
RETURN VALUE:
0-255 - Character read from comm port
-1 - No character available
DESCRIPTION:
This function tests for a character from the communications
port previously opened with "Copen", and returns that character if
one if found. If no character is available, "Ctestc" returns -1.
EXAMPLES:
if((c = Ctestc()) == -1) {
Cclose();
abort("No character available"); }
DISABLE DISABLE
PROTOTYPE:
disable();
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
The "disable" function disables the 8086 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,
such as timekeeping, and serial communications (Via MICRO-C serial
drivers).
EXAMPLES:
disable(); /* Disallow interrupts */
Cflags &= ~TRANSPARENT; /* Remove transparency */
enable(); /* Re-allow interrupts */
ENABLE ENABLE
PROTOTYPE:
enable();
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
The "enable" function enables the 8086 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 */
Cflags |= TRANSPARENT; /* Force transparency */
enable(); /* Re-allow interrupts */
EXEC EXEC
PROTOTYPE:
int exec(char *exefile, char *args)
ARGUMENTS:
exefile - Full pathname of a '.COM' or '.EXE' file
args - Command tail containing arguments
RETURN VALUE:
0 if successful, otherwise an MS-DOS error code
DESCRIPTION:
The "exec" function causes MS-DOS to suspend the execution of
the MICRO-C program, and then to execute the indicated '.EXE' or
'.COM' program file. When that program terminates, execution of
the MICRO-C program will resume.
This is a low level interface to the MS-DOS 'EXEC' function,
and as such, it does not search your PATH, does not process '.BAT'
files, nor provide any I/O redirection facilities. If you want to
make use of these features (which are provided by 'COMMAND.COM'),
use the higher level 'SYSTEM' function.
EXAMPLES:
printf("Type 'EXIT' to return to the MICRO-C program\n");
exec("C:\\COMMAND.COM", ""); /* Start up a sub-shell */
FREE_SEG FREE_SEG
PROTOTYPE:
int free_seg(int segment)
ARGUMENTS:
segment - A previously allocated segment of memory
RETURN VALUE:
0 - The segment was released
!0 - DOS error code
DESCRIPTION:
The "free_seg" function releases a segment of memory previously
allocated by "alloc_seg", and returns it to the operating system.
This should be used whenever your program has finished with a
segment of extra memory which it has allocated.
EXAMPLES:
aseg = alloc_seg(4096); /* Allocate a 64K segment */
set_es(aseg); /* Set up extra segment */
asm_func(); /* Call assembler function */
free_seg(aseg); /* Releas the memory */
GET_ATTR GET_ATTR
PROTOTYPE:
int get_attr(char *pathname, int &attrs)
ARGUMENTS:
pathname- Name of file to get attributes of
attrs - Integer to receive attributes
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function retreives the attributes of the specified file.
The meaning of the individual bits in the "attrs" value is
defined in the "file.h" header file.
EXAMPLES:
get_attr("tempfile", &attributes);
GET_CS GET_CS
PROTOTYPE:
int get_cs()
ARGUMENTS:
None
RETURN VALUE:
The current processor CODE segment
DESCRIPTION:
This function is available only on the 8086 family of
processors, and returns the current processor CODE segment.
EXAMPLES:
code_seg = get_cs();
GET_DATE GET_DATE
PROTOTYPE:
get_date(int &day, int &month, int &year)
ARGUMENTS:
&day - Address of integer to receive day (1-31)
&month - Address of integer to receive month (1-12)
&year - Address of integer to receive year (1980-2099)
RETURN VALUE:
None
DESCRIPTION:
This function gets the current system date in day, month, and
year.
EXAMPLES:
get_date(&day, &month, &year);
printf("%s %u, %u", months[month], day, year);
GET_DRIVE GET_DRIVE
PROTOTYPE:
int get_drive()
ARGUMENTS:
None
RETURN VALUE:
The currently active (default) disk drive (0=A, 1=B, 2=C, ...)
DESCRIPTION:
The "get_drive" function returns the drive index (0-n) of the
currently active or "default" MS-DOS disk drive.
EXAMPLES:
old_drive = get_drive();
set_drive(new_drive);
GET_DS GET_DS
PROTOTYPE:
int get_ds()
ARGUMENTS:
None
RETURN VALUE:
The current processor DATA segment
DESCRIPTION:
This function is available only on the 8086 family of
processors, and returns the current processor DATA segment.
EXAMPLES:
data_seg = get_ds();
GET_ES GET_ES
PROTOTYPE:
int get_es()
ARGUMENTS:
None
RETURN VALUE:
The current processor EXTRA segment
DESCRIPTION:
This function is available only on the 8086 family of
processors, and returns the current processor EXTRA segment.
EXAMPLES:
code_seg = get_es();
GET_TIME GET_TIME
PROTOTYPE:
get_time(int &hour, int &minite, int &second)
ARGUMENTS:
&hour - Address of integer to receive hour (0-23)
&minite - Address of integer to receive minite (0-59)
&second - Address of integer to receive second (0-59)
RETURN VALUE:
None
DESCRIPTION:
This function gets the current system time in hours, minites
and seconds.
EXAMPLES:
get_time(&hour, &minite, &second);
printf("%02:%02:%02", hour, minite, second);
RESIZE_SEG RESIZE_SEG
PROTOTYPE:
int resize_seg(int segment, int size)
ARGUMENTS:
segment - A previously allocated segment of memory
size - Desired size (in 16 byte paragraphs)
RETURN VALUE:
0 - The segments size has been adjusted
!0 - DOS error code
DESCRIPTION:
The "resize_seg" function provides a method of changing the
size of a segment of memory previously allocated via "alloc_seg".
If not enough memory is available, the function will fail with a
ono-zero return value.
This function may also be used to adjust the memoey allocation
of the programs image, by passing it the segment address of the
programs own PSP. This value is available in the external variable
"PSP".
MICRO-C normally allocates 64K for programs compiled in the
TINY model, and (64K + (256 byte PSP) + (size of executable code))
for programs compiled in the SMALL model. This allocation should
NEVER be reduced, however you may enlarge it if your program
wishes to access additional data immediately following its own
DATA/STACK segment.
EXAMPLES:
extern int PSP;
main()
{
if(resize_seg(PSP, 8192)) /* Append 64K buffer */
abort("Not enough memory!!!");
... /* Remainder of program */
}
RESTORE_VIDEO RESTORE_VIDEO
PROTOTYPE:
restore_video(char buffer[4006]);
ARGUMENTS:
buffer - Video save area.
RETURN VALUE:
None
DESCRIPTION:
The RESTORE_VIDEO function restores the contents and state of
the IBM P.C. video display to the state it was in when
"SAVE_VIDEO" was executed. At the present time, the function is
effective only for text modes. Graphics screens will not be
restored, due to the high memory requirements.
Although this function is useful in ANY program (to restore the
DOS screen before termination), it is most useful in "POP-UP" ram
resident programs (See "TSR" function), to save the screen of any
application which may be running when you pop up.
The video state is restored from the passed "buffer" argument,
which is 4006 bytes in size, and must have been filled in by
"SAVE_VIDEO". It has the following format:
buffer[0] - Video mode
" [1] - Video page
" [2] - 'X' cursor position
" [3] - 'Y' cursor position
" [4] - Ending line for cursor
" [5] - Starting line for cursor
" [6-4005] - Saved video screen contents
EXAMPLES:
popup_func()
{
save_video();
perform_function();
restore_video();
}
SAVE_VIDEO SAVE_VIDEO
PROTOTYPE:
save_video(char buffer[4006]);
ARGUMENTS:
buffer - Video save area.
RETURN VALUE:
None
DESCRIPTION:
The SAVE_VIDEO function saves the current contents and state of
the IBM P.C. video display. The screen may be restored at any time
using "RESTORE_VIDEO". At the present time, the function is
effective only for text modes. Graphics screens will not be saved,
due to the high memory requirements.
Although this function is useful in ANY program (to restore the
DOS screen before termination), it is most useful in "POP-UP" ram
resident programs (See "TSR" function), to save the screen of any
application which may be running when you pop up.
The video state is saved in the passed "buffer" argument, which
is 4006 bytes in size, and has the following format:
buffer[0] - Video mode
" [1] - Video page
" [2] - 'X' cursor position
" [3] - 'Y' cursor position
" [4] - Ending line for cursor
" [5] - Starting line for cursor
" [6-4005] - Saved video screen contents
EXAMPLES:
popup_func()
{
save_video();
perform_function();
restore_video();
}
SET_ATTR SET_ATTR
PROTOTYPE:
int set_attr(char *pathname, int attrs)
ARGUMENTS:
pathname- Name of file to get attributes of
attrs - New attributes
RETURN VALUE:
0 if successful, otherwise an operating system error code
DESCRIPTION:
This function sets the system attributes of the specified file.
The meaning of the individual bits in the "attrs" value is
defined in the "file.h" header file.
EXAMPLES:
set_attr("tempfile", READONLY|HIDDEN);
SET_DATE SET_DATE
PROTOTYPE:
set_date(int day, int month, int year)
ARGUMENTS:
day - New day (1-31)
month - New month (1-12)
year - New year (1980-2099)
RETURN VALUE:
0 - Success
-1 - Invalid date given
DESCRIPTION:
This function sets the current system date to day, month, and
year.
EXAMPLES:
set_date(1, 1, 1980); /* Set to Jan 1, 1980 */
SET_DRIVE SET_DRIVE
PROTOTYPE:
int set_drive(int drive)
ARGUMENTS:
Drive - New drive index (0=A, 1=B, 2=C ...)
RETURN VALUE:
The total number of "logical" disk drives in the system
DESCRIPTION:
The "set_drive" function sets the disk drive indicated by the
"drive" index (0-x) to be the currently active or "default" MS-DOS
disk drive.
EXAMPLES:
old_drive = get_drive();
set_drive(new_drive);
SET_ES SET_ES
PROTOTYPE:
int set_es(int segment)
ARGUMENTS:
segment - The 16 bit new segment value
RETURN VALUE:
None
DESCRIPTION:
This function is available only on the 8086 family of
processors, and sets the processors EXTRA segment to the indicated
value.
EXAMPLES:
set_es(get_ds()); /* Copy DATA to EXTRA segments */
SET_TIME SET_TIME
PROTOTYPE:
set_time(int hour, int minite, int second)
ARGUMENTS:
hour - New hour (0-23)
minite - New minite (0-59)
second - New second (0-59)
RETURN VALUE:
0 - Success
-1 - Invalid time given
DESCRIPTION:
This function sets the current system time to "hour", "minite"
and "second".
EXAMPLES:
set_time(0, 0, 0); /* Set to 00:00:00 (midnight) */
TSR TSR
PROTOTYPE:
tsr(&func, int hotkey, int alloc)
ARGUMENTS:
func - Address of function to execute
hotkey - POP-UP activation hotkeys
alloc - Memory allocation
RETURN VALUE:
Function normally never returns, but if it does, an
operating system error code is passed back.
DESCRIPTION:
The TSR function terminates the MICRO-C program, but leaves it
resident in memory. When the specified "HOT KEYS" are detected on
the IBM PC keyboard, the context of whatever program is running is
saved, and the specified MICRO-C function is called. When that
function returns, the interrupted program is resumed.
When activated this way, THE "func" FUNCTION MUST NOT TERMINATE
WITH "exit" or one of its related functions (abort etc.). THE ONLY
WAY IT MAY TERMINATE IS TO "return" NORMALLY.
The meaning of "hotkey" is defined in the "tsr.h" header file.
The "alloc" paremeter specifies how much extra memory is to be
retained in the TSR image for use by the MICRO-C stack and heap
memory allocation functions. The "func" function(s) must insure
that the total amount of memory used by the stack and calls to
"malloc" does not exceed this value.
NOTE: "malloc" will fail if the heap grows to within 1K of the
stack pointer.
All memory used by code, global variables, string space, and
"malloc" calls prior to the use of "tsr" is automatically
retained, and should not be included in the "alloc" value.
TSR programs which perform screen I/O should take care to save
and restore the screen contents when popping up and down.
EXAMPLES:
tsr(&popup_func, ALT+L_SHIFT, 1024);
VCLEAR_BOX VCLEAR_BOX
PROTOTYPE:
vclear_box(int x, int y, int w, int h)
ARGUMENTS:
x - COLUMN of top left corner of box
y - ROW of top left corner of box
w - Width of box (columns)
h - Height of box (rows)
RETURN VALUE:
None
DESCRIPTION:
This function clears a box of the specified height and width,
at the indicated 'X' and 'Y' coordinates, on the IBM-PC video
screen using the block graphics characters. The entire box is
cleared to spaces.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vclear_box(21, 11, 8, 3); /* Clear a BOX */
VCLEOL VCLEOL
PROTOTYPE:
vcleol()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function clears the IBM PC video screen from the current
cursor position to the end of a line.
You must "#include video.h", and execute "VOPEN" prior to using
this function.
EXAMPLES:
vprintf("Input> "); /* Display a prompt */
vcleol(); /* Clear remainder of input line */
VCLEOS VCLEOS
PROTOTYPE:
vcleos()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function clears the IBM PC video screen from the current
cursor position to the end of a screen.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vgotoxy(0, 10); /* position at line 11 */
vcleos(); /* Clear lower part of screen */
VCLSCR VCLSCR
PROTOTYPE:
vclscr()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function clears the entire IBM PC video screen and resets
the cursor position to the top left hand corner.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
if(c = 0x1b) { /* Escape command */
vclscr(); /* Clear the screen */
vprintf("%s has terminated\n", argv[0]);
exit(-1); }
VCURSOR_BLOCK VCURSOR_BLOCK
PROTOTYPE:
vcursor_block()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function enables (turns on) display of the cursor on the
IBM PC video display. The cursor is shown as flashing block,
occupying the entire character window.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
if(insert) /* Test insert mode flag */
vcursor_block(); /* Indicate inserting with block cursor */
else
vcursor_line(); /* Indicate overwrite with line cursor */
VCURSOR_LINE VCURSOR_LINE
PROTOTYPE:
vcursor_line()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function enables (turns on) display of the cursor on the
IBM PC video display. The cursor is shown as a single flashing
line, at the bottom of the character window.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vcursor_line(); /* Re-enable the cursor */
exit(0); /* And terminate */
VCURSOR_OFF VCURSOR_OFF
PROTOTYPE:
vcursor_off()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function inhibits (turns off) display of the cursor on the
IBM PC video display. This affects the cursor display only, screen
output will continue to be displayed at the correct cursor
position.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vclscr(); /* Clear screen */
vcursor_off(); /* Inhibit cursor */
vmenu(10, 10, main_menu, 0, &index); /* Present main menu */
VDRAW_BOX VDRAW_BOX
PROTOTYPE:
vdraw_box(int x, int y, int w, int h)
ARGUMENTS:
x - COLUMN of top left corner of box
y - ROW of top left corner of box
w - Width of box (columns)
h - Height of box (rows)
RETURN VALUE:
None
DESCRIPTION:
This function draws a box of the specified height and width, at
the indicated 'X' and 'Y' coordinates, on the IBM-PC video screen
using the block graphics characters.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vdraw_box(20, 10, 10, 5); /* Draw a BOX */
VERSION VERSION
PROTOTYPE:
int version()
ARGUMENTS:
None
RETURN VALUE:
MS-DOS operating system version number
DESCRIPTION:
The "version" function is available only under MS-DOS, and
returns the version number of the operating system.
The higher 8 bits of the returned value indicates the MAJOR
version number ('3' in DOS 3.1)
The lower 8 bits of the returned value indicates the MINOR
version nuber ('1' in DOS 3.1).
EXAMPLES:
if(version() < 0x031E)
abort("Requires DOS 3.30 or higher\n");
VGETC VGETC
PROTOTYPE:
int vgetc()
ARGUMENTS:
None
RETURN VALUE:
0-127 - ASCII value of key pressed
< 0 - Special function key as defined in "video.h"
DESCRIPTION:
The "vgetc" function waits until a key is pressed on the system
console, and returns its value.
Note that due to the buffering of the IBM-PC keyboard, every
keypress will be reported, even if the VGETC function is called
after a key is pressed and released.
EXAMPLES:
switch(vgetc()) { /* Handle input keys
. . .
}
VGETS VGETS
PROTOTYPE:
int vgets(int x, int y, char *prompt, char *field, int width)
ARGUMENTS:
x - COLUMN of top left corner of input box
y - ROW of top left corner of input box
prompt - String to prompt with
field - String to receive the data
width - Width in characters of input field
RETURN VALUE:
0 - Selection was made and ENTER pressed.
!0 - Input was aborted via ESCAPE key.
DESCRIPTION:
The "vgets" function draws a box on the video screen at the
specified X and Y coordinates, then prompts for and receives an
input string in the box. The box is drawn large enough to contain
the prompt and the specified width of input field. The prompt is
displayed at the left hand side of the box, and the cursor is
positioned immediatly following it, at the start of the input
field.
The "field" parameter is the address of a character array to
receive the input string. The previous value of the "field" array
is inserted into the input box when VGETS is invoked. If you do
not want to display an old value, you should set the first
character pointed to by field to zero, before calling VGETS.
When entering the string, the user may use the following
special keys to edit the input field:
Right Arrow - Move forward one character
Left Arrow - Move backward one character
Delete - Delete the character under the cursor
Backspace - Move backward one character and delete.
Home - Move to beginning of string
End - Move to end of string
PgUp - Clear (erase) entire string
PgDn - Clear from cursor to end of string
Enter - Accept (enter) the string
ESC - Abort the input request
All data entered in the input box is inserted into any data
which is already present.
EXAMPLES:
name[0] = 0;
if(vgets(10, 10, "Your name?", name, 30))
return; /* Aborted, exit to higher level */
VGOTOXY VGOTOXY
PROTOTYPE:
vgotoxy(int x, int y)
ARGUMENTS:
x - New COLUMN (0-79)
y - New ROW (0-24)
RETURN VALUE:
None
DESCRIPTION:
The "vgotoxy" function positions the cursor on the IBM-PC video
screen. Any further display output will occur at the new ROW and
COLUMN on the screen.
The extern "int" variable "V_XY" may be referenced to read or
set the current X/Y position. The higher 8 bits contain the 'Y'
coordinate, and the lower 8 bits contain the 'X' coordinate. If
this variable is used to set (restore) the cursor position,
"vupdatexy" must then be called to position the physical cursor.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
for(i=0; i<24; ++i) { /* Draw a diagonal line of 'X's */
vgotoxy(i, i);
vputc('X'); }
VMENU VMENU
PROTOTYPE:
vmenu(int x, int y, char *names[], char erase, int &index)
ARGUMENTS:
x - COLUMN of top left corner of menu box
y - ROW of top left corner of menu box
names - Array to menu selection text (last entry = 0)
erase - 1=Erase BOX after selection is made
index - Address of message selection index variable
RETURN VALUE:
0 - Selection was made and ENTER pressed.
!0 - Menu was aborted via ESCAPE key.
DESCRIPTION:
The "vmenu" function displays a list of menu items enclosed in
a box on the IBM-PC video screen at the specified ROW and COLUMN
address. The user may use the UP, DOWN, HOME and END keys to
select an entry by moving the INVERSE VIDEO selection cursor.
When the desired selection is under the cursor, the selection
is made by pressing the ENTER key.
At any time the menu selection may be canceled by pressing the
ESCAPE key.
The "names" argument must be a pointer to an array of character
strings which are the selections to display. This array MUST end
with a zero (0) element to indicate the end of the list.
The "erase" flag indicates that the menu box should be cleared
to blanks when the selection is made. If "erase=0", the menu box
is left on the screen, WITHOUT the selection cursor, with the
selected entry marked by '>' and '<'. The menu box is always
erased when the menu is aborted with the ESCAPE key.
The "index" argument is the address of an "int" variable which
contains the position of the selection cursor. It controls where
the selection cursor will appear when the function is first
invoked (0 = first entry), and also is assigned the position of
the selection cursor when the selection is made.
EXAMPLES:
char *names[] = { "One", "Two", "Three", "Four", "Five", 0 };
. . .
index = 0;
if(vmenu(10, 10, names, 0, &index))
return; /* Aborted, exit to higher level */
switch(index) { /* Handle selection */
. . .
}
VMESSAGE VMESSAGE
PROTOTYPE:
vmessage(int x, int y, char *string)
ARGUMENTS:
x - COLUMN of top left corner of message box
y - ROW of top left corner of message box
string - Message to display
RETURN VALUE:
None
DESCRIPTION:
The "vmessage" function displays a text string surrounded by a
BOX, on the IBM-PC video screen, at the specified ROW and COLUMN
address.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vmessage(20, 20, "Press any KEY to continue");
get_key();
vclear_box(20, 20, 26, 2);
VOPEN VOPEN
PROTOTYPE:
vopen()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function initializes the IBM-PC video display adapter for
use with the MICRO-C video library functions. It determines the
adapter type (COLOR ot MONOCHROME), sets up internal variables
with information required by the other video functions, and clears
the video screen.
This function MUST be called before any of the other video
functions in the library are used.
After "vopen" is called, the extern "int" variable "V_BASE" may
be referenced to determine the memory segment of the video display
(B000 for monochrome, B800 for color).
Any program using the video library functions must include the
"video.h" header file.
EXAMPLES:
vopen();
VPRINTF VPRINTF
PROTOTYPE:
register vprintf(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:
This function performs exactly as the "PRINTF" function in the
standard function library, except that it outputs directly to the
video screen using the video interface library routines.
This function should be used in preference to "PRINTF" when
using the video function library since "PRINTF" will not move the
video librarys cursor.
NOTE: This function uses a variable number of arguments, and
must be declared as "register" (See "video.h").
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vgotoxy(0, 0);
vprintf("Screen %u", screen);
VPUTC VPUTC
PROTOTYPE:
vputc(char chr)
ARGUMENTS:
chr - Character to display
RETURN VALUE:
None
DESCRIPTION:
This function displays a character on the video screen at the
current cursor position.
Characters are output in "tty" fashion, with proper handling of
control codes such as CARRIAGE-RETURN, LINE-FEED and BELL. The
screen will scroll upwards when a NEWLINE is printed on the bottom
line of the screen, or when the bottom line wraps around to the
next.
Although only the lower 8 bits of a passed value are used,
"vputc" will not perform ANY output translations if any of the
upper 8 bits are set. This provides a method of displaying the
video characters represented by control codes such as NEWLINE, and
BACKSPACE.
The external "char" variable "V_ATTR" may be used to set the
video attribute used by "VPUTC" to display the character. This
value is written to the attribute location associated with the
character on the video display hardware. Its effect is dependant
on the video adapter in use. The "video.h" header file contains
definitions of the attribute bits for use on "standard" monochrome
and color displays.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vputc(0x0A); /* Line-feed, advance cursor */
vputc(0x0A | 0xff00); /* Display 0x0A character code */
VPUTF VPUTF
PROTOTYPE:
vputf(char *string, int width)
ARGUMENTS:
string - Pointer to character string
width - Width of output field
RETURN VALUE:
None
DESCRIPTION:
The "vputf" function outputs a character string to the IBM-PC
video screen using the video library functions.
The string is left justified in a field of the specified width.
If the string is shorter than "width", the field is padded with
blanks. If the string is longer than "width", the output is
truncated.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vputf(message, 10);
VPUTS VPUTS
PROTOTYPE:
vputs(char *string)
ARGUMENTS:
string - Pointer to character string
RETURN VALUE:
None
DESCRIPTION:
The "vputs" function outputs a character string to the IBM-PC
video screen using the video library functions.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vputs(message);
VTSTC VTSTC
PROTOTYPE:
int vtstc()
ARGUMENTS:
None
RETURN VALUE:
0 - No key pressed
1-127 - ASCII value of key pressed
< 0 - Special function key as defined in "video.h"
DESCRIPTION:
The "vtstc" function tests for a key pressed on the system
console, and returns its value. A returned value of zero (0)
indicates that no key was found to be pressed.
Note that due to the buffering of the IBM-PC keyboard, every
keypress will be reported, even if the VTSTC function is called
after a key is pressed and released.
EXAMPLES:
if(vtstc() == 0x1B) /* exit loop on ESCAPE key */
break;
VUPDATEXY VUPDATEXY
PROTOTYPE:
vupdatexy()
ARGUMENTS:
None
RETURN VALUE:
None
DESCRIPTION:
This function updates the real X/Y cursor position on the video
screen to reflect the "logical" position where the next character
will be output.
The MICRO-C video library uses a BIOS interrupt (INT 10) to
position the cursor, which is quite slow, compared to the speed of
the library video routines. To prevent this from slowing down the
video output, the cursor is only physically re-positioned when a
"vgotoxy" or a "vgetc" is executed.
This allows the library routines to run at full speed, and
still put the cursor in the right place when output stops and an
input request is made.
A side effect of this is that the cursor on the screen will not
appear to move unless you call "vgotoxy" or "vgetc". This only
affects the physical cursor on the screen, MICRO-C maintains its
own internal cursor location which it uses to determine where on
the screen the next write will occur.
Some applications which run in real time (Such as a terminal
emulator) do not call "vgetc", but use "vtstc" to poll the
keyboard on a regular basis. In this case, the "vupdatexy" routine
should be called any time that the visual position of the cursor
is important.
"VOPEN" MUST be called prior to using this function.
EXAMPLES:
vupdatexy(); /* position the cursor *
c = vtstc(); /* Test for a character */
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
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 Statements 8
2.6 Expression Operators 10
2.7 Preprocessor Commands 12
2.8 Error Messages 13
2.9 Quirks 17
3. ADVANCED TOPICS 20
3.1 Conversion Rules 20
3.2 Assembly Language Interface 21
3.3 Compiling for ROM 24
4. PORTING THE COMPILER 25
4.1 The "io" module 26
4.2 The "code" module 28
4.3 The "compile" module 33
4.4 Porting without a compiler 34
4.5 Porting without a linker 34
4.6 Optimization Techniques 35
5. THE MICRO-C PREPROCESSOR 38
5.1 The MCP command 39
5.2 Preprocesor Commands 40
5.3 Error messages 43
6. THE MICRO-C OPTIMIZER 45
6.1 The MCO command 45
6.2 Porting to a new processor 46
7. THE COMMAND CO-ORDINATOR 47
7.1 The CC command 47
MICRO-C Table of Contents
Page
7.2 Using multiple object modules 48
8. THE MAKE UTILITY 49
8.1 MAKEfiles 49
8.2 Directives 52
8.3 The MAKE command 53
8.4 The TOUCH command 54
9. THE SOURCE LINKER 55
9.1 The SLINK Command 55
9.2 The External Index File 56
9.3 Multiple source files 57
9.4 Source file information 57
9.5 The SINDEX utility 59
10. LIBRARY FUNCTIONS 60
10.1 Standard Library Functions 60
10.2 IBM-PC/MS-DOS Library Functions 61