3. Calling assembly functions from high level language
You can either use inline assembly or code a subroutine entirely in assembly language and link it into your project. If you choose the latter option, then it is recommended that you use a compiler which is capable of translating high level code directly to assembly. This assures that you get the function calling method right. Most C++ compilers can do this.
The methods for function calling and name mangling can be quite complicated. There are many different calling conventions, and the different brands of compilers are not compatible in this respect. If you are calling assembly language subroutines from C++, then the best method in terms of consistency and compatibility is to declare your functions extern "C" and _cdecl. The assembly code must then have the function name prefixed by an underscore (_) and be assembled with case sensitivity on externals (option -mx).
If you need to make overloaded functions, overloaded operators, member functions, and other C++ specialties then you have to code it in C++ first and make your compiler translate it to assembly in order to get the right linking information and calling method. These details are different for different brands of compilers. If you want an assembly function with any other calling method than extern "C" and _cdecl to be callable from code compiled with different compilers then you need to give it one public name for each compiler. For example an overloaded square function:
; int square (int x); SQUARE_I PROC NEAR ; integer square function @square$qi LABEL NEAR ; link name for Borland compiler ?square@@YAHH@Z LABEL NEAR ; link name for Microsoft compiler _square__Fi LABEL NEAR ; link name for Gnu compiler PUBLIC @square$qi, ?square@@YAHH@Z, _square__Fi MOV EAX, [ESP+4] IMUL EAX RET SQUARE_I ENDP ; double square (double x); SQUARE_D PROC NEAR ; double precision float square function @square$qd LABEL NEAR ; link name for Borland compiler ?square@@YANN@Z LABEL NEAR ; link name for Microsoft compiler _square__Fd LABEL NEAR ; link name for Gnu compiler PUBLIC @square$qd, ?square@@YANN@Z, _square__Fd FLD QWORD PTR [ESP+4] FMUL ST(0), ST(0) RET SQUARE_D ENDP
The way of transferring parameters depends on the calling convention:
calling convention | parameter order on stack | parameters removed by |
_cdecl | first par. at low address | caller |
_stdcall | first par. at low address | subroutine |
_fastcall | compiler specific | subroutine |
_pascal | first par. at high address | subroutine |
Register usage in 16 bit mode DOS or Windows, C or C++:
16-bit return value in AX, 32-bit return value in DX:AX, floating point return value in ST(0). Registers AX, BX, CX, DX, ES and arithmetic flags may be changed by the procedure; all other registers must be saved and restored. A procedure can rely on SI, DI, BP, DS and SS being unchanged across a call to another procedure.
Register usage in 32 bit Windows, C++ and other programming languages:
Integer return value in EAX, floating point return value in ST(0). Registers EAX, ECX, EDX (not EBX) may be changed by the procedure; all other registers must be saved and restored. Segment registers cannot be changed, not even temporarily. CS, DS, ES, and SS all point to the flat segment group. FS is used by the operating system. GS is unused, but reserved. Flags may be changed by the procedure with the following restrictions: The direction flag is 0 by default. The direction flag may be set temporarily, but must be cleared before any call or return. The interrupt flag cannot be cleared. The floating point register stack is empty at the entry of a procedure and must be empty at return, except for ST(0) if it is used for return value. MMX registers may be changed by the procedure and if so cleared by EMMS before returning and before calling any other procedure that may use floating point registers. All XMM registers may be modified by procedures. Rules for passing parameters and return values in XMM registers are described in Intel's application note AP 589. A procedure can rely on EBX, ESI, EDI, EBP and all segment registers being unchanged across a call to another procedure.