============================================================================== 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. C Scene Official Email : cscene@mindless.com |
|