ZeePedia

Interfacing with High Level Languages: Calling Conventions, Calling C from Assembly

<< Protected Mode Programming: VESA Linear Frame Buffer, Interrupt Handling
Comparison: Motorolla 68K Processors, Sun SPARC Processor >>
img
16
Interfacing with High
Level Languages
16.1. CALLING CONVENTIONS
To interface an assembly routine with a high level language program
means to be able to call functions back and forth. And to be able to do so
requires knowledge of certain behavior of the HLL when calling functions.
This behavior of calling functions is called the calling conventions of the
language. Two prevalent calling conventions are the C calling convention and
the Pascal calling convention.
What is the naming convention
C prepends an underscore to every function or variable name while Pascal
translates the name to all uppercase. C++ has a weird name mangling
scheme that is compiler dependent. To avoid it C++ can be forced to use C
style naming with extern "C" directive.
How are parameters passed to the routine
In C parameters are pushed in reverse order with the rightmost being
pushed first. While in Pascal they are pushed in proper order with the
leftmost being pushed first.
Which registers must be preserved
Both standards preserve EBX, ESI, EDI, EBP, ESP, DS, ES, and SS.
Which registers are used as scratch
Both standards do not preserve or guarantee the value of EAX, ECX, EDX,
FS, GS, EFLAGS, and any other registers.
Which register holds the return value
Both C and Pascal return upto 32bit large values in EAX and upto 64bit
large values in EDX:EAX.
Who is responsible for removing the parameters
In C the caller removes the parameter while in Pascal the callee removes
them. The C scheme has reasons pertaining to its provision for variable
number of arguments.
16.2. CALLING C FROM ASSEMBLY
For example we take a function divide declared in C as follows.
int divide( int dividend, int divisor );
To call this function from assembly we have to write.
push dword [mydivisor]
img
Computer Architecture & Assembly Language Programming
Course Code: CS401
CS401@vu.edu.pk
push dword [mydividend]
call _divide
add esp, 8
; EAX holds the answer
Observe the order of parameters according to the C calling conventions
and observe that the caller cleared the stack. Now take another example of a
function written in C as follows.
void swap( int* p1, int* p2 )
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
To call it from assembly we have to write this.
[section .text]
extern _swap
x:
dd 4
y:
dd 7
push dword y
push dword x
call _swap
; will only retain the specified registers
add esp, 8
Observe how pointers were initialized appropriately. The above function
swap was converted into assembly by the gcc compiler as follows.
; swap generated by gcc with no optimizations (converted to Intel
syntax)
; 15 instructions AND 13 memory accesses
_swap:
push ebp
mov
ebp, esp
sub
esp, 4
; space created for temp
mov eax, [ebp+8]
mov eax, [eax]
mov
[ebp-4], eax
; temp = *p1
mov edx, [ebp+8]
mov eax, [ebp+12]
mov eax, [eax]
mov
[edx], eax
; *p1 = *p2
mov edx, [ebp+12]
mov eax, [ebp-4]
mov [edx], eax
; *p2 = temp
leave
;;;;; EQUIVALENT TO mov esp, ebp AND pop ebp ;;;;;
ret
If we turn on optimizations the same function is compiled into the following
code.
180
img
Computer Architecture & Assembly Language Programming
Course Code: CS401
CS401@vu.edu.pk
; generated with full optimization by gcc compiler
; 12 instructions AND 11 memory accesses
_swap:
push
ebp
mov
ebp, esp
push
ebx
mov
edx, [ebp+8]
mov
ecx, [ebp+12]
mov
ebx, [edx]
mov
eax, [ecx]
mov
[edx], eax
mov
[ecx], ebx
pop
ebx
pop
ebp
ret
16.3. CALLING ASSEMBLY FROM C
We now write a hand optimized version in assembly. Our version is only 6
instructions and 6 memory accesses.
Example 16.1
001
[section .text]
002
global
_swap
003
_swap:
mov  ecx,[esp+4]
; copy parameter p1 to ecx
004
mov  edx,[esp+8]
; copy parameter p2 to edx
005
mov  eax,[ecx]  ; copy *p1 into eax
006
xchg eax,[edx]  ; exchange eax with *p2
007
mov  [ecx],eax  ; copy eax into *p1
008
ret
; return from this function
We assemble the above program with the following command.
·nasm ­f win32 swap.asm
This produces a swap.obj file. The format directive told the assembler that
it is to be linked with a 32bit Windows executable. The linking process
involves resolving imported symbols of one object files with export symbols of
another. In NASM an imported symbol is declared with the extern directive
while and exported symbol is declared with the global directive.
We write the following program in C to call this assembly routine. We
should have provided the swap.obj file to the C linker otherwise an
unresolved external symbol error will come.
Example 16.1
001
#include <stdio.h>
002
003
void swap( int* p1, int* p2 );
004
005
int main()
006
{
007
int a = 10, b = 20;
008
printf( "a=%d b=%d\n", a, b );
009
swap(&a, &b );
010
printf( "a=%d b=%d\n", a, b );
011
system( "PAUSE" );
012
return 0;
013
}
181
img
Computer Architecture & Assembly Language Programming
Course Code: CS401
CS401@vu.edu.pk
EXERCISES
1. Write a traverse function in assembly, which takes an array, the
number of elements in the array and the address of another function
to be called for each member of the array. Call the function from a C
program.
2. Make the linked list functions make in Exercise 5.XX available to C
programs using the following declarations.
struct node {
int data;
struct node* next;
};
void init( void );
struct node* createlist( void );
void insertafter( struct node*, int );
void deleteafter( struct node* );
void deletelist( struct node* );
3. Add two functions to the above program implemented in C. The
function "printnode" should print the data in the passed node using
printf, while "countfree" should count the number of free nodes by
traversing the free list starting from the node address stored in
firstfree.
void printnode( struct node* );
void countfree( void );
4. Add the function "printlist" to the above program and implement
in
assembly. This function should traverse the list whose head
is
passed as parameter and for each node containing data (head
is
dummy and doesn't contain data) calls the C function printnode
to
actually print the contained data.
void printlist( struct node* );
5. Modify the createlist and deletelist functions in the above program to
increment and decrement an integer variable "listcount" declared in
C to maintain a count of linked lists present.
182