Interfacing Assembly Language Routines with C |
|
Before entering deep into the topic, I would like to address the general description of:
Subroutine invocation:
I must clear at this point that, now we are entering into the world of assembly language of 8086 family microprocessors, so further discussion heavily depends upon it. Prior knowledge of 8086 assembly language is assumed. I shall give explanation as far as space constraints allow. Simplest form of subroutine invocation is "CALL" mnemonic. Notre that stack frame is different for "FAR CALL" and for "NEAR CALL".
Passing Parameters:
High level languages like C pass parameters on stack. Because each high level language uses memory in different ways, a common area is needed for subroutine interfacing. The accepted standard is stack. Mnemonic used to store 16 bit data on stack is "PUSH". "POP" to retrieve data. Normally if the parameter is a short numeric integer, its data value is pushed on stack. If the parameter is an alphanumeric string, a pointer to the string is passed. Floating point format numbers can be passed either by pointer or direct value. (single/double precision)
Accessing parameters on the stack is simplified by secondary stack pointer: BP. The BP register is used for accessing information on stack. The important thing is our assembly subroutines which are "extern" for C program should use BP pointer in such a way that it should not affect the working of main C program. C programs heavily depends on BP pointer.
Returning values:
Values are returned by following ways:
Although above discussion is not through, It gives good idea of the basics of interfacing assembly routines with high level language. Now on the basis of above discussion I would like to start with the topic of our interest: ( Interfacing with C )
Memory models are important concepts in C programs. They have significant impact on the guidelines given below.
Most C programs are written in either small or large memory model. General interfacing guidelines:
There are two ways to interface with C or C++. The easier method is to specify a language using assembler directives. (coming next) This feature is available with MASM and TASM. Another old method is to do manually what can be done automatically. Real knowledge lies in the older method. I have summarized the points in brief which are absolutely necessary for interfacing.
Simpler interfacing:
Latest assemblers provide facility of interfacing with popular languages very easily by means of advanced assembler directives. Older interfacing is shown in next block.
PUBLIC _MYFUNCT _TEXT SEGMENT WORD PUBLIC ‘CODE’ ASSUME CS:_TEXT _MYFUNCT PROC NEAR ; For small memory model.
If you are using newer advanced directives then this same information is coded as follows
PUBLIC MYFUNCT
.MODEL small, C .CODE MYFUNCT PROC
This source code is much cleaner. .MODEL and .CODE simplify the programmers task.
Interfacing subroutines without parameter passing:
These subroutines are very easy to program. Only care that should be exercised is, the special purpose registers (CS ,ES, DS, SS, DI, SI) which get modified during the subroutine execution should be restored before the procedure returns to main calling program. An example is given below.
The C program which uses this procedure CUR_ON is shown below. Note that prototype of the function is "extern."
extern void CUR_ON ( void ) ; void main ( void ) { CUR_ON( ) ; } Interfacing subroutines with parameter passing:
This is the most interesting part of the tutorial. TASM / MASM assemblers provide advanced directives. If you use them then you don’t have to understand how parameters are manipulated in a C compatible assembly routine. One of these directives is "USES". An example is given below. The example also shows how to return values from a subroutine.
Extra lines we see in the second column are in fact added by the assembler itself if we use the directives. BP must be saved because it is used by the calling program. The procedure given above requires one parameter on stack of 16 bits. Observe how it is used using BP pointer. The stack frame of the program before execution of instruction mov ax, [bp+4] is shown below.
LSB = Least Significant Byte. MSB = Most Significant Byte.
Thus you can observe that the first parameter begins at address BP+4. Don’t forget that this is the first parameter from left of the function call in C. All the remaining parameters (if any) should be successively push on stack prior to call. They can be manipulated using based index addressing mode e.g. [BP+6] Also important point is, in 8086 family microprocessor stack grows downwards hence the memory locations are given in decreasing order. If you have observed keenly the location of BP pointer, you must have noticed the it points at it’s original contents. But what about the pushed ES and DI? Those are pushed on stack to restore them later.
For extra information I would like to mention here about local variables. Those are stored after ES and DI. Yes! in stack. And every reference to it produces an instruction containing [BP - 6], [BP - 8] etc. operands. Thus in general in a C program, [BP + n] manipulates parameters and [BP - n] manipulates local variables.
A C program which uses above assembly procedure is given below.
#include <stdio.h> extern int TRIPLE ( int ) ; void main ( void ) { int p ;
p = TRIPLE ( 20 ) ; printf ( "%d", p ) ; }
Now because the procedure is returning a 16 bit data (integer) the compiler produces code which reads AX after function call and assigns it to p. (Refer table given above) As p is also a local variable it is stored in stack. Hence a typical code produced is
mov [BP + n], ax
Note that BP has been restored by the procedure before returning. All the library functions, user defined functions follow the same technique. Just for fun try following code:
#include <stdio.h> int twice ( int ) ; void main ( void ) { int p ;
p = twice ( 20 ) ; printf ( "%d", p ) ; }
int twice ( int q ) { q*=2; asm { mov ax, q add ax, 10 pop bp ret } }
The answer you get is 50. Which shows you have fooled the compiler!! Some of you must be wondering what is "asm"? This is a facility provided by C compiler to write inline assembly code. If writing assembly code is so simple in C then why did we go through all the way long in the tutorial. The reason is:
Compiling C programs with "extern" function calls:
I shall explain it using TASM assembler.
|
Designed and Managed by |
|