6.5 Using and Preserving Registers

In general, you should not assume that a register will have a given value when an __asm block begins. An __asm block inherits whatever register values happen to result from the normal flow of control.

If you use the __fastcall calling convention, the compiler passes function arguments in registers instead of on the stack. This can create problems in functions with __asm blocks, since a function has no way to tell which parameter is in which register. If the function happens to receive a parameter in AX and immediately stores something else in AX, the original parameter is lost. In addition, you must preserve the CX register in any function declared with __fastcall.

Summary: Don't use the __fastcall calling convention for functions with __asm blocks.

To avoid such register conflicts, don't use the __fastcall convention for functions that contain an __asm block. If you specify the __fastcall convention globally with the /Gr compiler option, declare every function containing an __asm block with __cdecl or __pascal. (The __cdecl attribute tells the compiler to use the C calling convention for that function. The __pascal attribute tells the compiler to use the FORTRAN/Pascal convention, which is the default for C++ functions.) If you are not compiling with /Gr, avoid declaring the function with the __fastcall attribute.

As you may have noticed in the POWER2.C example in “Writing Functions”, the power2 function doesn't preserve the value in the AX register. When you write a function in assembly language, you don't need to preserve the AX, BX, CX, DX, ES, and flags registers. However, you should preserve any other registers you use (DI, SI, DS, SS, SP, and BP).

Note:

If your inline assembly code changes the direction flag using the STD or CLD instructions, you must restore the flag to its original value.

Summary: Functions return small values in the AX and DX registers.

The POWER2.C example in “Writing Functions” also shows that functions return values in registers. This is true for return values that are four bytes or smaller (except for structures), whether the function is written in assembly language or in C or C++.

If the return value is short (a char, int, or near pointer), it is stored in AX. The POWER2.C example returned a value by terminating with the desired value in AX.

If the return value is long, store the high word in DX and the low word in AX. To return a longer value (such as a floating-point value), store the value in memory and return a pointer to the value (in AX if near or in DX:AX if far).

Assembly instructions that appear inline with C or C++ statements are free to alter the AX, BX, CX, and DX registers. C and C++ don't expect these registers to be maintained between statements, so you don't need to preserve them. The same is true of the SI and DI registers, with some exceptions (see “Optimizing”). You should preserve the SP and BP registers unless you have some reason to change them—to switch stacks, for example.