Mixing C and ASM compilers

by : Peter Quiring
date : Aug/97

Overview
This tutorial will teach you how to use all those different compilers together in harmony. 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 ever 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 their 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.
  • 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.
The -B is optional and will force BC to use tasm32.exe to compile (in case there is ASM {} blocks within the code).
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:
#pragma aux default "_*";
This will add the underscore to all externs/publics. This is already in QLIB.H in QLIB v2.00.

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 ML /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:_text,gs:_text
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.
The /q suppresses OBJ records not needed for linking.
The /m2 is very important or forward references may not be allowed to compile.
The /ml enables full case sensitivity.
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, dos4gw, or wdosx if you have these DOS extenders.
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 it can not find in the current directory.
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:
.386
.model flat,c
assume fs:flat,gs:flat   ;override MASM bug
.code
  Place code here
.data
  Place init data here
.data?
  Place uninit data here
.stack ###  ;define a stack of ### bytes
If not, try this:
.386
_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.
BACK