segment = FP_SEG( p );
offset = FP_OFF( p );
A demonstration of MK_FP() is shown in Listing 5.1.
LISTING 5.1. DEMONSTRATIONUSEOFTHEMK_FP()MACRO.
1 /* KEYCHECK.C
2 Demonstrates use of the MK_FP macro.
3 */
4 #include <stdio.h>
5 #include <dos.h>
6 #include <conio.h>
7
8 void main(void) 9 {
10 unsigned char far *p;
11
12 p = MK_FP(0x0040,0x0017);
13 14 do {
15 if (*p & 128) puts(“Insert Mode toggled on.”);
16 else puts(“Insert Mode toggled off.”);
17 if (*p & 64) puts(“Caps Lock toggled on.”);
18 else puts(“Caps Lock toggled off.”);
19 if (*p & 32) puts(“Num Lock is toggled on.”);
20 else puts(“Num Lock is toggled off.”);
21 if (*p & 16) puts(“Scroll Lock is toggled on.”);
22 else puts(“Scroll Lock is toggled off.”);
23 puts(“Press a key to continue; Esc to stop: “);
24 } while ( getch() != 27 );
25 }
M IXED M ODEL P ROGRAMMING AND P OINTER M ODIFIERS
Occasionally, programs need to link modules that are compiled using a different memory model than that of the main program. Normally, you will not want to mix memory models, because various kinds of problems can occur.
When you need to mix code modules compiled with different memory models, however, you override the default near or far type, as appropriate, for the functions that your program calls.
Consider a small model program that must link an object module (or more typically, a module from a library) that has been compiled using the large memory model. By default, the compiler will generate near calls to all functions. A near function call will not reach the large model code; indeed, it probably will crash your program. Any data parameters that should be passed as a far pointer will be passed as near pointers, wreaking all kinds of havoc.
There is a solution to this mixed-model situation. You need to add a function prototype for the routines that will be called from the outside module. Suppose that you have a small model program that must display a complex number using a function named put_complex(), where put_complex() is defined in a large model object file named mycomp.obj. By default, the small model program issues a near call to put_complex(), passing near addresses as parameters. To override this default setting, either create a new prototype for the function, placing the far modifier before the function name, or better, define the header file for mycomp to explicitly use the far keyword on all functions and function parameters.
Listing 5.2 shows a sample mixed-model program named mixmodel.c, which was compiled using the small model. Listing 5.3 shows the header file for mycomp.h, and Listing 5.4 shows the mycomp.c source file. mycomp.c was compiled using the large model. There are two ways that the mixed memory models can be accommodated. Listing 5.3 shows the use of the far keyword in the header file. This way, all source files that include mycomp.h will get a function prototype that forces the compiler to generate a far call to put_complex().
LISTING 5.2. THEMAINSOURCEFILEFORDEMONSTRATINGMIXED-MODELPROGRAMMING.
1 /* MIXMODEL.C
2 Demonstrates calling a far function from a small model program.
3 */
4 #include <math.h>
5 #include “mycomp.h”
6
7 void main(void) 8 {
9 struct complex c;
continues
LISTING 5.2. CONTINUED 10 c.x = 3;
11 c.y = 1;
12
13 put_complex( &c );
14 15 }
LISTING 5.3. HEADERFILEFORTHELARGEMODELMODULE.
1 /* MYCOMP.H 2 */
3 #include <math.h>
4
5 void far put_complex ( struct complex far *x );
LISTING 5.4. THELARGEMODELMODULECONTAININGTHEput_complex() FUNCTION.
1 /* MYCOMP.C
2 Contains put_complex() compiled under the large memory model.
3 */
4
5 #include <stdio.h>
6 #include <math.h>
7 #include “mycomp.h”
8
9 void far put_complex ( struct complex far *x ) 10 {
11 printf(“%f+%fi”, x->x, x->y );
12 }
Another way, not shown in the sample listings, is to create your own header or function prototype for put_complex(). Suppose that mycomp.h had contained this definition:
void put_complex ( struct complex *x );
When the compiler sees this definition while compiling a small code model program, it will generate near function calls to put_complex(). You can manually fix this by creating your own prototype:
void far put_complex ( struct complex far *x );
You might insert this prototype directly into your source code, or you could copy mycomp.h to a new file, edit the header, and then include the revised header in your program.
After you’ve done all this, you still need to do some special work to get this program to link properly. If you try to compile and link this application using a conventional approach, you will get an abnormal program termination due to the linker’s confusion when trying to link the correct library for the call to
printf(). To understand the problem, you can compile this program by typing
bcc -c -ml mycomp.c bcc -ms mixmodel.c
These commands will compile mycomp.c into a large model object file and mixmodel.c into a small model object file. The program will link but it won’t execute correctly because the linker brings in a small model library version of
printf(). To get this to run, you need to reverse the order of the compilation and link:
bcc -c -ms mixmodel.c
bcc -ml -emixmodel.exe mycomp.c mixmodel.obj
The second command compiles mycomp.c as a large model program and links in the previously compiled mixmodel.obj, producing the executable mixmodel.exe.
You can see that mixed-model programming must be done sometimes, but you also can see how mixing memory models can cause problems.
U
SING THENEARM
ODIFIERWhen a module is compiled using a large code model, each call to a function within the module is also made as a far call. But because these functions are all located within the same code segment, it might not be necessary to use far calling conventions for all these functions. When you have functions that are
used only within the module and that are not called from outside the module, you can use the near keyword to change these to near functions. Here’s an example showing the near keyword used in a function prototype:
unsigned int near compute_elevation( double latitude, double longitude);
Functions called as near procedures are more efficient than those called as far procedures. Only two bytes are used for the function’s address (instead of four), so the underlying CALL machine instruction is shorter. Because the function is in the same segment, the current value of CS is not saved to the stack, saving two extra bytes of space on the stack and speeding up the push and pop of the return address.