此处所列的文章均是我自己从国外的网站摘抄并翻译的,由于英文水平有限,里面肯定有不少错漏.翻译这些东西没有其他的什么用途,只是提高自己的英语阅读能力和编程技术水平而已     

Inline Assembler in Delphi (VI) Calling external procedures

 

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


posted @ 2010-11-02 09:01  AppleAndPear  阅读(555)  评论(0编辑  收藏  举报