
MICRO-C Compatibility Notes

This file was written by Steffen Kaiser on or about October 1997.
Steffen has developed this doucment for the benefit of helping people
familiar with more "standard" C Compilers to adjust to MICRO-C programming.
I have made some minor edits. Morgan Toal, April 1998.

------------------------------------------------------------------------

Introduction

Hi,

This document is dedicated to people, who want to start coding with
Micro-C, but already know a commercial C compiler. This document was
written by someone, who implemented code compilable by Micro-C and
Turbo/Borland-C, and compilable by Micro-C only. (Hey, that _someone_ is
me!)

FreeDOS contains (at least) three packages compilable by Micro-C & Borland
C:

   * RM
   * LIB
   * SUPPL.

What Micro-C misses (see MC.TXT, section 1.2.1pp):

   * C types: (long), any (float), enum, typedef, bit fields.
   * Semi-standard of DOS C compilers.
   * Debugging support.
   * Far pointers.
   * Standardized DOSish functions.

As I will point out below, I've coded & still code with Micro-C, but I'll
not write any FAQ; perhaps I'll answer some questions :)

  ------------------------------------------------------------------------

My own opinions about Micro-C

Micro-C was developed to be portable for several chips, architectures and
environments; for Micro-C there is no need to be a complete DOS-ish C
compiler. However, one can easely hack-in code, compile, and run. I use
Micro-C for all programs "when I can hack-in the code without much fear
that something goes wrong". That mostly limits the code in its size, both
size of source code and the code/data segment.

