Compiling Program With Microsoft C Version 5.1


As mentioned in the main article, the key requirement for any interrupt driver is speed. For this reason, assembly language is generally preferred over high level languages for implementing interrupt drivers. However, it is possible to approach the speed of hand optimized assembly code by using a good optimizing compiler, and by careful choice of C constructs to generate faster code.

The /0x switch in Microsoft C5.1 used for compiling the interrupt driver (see Listing 7 for make file TERM.MK) generates code optimized for maximal speed. It performs the following optimizations: relaxed alias checking, loop optimization (unrolling of loops, moving calculations out of loops, etc.), intrinsic substitution (generating inline assembly code instead of function calls for common functions like memcpy, inp, outp, _enable, _disable, etc.), favor execution speed (over code size) in all optimizations, disable stack probing (also done by the #pragma check_stack(off) statements in the interrupt drivers). In practice, I have found that this switch produces assembly code that is pretty efficient, maybe not quite as much as hand optimized assembly code, but very close.

Disabling of stack probing for interrupt routines is necessary since Microsoft's stack probe assumes that the stack occupies the space allocated during program initialization. However, it is possible to have some other stack active (i.e., a DOS stack) when an interrupt occurs. In this case, the stack probe would crash the program with a stack overflow message. Of course, removing stack probes also decreases overall execution time.

Because stack probing is disabled you should avoid using automatic local variables (these would increase stack allocation). Instead declare all local variables as static (preferably static near so that they will be in the default DS segment).

When using the /Ox switch, you should avoid declaring loop indexes as register variables — doing so may interfere with the compiler's loop optimization (it will normally try to keep loop indexes in registers for the duration of the loop anyway). Also you should not assign a pointer to point to a different item within a loop (like p_a = &a and later p_a = &b) as the compiler may not recognize this with the relaxed aliasing, and generate incorrect code as a result. Auto- increment/decrement (p_a++ or p_a-- --) are safe to use though. You may reassign the pointer outside of the loop, but be careful.

The other switches which are used for compiling all programs are /AL (use large library), /Zp (pack structures instead of trying to align them on word boundaries), /c (compile but don't link), and /W3 (maximum warning level). In addition, if you know that your program will only be executed on a 80286 or higher, you can add the /G2 switch to generate 80286 specific code (which will be slightly more efficient).