Mixing C and ASM compilers

by : Peter Quiring
date : Aug/97
update : Mar 31/98

This tutorial will teach you how to use all those different compilers together in harmony. It will also should you how to link your application later. So I'm going to describe how each should be used so they can all work with each other. This convention is used by QLIB v2.00 and is not intended to force everyone to use it, you may use what you like but this is how QLIB was designed.
The Goal
The goal is to pick conventions that all compilers support and use that. Therefore I'll show you here what conventions I use that EVERY compiler will support (BC, WC, M$C, MASM, TASM, etc.).
  • I use stack calling convention cause it's easy to use and frees up a lot of registers, if every register is needed to pass stuff to a rountine then you can't use those through out your program without saving there contents each time you call a rountine.
  • Fastcall can not be used since there is no standard for it. Each compiler uses it's own order in which to pass parameters in registers.
  • I decided to have all rountines preserve all regs that it does not return info in, which usually is just EAX.
  • I never use 486 or higher specfic stuff, so that 386 users could run my programs if need be. As for 286 and below, they will just have to upgrade to 386 or better to follow this convention I use (and to program in PMODE in any case).
Compiler Options
And now here's each compiler and how to force each to follow this convention.

Borland C v4.5 or v5.0

This compiler is very good. Here's how I would compile using it.

bcc32 -Vs -K -B -3 -c yourfile.c

The -Vs option forces Borland to create Watcom compatible OBJ files.
The -K option forces char=unsigned byte. [optional]
The -B will force BC to use tasm32.exe to compile (in case there is ASM {} blocks within the code). [optional]
The -c will compile only (see the next section on linking info).
The -3 option allows BC to use 386 instructions.

Watcom C v10.x and v11.0

This is a good compiler, a bit buggy.

[WCC386 | WPP386] -s -zl -fpi87 -fp3 -mf -3s -zp1 -zdp -zfp -zgp yourfile.c

There any many options here but basically they do the following: use stack calling convention, use inline FPU 80387, and make sure that Watcom does not use GS or FS for it's own purpose.
You must also add the following somewhere in your code:
//enable Watcom underscore appending
//Also force Watcom to return double/floats in st(0)
#ifdef __WATCOMC__
  #pragma aux default "_*" parm caller [] \
  value struct routine [eax 8087] \
  modify [eax];
  //Change asm to _asm for Watcom v11.0
  #define asm _asm
This will add the underscore to all externs/publics. This is already in QLIB v2.00+.
It also forces floats to be returned in st(0) which is a standard.

Micro$oft C++

This compiler is very fusy and took me forever to support within QLIB.
If your prototype for standard C functions do not match ANSI C 100% it causes errors in compile.
cl /Ox /J /c /Gd /Gs /Zl yourfile.c
I forget what each means since I just uninstalled M$ Visual C++ but you can easily find out with CL /help.

MASM v6.11

This compiler sux but is better than TASM. For many reasons I can't go into here.

ML /c /Cx /Cp yourfile.asm

The /c means compile only.
The /Cx and /Cp are optional but they compile with case sensitivity enabled.
Inside the ASM file you should start the file like this:
  .model flat,c
  assume fs:flat,gs:flat
The assume is needed to allow you to use FS and GS because by default these are assumed to ERROR because M$ thought that under flat mode you would not need them.

TASM v5.0

Here's how to use it to compile:

tasm32 /zn /q /m2 /ml yourfile.asm

The /zn will remove all debug info. [optional]
The /q suppresses OBJ records not needed for linking. [optional]
The /m2 is very important or forward references may not be allowed to compile.
The /ml enables full case sensitivity. [optional]
Inside the ASM file you should start the file like this:
.model flat,c
I've not been able to successful use TASM because it can not compile MASM files yet (although Borland says it can now). So the above "should" work, but I've never been able to test it out since all my code is for MASM.

That's it

The finished OBJ files created by any compiler will now be able to easily call functions in other OBJ files.
Linking Options
There is only one linker that can be used to Link PMODE programs, and that is WLINK (Watcom Linker).
The Watcom linker is an very advanced linker and you should give option to it thru a response file.
This is how I use it:
echo system choose_a_system >tmp.tmp
echo name choose_an_exe_name >>tmp.tmp
echo file choose_an_obj_or_lib_file >>tmp.tmp
echo path choose_an_obj_path >>tmp.tmp
echo libpath choose_an_lib_path >>tmp.tmp
wlink @tmp.tmp
Where choose_a_system can be pmodew, dos4g, or wdosx if you have these DOS extenders. Normally the dos4g system is the only one defined in the WLINK.LNK file. To add others edit this file and add systems. Here are examples for PMODE/W and WDOSX.
system begin pmodew
    option osname='PMODE/W'
    option stub=pmodew.exe
    format os2 le

system begin wdosx
    option osname='WDOSX'
    option stub=wdosxle.exe
    format os2 le
You can insert as many obj/libs as you need. The libpath and path option tells WLINK what directory it should look in for OBJs and LIBs if it can not find it in the current directory.
There are other options that Watcom uses, for more help use WLINK /? and maybe print them up.
ASM Layout
In order to call C functions from your ASM files I suggest you do one of the following:

If your assembler supports quick directives use this:
.model flat,c
assume fs:flat,gs:flat   ;override MASM bug
  Place code here
  Place init data here
  Place uninit data here
.stack ###  ;define a stack of ### bytes
If not, try this:
_TEXT segment dword public use32 'CODE'
  Place code here
_TEXT ends
_DATA segment dword public use32 'DATA'
  Place init data here
_DATA ends
_BSS segment dword public use32 'BSS'
  Place uninit data here
_BSS ends
_STACK segment dword public use32 'STACK'
  Place stack here, like this:
  db ### dup (?)
_STACK ends

dgroup group _TEXT,_DATA,_STACK,_BSS

assume ds:dgroup,es:dgroup,ss:dgroup,fs:dgroup,gs:dgroup
That should work.

Hello, world example


  void main(void) {
    printf("Hello, world!");
To compile/link (using BC compiler/WC linker):
  bcc32 -Vs -K -B -3 -c hello.c
  if not exist hello.obj goto end
  echo system dos4g >tmp.tmp
  echo name hello.exe >>tmp.tmp
  echo file hello.obj >>tmp.tmp
  echo path . >>tmp.tmp
  echo libpath .;%lib% >>tmp.tmp
  wlink @tmp.tmp
And now you can use HELLO.EXE, a DOS 32bit-extended application!
Copyright © 1995-2005 Nexus Systems Privacy
SourceForge.net Logo