==============================================================================
C-Scene Issue #2
..oO Interfacing Assembly With C Oo..
Moe Elzubeir
==============================================================================

I did NOT want to write this. So, don't blame me if any of it is written
badly, etc. I was forced into submission by Brent, it wasn't my fault ;)
Blame him ! Okay, now on with it...

[Oh BS ;}... I didn't force you... -ed]

I first have to admit to not knowing the AT&T syntax, although I have been
using Linux for quiet some time now, I still haven't bothered to get to it,
and so I end up writing pure C programs, which actually look better to me ;)
So, Brent sent me an AT&T syntax tutorial which I haven't read - yet.

[Lay-zee... -ed]

So, what's the deal ?

I assume you own a Microsoft C / Borland C/C++ / Or any other DOS compiler
basically that has an assembly compiler a linker and supports the Intel asm
syntax. That way we're all set and ready. If Brent decides to add any
modifications to this article to make it support the AT&T syntax well and
good, but as I write this, it's only for the Intel syntax. 

Before we begin, there are two ways or writing asm routines for your C
program : a. inline assembly
 	  b. separate C and assembly modules

In this article I am more concerned about the second one (b). Inline
assembly is basically for smaller asm routines, and basically for a program
that will not have a lot of assembly. But, when your the program grows in
size and starts carrying a lot of assembly it becomes hard to read and
follow. So, we go for the separate C and assembly modules.

Doing inline assembly differs from one compiler to another. So I will not
get into that. But, I can just say that in Borland C++ it is done as :

----------
#pragma inline

#include <blabla.h>

int main(void) {
	int i, ho;
	
	asm { 
		/* assembly code */
	}

	/* C code */
}

--------

Consult your compiler's manual for more information.

There are a few guidelines you must follow :

1. You must give a specific segment name to the code segment of your
   assembly language subroutine. The name may vary from one compiler to
   another. Microsoft C requires a segment name _TEXT for small/compact
   memory models, and a segment name with the suffix _TEXT for other memory
   Models. Other compilers may  have CODESEG, or some other more meaningful
   segment names. BC++ has it called DOSSEG.

2. Your C Compiler may require specific names for data segments. That's only
   if the data is being references outside the code segment. Microsoft C
   requires that segment to be called _DATA. On the other hand, for BC++ it 
   will be called .DATA.

3. You must understand how variables are being passed through the stack [I
   think someone wrote something about the Stack this issue].

	eg. function1(arg1,arg2,arg3,...,argn);

   argn is being pushed on the stack first, followed up until arg1.

4.  You must save any registers that your assembly routines may "disturb".
    Registers such as, CS,DS,SS,BP,SL, and DI. Failing to do so may result
    in undesirable results. But, you do not need to save the contents of AX,
    BX, CX, or DX since they are not considered "non-changeable" by C.

With those guidelines in mind, we can move on to some examples of simple
interfacing. 

---------------delayer.asm---------------

	DOSSEG
	.MODEL small
	.486

	.CODE
	PUBLIC	C	delayer

delayer	PROC	C NEAR
	push	BP
	mov	BP,SP

	mov	CX,91	; 91 ticks

D1:	push	CX

	mov	AH,0	; read time
	int	1Ah	; get initial ticks
D2:	push	DX	; save tick count
	mov	AH,0
	int	1Ah
	pop	BX	; get back to prev count
	cmp	BX,DX	; compare them.. 
	je	D2	; the same ? continue

	pop	CX
	loop	D1

	pop	BP
	ret

delayer	ENDP
	END
----------------delayer.am----------------

Now, the C module

--------------delayem.c-----------

#include <stdio.h>

int task();

int main(void) {

	printf("About to delay for 5 seconds. Hit Enter to start.\n");
	getch();

	printf("Starting delay for 5 seconds...\n");
	delayer();

	printf("Done!\n");
	
	return(0);
}
-----------delayem.c--------------

Now, to compile this example program, all you need to do is follow the
following steps :

1. tasm delayer.asm
2. bcc -c delayem.c
3. tlink c0s delayem delayer,delayem, ,cs

Commands, options vary from one compiler to another, this applied to
Borland C++ (and possibly TC++). Compile it, and try it.

Now that we know how to call and create a separate assembly module, you are
probably wondering, what about PASSING arguments,variables, to my assembly
routine ? Well, very simple, just declare your assembly function in your C
file. eg       int function1(int, char);

And then in your assembly code it would look something like :

function1	PROC	C NEAR	f1:WORD,f2:BYTE

The "C" above is a keyword which tells the program to expect arguments to be
passed from right to left via the stack. The procedure (routine) would be
declared NEAR if you are using a small memory model (of course when doing
32-bit programming, you won't have to bother with that any more). If using a
large, huge, etc. memory model, then it should be declared FAR. As for the
WORD, BYTE, and DWORD, those are assembly data types, and they can be placed
directly into the registers. (BYTE 8bit, WORD 16 bit, etc..).

The rest would basically look like any other assembly routine.

You can download a zipfile of the source for this here.


C Scene Official Web Site : http://cscene.oftheinter.net
C Scene Official Email : cscene@mindless.com
This page is Copyright © 1997 By C Scene. All Rights Reserved