The point that Micro-C is a multi-pass compiler separated into several
programs might look archaic, but I never use any IDE/workbench or whatever,
but prefer the good ol' vi (in an improved modern version :) in conjunction
with Makefiles. Because Micro-C does not support debugging (at least I
didn't find anything), I'd patched the supplied CC.C to pass debugging
options to TASM & TLINK and to capture any error text into a file. Now I
can debug the assembly source that contains the C statements as comment.
Not comparable to the built-in debugging of IDEs, but it does its job.

I also patched CC to always define the _MICORC_ macro on command line.
Well, strictly speaking, preprocessing is also always enabled.

Micro-C sees the memory as a flat one. Looking at K&R and ANSI
specification that's conform. Though, if the Intel CPU is in real mode,that
means that only 64KB are directly accessable. I don't know, if there is a
Micro-C variant supporting flat memory in conjunction with a PD DOS
extender. For the programmer this view has two restrictions:

  1. Tiny & small memory models only.
  2. No far pointers.

One will note that Micro-C can put more code into a small model program
than Borland C, because Micro-C's standard CLibrary is hand-made, written
in assembly and because of Micro-C's "lack" of several DOS-ish
semi-standard functionality and the lack of some ANSI requirements. This
makes the "overhead" of the code much smaller than with commercial
compilers. Though, this advantage will drop if the program becomes larger,
because the code optimizer of commercial compilers is better than
Micro-C's.

Micro-C has no far pointers, but one can emulate them with the peek() and
poke() functions, Micro-C also has functions for the DOS API allocate
andfree DOS memory. Unfortunately, it's pain to emulate far pointer
_variables_, because Micro-C supports only _one_ data type: (int); 2 bytes.
All other types are variants of that type. Far pointers would require 4
bytes.

Micro-C does not incorporate floating point arethemtic, as it would also
require a type > 2 bytes. This also applies to (long). One can use the
supplied longmath functions (longadd(), longmul() ...) for dealing with 4
byte numbers, but this is not the same as (long) in Borland C.

The miss of typedef, enums and bit fields, many programmers can live with
that. (NO, no discussion "typedef vs. macros"!)

Take a glance at MC.TXT. Run over all the paragraphes, some useful
information is surrounded by basic C knowledge, because this manual is not
dedicated to the experienced programmer, foremost.

  ------------------------------------------------------------------------

What I found disturbing and worth noting when I coded sources compilable by
Micro-C and Borland C

Because I started with Borland C to code, most of the following facts will
start with "Micro-C does not". This means not necessarily something bad,
but non-standard and unexpecting to me. Some things I bring up here might
be removed with newer Micro-C version than 3.14.

  1. Note that the _MICROC_ macro is defined in <stdio.h>. It is neither a
     standard macro nor a default definition of CC.COM.
  2. CC.COM does not call the external (& more powerful, more standard)
     preprocessor by default, you'll need to pass the "-p" switch.
  3. Micro-C cannot compile more than one source at once, CC.COM does not
     recognize the "-c" switch as "compile but not link".
  4. Micro-C's header files don't follow the standards. Unless the program
     is very simple you'll need to #ifdef the include section of a source
     for the standard include files.
  5. Micro-C does not pass the "environ" parameter to main(); well, a
     highly unportable thing anyway.
  6. Micro-C's startup code looks like:

     /* breaking command line */
     main(argc, argv);
     exit(0);

     Micro-C will ignore any value returned by "return" from main().
     Micro-C does not know EXIT_SUCCESS or EXIT_FAILURE.
  7. Micro-C does not flush FILE* pointers with exit().
  8. Micro-C does not support O_TEXT with handle-based IO. open() is
     non-standard.
  9. Micro-C's internal structure of FILE differs from standard, that
     requires that setbuf() returns a new FILE* pointer. stdin and stdout
     are unbuffered by default.
 10. Structures, e.g. struct { int a,b,c; }, are internally interpreted as
     arrays of (char), e.g.: char [6]. As long as one accesses structures
     as arrays this don't bother, but pointers to structures are pointers
     to (char)! See & understand MC.TXT section 3.5.1 & 3.11.

     Micro-C does not allow you to take an address of structures, e.g.

     [non-standard]:
     struct TYPE s;
     fct(&s); /* error */
     fct(s); /* correct */
 11. You cannot use sizeof() within variable declarations.
 12. Initializing static arrays differs, see MC.TXT section 3.5.1.
 13. Micro-C passes inline assembly directly to the assembler. It does
     neither replace variable names. That means that any valid assembly
     instruction (label, DB, mnemodic) can be typed there.
 14. The logical operators '&&' and '||' return the value that stopped the
     evaluation process, e.g.: 'a || b' with a != 0, will result in a; a =
     = 0 in b.
 15. For some (unknown) reason I had problems with complex conditions of
     '?:' from time to time; I recommend to always parenthize the
     condition.
 16. printf() buffers the text into a local array of (char). This array is
     130  2 bytes. If this buffer overflows, the output will be corrupted
     (see the help screen of RM ;) or the program crashes.
 17. abort() is non-standard.
 18. Micro-C don't recognize some special characters if upper-cased.
 19. See the index of LIBRARY.TXT to see the available functions. Also note
     the different names and arguments compared to DOS-ish C compilers.
     Also read the header files to gain the knowledge about different names
     of DOS-ish structures and their members. Also note the difference in
     the argument list and the availability of standard macros.
 20. Passing variables to functions is implemented different from Borland
     C! NEVER CALL SUCH A FUNCTION WITHOUT PROTOTYPE! e.g. printf() without
     <stdio.h>!
 21. Micro-C evaluates & passes arguments from left to the right. That's no
     problem unless one tries to use arguments from within inline assembly
     or one codes a function with dynamic argument list.

     Accessing arguments from inline assembly:
     2*#[BP], where '#' is the number of the argument counting from right
     to the left, starting with one (1).

     Functions with dynamic argument list MUST BE prototyped and declared
     with the keyword "register", e.g.: register error(char *fmt);

     Then Micro-C will pass the number of arguments to the function, to
     gain a pointer to the first argument you need:

     register error(int fmt)
     {
     unsigned *p;
     p = nargs() * 2 + &fmt;
     p[0] /* first argument */
     p[1] /* 2nd argument */
     /* etc. */
     }

     Note: "nargs()" must be the very first expression!

     Micro-C does not know of <vararg.h> or <stdarg.h>.
 22. Micro-C does not change the DTA by default. So don't invoke any
     function that uses the the DTA before you've read all the command line
     arguments. The find_first() and find_next()functions use the DTA for
     instance (underscore!).
 23. Calling functions by pointers always results in errors in my code.
     This will do its job:

     {
     int pfct;
     pfct = <expression to get the address of a function>;
     pfct(<arguments>);
     }

     To create a pointer of a function you need the ampersand '&'
     (non-standard):

     fct() {}

     fct1(fct); /* error */
     fct1(&fct); /* correct */

  ------------------------------------------------------------------------

Bugs I encountered:

   * Never use "rw" filemode with fopen().
   * realloc() might destroy data (rare).

To overcome the differences between Micro-C & Borland C I started to create
a "portable.h" file to help. SUPPL also contains a far memory concept that
uses the segm:ofs pair to address far memory, but uses the far pointers to
access the memory with Borland C. Well, as the examples LIB & SUPPL show I
didn't found the universal portable header to conform Micro-C and Borland
C. The header files were created while coding, so they will contain unused
definitions.

  ------------------------------------------------------------------------
[ Top | Index | Programs | Concepts | Tutorial ]
