Recently, one of the projects for my paid employment changed micro-controllers, to an R3000 from Rabbit Semiconductor. The processors are Z80 based, although peripheral interface appears to be a little better than the eZ80 from Zilog we tried earlier.
One of the attractors for the rabbit system is that it comes with a large library of software, including TCP/IP networking, out of the box. Unfortunately, these libraries are written in ‘Dynamic C’, not ‘C’. Dynamic C is related to C in a similar way as C++ is to C. In some places it is a super set – there is support for co-processing, function chaining, and the usual embedded system memory mapping extensions. In other places it is a subset – there are no static variables (you have to use the function chaining to work around it) and indeed all initial values cause an implicit ‘const,’ so in many cases you can’t use them. In some cases it is just plain different – only the most trivial C programs would compile under Dynamic C.
The key difference here is that Dynamic C doesn’t support ‘#include’ It would be a trivial program indeed that didn’t at least use some of the standard library. Instead it supplies ‘#use’, and all of the system libraries are .LIB files, which interleave the information that would normally be in .h and .c files using special comment fields. This has several purposes – the declaration (or header) information is in the same file and the same place as the related code, only functions (or other code sections) that are actually used are complied (*subject to manual identification of which symbols cause the compilation of which sections), and the simple fact that it doesn’t support separate compilation – the apparent effect is as if all of the referenced code was text-included in the file, and indeed everything gets compiled every time.
By itself Dynamic C might be a nice environment, but choosing to program in ‘full’ Dynamic C would be tantamount to choosing to program CAML – it won’t compile in anything else. It would probably work especially well for small hobbyist projects doing home automation or something, where you need all the libraries, but the program itself isn’t very large.
However, we changed micro-controllers from an ARM (no manufacturer support and very expensive tools) and had an existing, working, C code base. It had run under linux originally, but platform dependent features were isolated to small number of files. Re-writing from scratch wasn’t very attractive. This, then is a guide to the changes needed to make C code run under under Dynamic C with the least effort (and damage to the possibility of going back to straight C)
- Wrap header files in /*** BeginHeader */ and /*** EndHeader */
- Somewhere in the .h file (I put it near the end), add #use “file.c”. Make sure to either remove the reference, in eacn .c file, to it’s own .h file, or leave that include statement before the BeginHeader. An alternate strategy is to change all the other use statements to reference the .c file.
- Add an empty /*** BeginHeader somesymbol */ /***EndHeader */ to the start of each ‘c’ files. Name one symbol that you know will be referenced some place else.
- Replace #include by #use. If cross compilation is necessary, #ifdef can be used to have two header blocks (but I didn’t want to maintain this) As a small exception, any statements prior to the first BeginHeader are ignored by the Dynamic C compiler
- In the compiler settings, #define ‘signed’ as nothing. Dynamic C supports only ‘unsigned’ as a keyword, with signed values assumed otherwise.
- Pointers to const are not supported. I defined CONSTP as nothing to work around this (so that it could be defined as ‘const’ in a different environment)
- Argument typing is not supported for function pointers. I already typedef function pointers as a matter of course for easier maintenance. Put ()/* */ around the arguments so the information is still available.
- Remove declaration initialization of variables. Most local function variables can simple be initialized in a statement. The program should have a startup area to initialize global. For other cases, or static function variables, the GLOBAL_INIT function chain can be used if necessary.
- The Rabbit microcontollers are 8-bit, with 16-bit registers. Convert 32-bit switch statements to if-else chains.
- Make a copy of the file ‘lib.dir’ in your project directory. Add all your files to it, with the full path. In the project settings, tell it to use your lib.dir
- In the project settings, set a default .c file. Otherwise it tries to compile the active window as the main program.
I may extend this list as I remember or discover things.