20.3.1 The C/MASM Interface

This section summarizes the details unique to the C and MASM interface. The information is accurate for Microsoft C 6.0 and QuickC version 2.5.

With the default naming and calling convention, the assembler (or compiler) pushes arguments right to left and adds a leading underscore to routine names.

Compatible Data Types

This list shows the C data types that are equivalent to the MASM 6.0 data types.

C Type Equivalent MASM Type  
unsigned char BYTE  
char SBYTE  
unsigned short, unsigned int WORD  
int, short SWORD  
unsigned long DWORD  
float REAL4  
C Type Equivalent MASM Type  
long SDWORD  
double REAL8  
long double REAL10  

Naming Restrictions

C is case sensitive and does not convert names to uppercase. Since C normally links with the /NOI command-line option, assemble MASM modules with the /Cx or /Cp option to prevent the assembler from converting names to uppercase.

Argument-Passing Defaults

When the C module is compiled in small or medium model and when a distance is not specified, the C compiler passes arrays by near reference. In compact, large, or huge model, C arrays are passed by far reference (if a distance is not explicitly specified). All other types defined in the C module are passed by value. You can pass by reference if you specifically pass pointers or addresses.

Changing the Calling Convention

Put _pascal or _fortran in the C function declaration to specify the Pascal calling convention.

Equivalent Arrays

Array declarations give the number of elements. A1[a][b] declares a two-dimensional array in C with a rows and b columns. By default, the array's lower bound is zero. Arrays are stored by the compiler in row-major order. By default, passing arrays from C passes a pointer to the first element of the array.

String Format

C stores strings as arrays of bytes and uses a null character as the end-of-string delimiter. For example, consider the string declared as follows:

char msg[] = "string of text"

The string occupies 15 bytes of memory as:

Since msg is an array of characters, it is passed by reference. To pass by value, declare the string to be a member of a structure and pass the structure.

Summary: External data can be accessed directly by other modules.

External Data

In C, the extern keyword tells the compiler that the data or function is external. You can define a static data object in a C module by defining a data object outside all functions and subroutines. Do not use the static keyword in C with a data object that you wish to be public.

Summary: C structures are word-aligned by default.

Structure Alignment

By default, C uses word alignment (unpacked storage) for all data objects longer than one byte. This storage method specifies that occasional bytes may be added as padding, so that word and doubleword objects start on an even boundary. In addition, all nested structures and records start on a word boundary. MASM is byte-aligned by default.

When transferring .H files with H2INC, you can use the /Zp command-line option to specify structure alignment. If the /Zp option is not specified, H2INC uses word-alignment. Without H2INC, set the alignment to 2 when declaring the MASM structure, or compile the C module with /Zp1 or the MASM module with /Zp2.

Compiling and Linking

Use the same memory model for both C and MASM.

Returning Values

The assembler returns simple data types in registers. Table 20.2 shows the register conventions for returning simple data types to a C program.

Table 20.2 Register Conventions for Simple Return Values

Data Type Registers,  
char AL,  
int, short, near AX,  
long, far High-order portion (or segment address) in DX; low-order portion (or offset address) in AX,  
    ^

Procedures using the C calling convention and returning type float or type double store their return values into static variables. In multi-threaded programs, this could mean that the return value may be overwritten. You can avoid this by using the Pascal calling convention for multi-threaded programs so float or double values are passed on the stack.

Summary: Your procedures can also return structures.

Structures less than four bytes long are returned in DX:AX. To return a longer structure from a procedure that uses the C calling convention, you must copy the structure to a global variable and then return a pointer to that variable in the AX register (DX:AX, if you compiled in compact, large, or huge model or if the variable is declared as a far pointer).

Structures, Records, and User-Defined Data Types

You can pass structures, records, and user-defined types as arguments by value or by reference.

Writing Procedure Prototypes

The H2INC utility simplifies the task of writing prototypes for the C functions you want to call from MASM. The C prototype converted by H2INC into a MASM prototype allows INVOKE to correctly call the C function. Here are some examples of C functions and the MASM prototypes created with H2INC.

/* Function Prototype Declarations to Convert with H2INC */

long checktypes (

char *name,

unsigned char a,

int b,

float d,

unsigned int *num );

my_func (float fNum, unsigned int x);

extern my_func1 (char *argv[]);

struct videoconfig _far * _far pascal my_func2 (int, scri );

For the C prototypes above, H2INC generates this code:

TYPEDEF PROTO C :PTR SBYTE, :BYTE,

:SWORD, :REAL4, :PTR WORD

checktypes PROTO @proto_0

@proto_1 TYPEDEF PROTO C :REAL4, :WORD

my_func PROTO @proto_1

@proto_2 TYPEDEF PROTO C :PTR PTR SBYTE

my_func1 PROTO @proto_2

@proto_3 TYPEDEF PROTO FAR PASCAL :SWORD, :scri

my_func2 PROTO @proto_3

Example

As shown in the short example below, the main module (written in C) calls an assembly routine, Power2.

#include <stdio.h>

extern int Power2( int factor, int power );

void main()

{

printf( "3 times 2 to the power of 5 is %d\n", Power2( 3, 5 ) );

}

Figure 20.2 shows how functions that observe the C calling convention use the stack frame.

The MASM module that contains the Power2 routine looks like this:

.MODEL small, c

Power2 PROTO C factor:SWORD, power:SWORD

.CODE

Power2 PROC C factor:SWORD, power:SWORD

mov ax, factor ; Load Arg1 into AX

mov cx, power ; Load Arg2 into CX

shl ax, cl ; AX = AX * (2 to power of CX)

; Leave return value in AX

ret

Power2 ENDP

END

The MASM procedure declaration for the Power2 routine specifies the C langtype and the parameters expected by the procedure. The langtype specifies the calling and naming conventions for the interface between MASM and C. The routine is public by default. When the C module calls Power2, it passes two arguments, 3 and 5 by value.

The C module first defines a prototype for the MASM routine. MASM 6.0 also supports prototyping of procedures and functions. See Section 7.3.6, “Declaring Procedure Prototypes,” and the examples in this section.