==============================================================================
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