The first problem for any embedded design is that of run-time libraries. These provide the full range of functions that the high level language offers and can be split into several different types, depending on the functionality that they offer and the hardware that they use. The problem is that with no such thing as an embedded design, they often require some modification to get them to work.
The bulk of a typical high level language library simply requires the processor and memory to execute. Mathematical functions, string manipulation, and so on, all use the processor and do not need to communicate with terminals, disk control-lers and other peripherals. As a result these libraries normally require no modification. There are some exceptions concern-ing floating point and instruction sets. Some processors, such as the MC68020 and MC68030, can use an optional floating point co-processor while others, such as the MC68000 and MC68010, cannot. Further complications can arise between processor variants such as the MC68040 family where some have on-chip floating point, while others do not. Running floating point instructions without the hardware support can generate unexpected processor exceptions and cause the sys-tem to crash. Instruction sets can also vary, with the later generations of M68000 processors adding new codes to their predecessor’s instruction set. To overcome these differences, compilers often have software switches which can be set to select the appropriate run-time to match the processor configu-ration.
If a program does not need any I/O at all, it is very easy to move from one machine to another. However, as soon as any I/O is needed, this immediately defines the hardware that the software needs to access. Using a printf statement calls the printf routine from the appropriate library which, in turn, either drives the hardware directly or calls the operating system to perform the task of printing data to the screen. If the target hardware is different from the native target, then the printf routine will need to be rewritten to replace the native version. Any attempt to use the native version will cause a crash because either the hardware or the operating system is different.
This is a similar problem to that of I/O dependent calls. Typical routines are those which dynamically allocate memory, task control commands, use semaphores, and so on. Again, these need to be replaced with those supported by the target system.
These are often neglected but are essential to any conver-sion. With many executable files created by compilers, the program is not simply downloaded into memory and the program counter set to the start of the module. Some systems attach a module header to the file which is then used by the operating system to load the file correctly and to preload registers with address pointers to stack and heap memory and so on. Needless to say, these need to be changed or simulated to allow the file to execute on the target. The start-up routine is often not part of a library and is coded directly into the module.
Similar problems can exist with exit routines used to terminate programs. These normally use an exit() call which removes the program and frees up the memory. Again, these need to be replaced. Fortunately, the routines are normally located in the run-time library rather than being hard coded.