Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (0)


Inline Assembler in Delphi (VI) - Calling external proceduresComponent available for this articleFormat this article printer-friendly!Bookmark function is only available for registered users!
Using different calling conventions to invoque external procedures from inline assembler code
Product:
Delphi 3.x (or higher)
Category:
Object Pascal
Skill Level:
Scoring:
Last Update:
09/12/2003
Search Keys:
delphi delphi3000 article borland vcl code-snippet inline assembler basm asm external dll stdcall lib library obj cdecl _cdecl pascal calling convention conventions register procedure function API functions procedures .obj .lib .dll static dynamic libraries
Times Scored:
3
Visits:
5877
Uploader: Ernesto De Spirito
Company: Latium Software
Reference: Pascal Newsletter #42
Component Download: http://www.latiumsoftware.com/download/p0042.zip
 
Question/Problem/Abstract:
How to call external procedures using different calling conventions to invoque external procedures from inline assembler code
Answer:



Inline Assembler in Delphi (VI)
Calling external procedures


By Ernesto De Spirito <edspirito@latiumsoftware.com>


API functions and the Stdcall calling convention



API functions are called transparently from inline assembler with the CALL statement. However, we must take into account that passing parameters to API functions is different since they normally use the Stdcall calling convention, instead of the Register calling convention, which is the one we've seen so far since it is the default convention.

In the Stdcall calling convention, all parameters are passed on the stack, from right to left, i.e. the last (rightmost) parameter is pushed first, and the first (leftmost) parameter is pushed last, so it'll the one on top of the stack. Here is an example of a procedure that calls an API function:

  procedure HideForm(Handle: THandle);
  // Windows.ShowWindow(Handle, SW_HIDE);
  asm
    push SW_HIDE               // push 0    // Pass second parameter
    push Handle                // push eax  // Pass first parameter
    call Windows.ShowWindow    // Call the API ShowWindow
  end;

If we have to call a method that uses the stdcall convention, remember that the Self pointer is an invisible first parameter, so it'll be pushed last on the stack.

If we have to write functions that use the stdcall convention, there's nothing special we should worry about. The compiler will always create a stack frame and references to parameter names will be converted into addresses relative to the base pointer:

  function AddAndMultiply(i1, i2, i3: integer): integer; stdcall;
  asm  // ==> push ebp; mov ebp, esp
    // Result := (i1 + i2) * i3;
    mov eax, i1   // mov eax, [ebp+8]
    add eax, i2   // add eax, [ebp+12]
    imul i3       // imul [ebp+16]
  end; // ==> pop ebp; ret 12

This is a sample call for the function:

  asm
    // a := AddAndMultiply(1, 2, 3); // result should be 9
    push 3
    push 2
    push 1
    call AddAndMultiply
    mov a, eax
  end;

After entering the function, the stack would look like this:

  |                |
  +----------------+
  |    Old EBP     |  [EBP], [ESP]
  +----------------+
  | Return Address |  [EBP+4]
  +----------------+
  |     i1 = 1     |  [EBP+8]
  +----------------+
  |     i2 = 2     |  [EBP+12]
  +----------------+
  |     i3 = 3     |  [EBP+16]
  +----------------+
  |                |


C/C++ libraries and the Cdecl calling convention



Sometimes we need to access functions in object files (.OBJ), static libraries (.LIB) or dynamic libraries (.DLL) written in C or C++, and quite frequently these functions use the Cdecl calling convention. It is very much like the Stdcall convention, but the stack should be cleaned by the caller, i.e., the caller should pop the parameters it pushed, or -better- increment the stack pointer.

  function AddAndMultiply(i1, i2, i3: integer): integer; cdecl;
  asm  // ==> push ebp; mov ebp, esp
    // Result := (i1 + i2) * i3;
    mov eax, i1   // mov eax, [ebp+8]
    add eax, i2   // add eax, [ebp+12]
    imul i3       // imul [ebp+16]
  end; // ==> pop ebp; ret

Notice in the comment for the last line that the function doesn't move the stack pointer as it did in the previous example that used the Stdcall convention, so the caller is the one who is responsible for that. This is a sample call for the function:

  asm
    // a := AddAndMultiply(1, 2, 3); // should be 9
    push 3
    push 2
    push 1
    call AddAndMultiply
    add esp, 12           // clean the stack
    mov a, eax
  end;

Notice that if the parameters were of type Byte instead of Integer, we should still move the stack pointer by 12 bytes since each parameter would take 32 bits (4 bytes) anyway.


The Pascal calling convention



Many C/C++ programmers prefer the Pascal calling convention over the Cdecl calling convention because it is more compact and also faster since the called function clears the stack in the RET statement, as it happens in the Stdcall convention. The Pascal convention is like the Stdcall convention, but parameters are passed left-to-right instead of right-to-left, i.e. the first (leftmost) parameter is pushed first, and the last (rightmost) parameter is pushed last:

  function AddAndMultiply(i1, i2, i3: integer): integer; pascal;
  asm  // ==> push ebp; mov ebp, esp
    // Result := (i1 + i2) * i3;
    mov eax, i1   // mov eax, [ebp+16]
    add eax, i2   // add eax, [ebp+12]
    imul i3       // imul [ebp+8]
  end; // ==> pop ebp; ret 12

Notice how the addresses of the parameters are translated different than in the previous examples.

This is a sample call for the function:

  asm
    // a := AddAndMultiply(1, 2, 3); // should be 9
    push 1
    push 2
    push 3
    call AddAndMultiply
    mov a, eax
  end;

After entering the function, the stack would look like this:

  |                |
  +----------------+
  |      EBP       |  [EBP], [ESP]
  +----------------+
  | Return Address |  [EBP+4]
  +----------------+
  |     i3 = 3     |  [EBP+8]
  +----------------+
  |     i2 = 2     |  [EBP+12]
  +----------------+
  |     i1 = 1     |  [EBP+16]
  +----------------+
  |                |




Previous: Inline Assembler in Delphi (V) - Objects
Next: Inline Assembler in Delphi (VII) - 128-bit integer arithmetic





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment













 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
S. Carter
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)