NASM meets Delphi - Procedures, Functions & Parameters
This site hosted by Free.ProHosting.com
Google

Procedures, Functions & Parameters

In the last issue you have learned how to write a very basic procedure which could be called by a Delphi Application. This time I will introduce how to pass parameters to functions and returning a values to the caller.

Returning a Value

Perhaps you want to write the following function with NASM:

function GetMaxDword: Dword;

Returning a value is quiet easy. You only have to store your result in "eax". Our Assemblercode looks like that:

GetMaxDword:
mov edx, 0
not edx
mov eax, edx
ret

Note that I've only used "edx" to illustrate that you finally have to move your value into "eax".

If you want to return a floating point value you can store it on the FPU Stack ("st0").

Retrieving Parameters

Where your parameters are stored depends on the calling convention you've chosen. Delphi's standard calling convention is "register", fast and complicated. But there are a few other cc you might want to use. To make it a bit clearer I will explain it on the Delphi function:

function Foobar(a,b,c,d,e: Integer): Integer; register; external;

Stdcall

a = [esp+4]
b = [esp+8]
c = [esp+12]
d = [esp+16]
e = [esp+20]
result = eax
exit with "ret 20"

Cdecl

a = [esp+4]
b = [esp+8]
c = [esp+12]
d = [esp+16]
e = [esp+20]
result = eax
exit with "ret"

Register

a = eax
b = edx
c = ecx
d = [esp+8]
e = [esp+4]
result = eax
exit with "ret 8"

Pascal

a = [esp+20]
b = [esp+16]
c = [esp+12]
d = [esp+8]
e = [esp+4]
result = eax
exit with "ret 20"

Parameter Types

Const and Var Parameters

The modifiers const and var do not pass the data of the value but the address. So if your function looks like

procedure Decrease(var Val: Integer); register; external;

you can access Val with the following code:

Decrease:
dec dword [eax]
ret

Note that the following declaration works with the identical sourcecode:

procedure Decrease(Val: PInteger); register; external;

Floating Point Values

Floating Point Values are always stored on the stack.

Open Arrays

The caller passes two values, the address of the array and the highest index. E. g.

function Sum(ints: array of Integer): Integer; register; external;

would look like that in your assembler code:

Sum:
; eax <- Address
; edx <- highest Index
mov ecx, 0
.addloop:
add ecx, dword [eax + 4*edx]
dec edx
jns .addloop
mov eax, ecx
ret

Stack Frames

To explain why stack frames are such useful I will first try to investigate the "ret" Instruction. "Ret" should be called in the end of every function. It takes one parameter specifying the number of bytes stored on the stack by the caller. "Ret" remembers the value the stack pointer (esp) points at. Then it adds the value passed to ret and 4 to the stack pointer (esp := esp + val + 4). Finally "ret" jumps to the address the stack pointer has pointed at. You will hopefully see that it is very important that the stack pointer remains unchanged during your function.

Well, you have to change the stack pointer if you want to use Local Variables because they are located on the stack. To reserve the memory for two Dwords you would type

sub esp, 8

Now you should use Stack Frames. Stack Frames save the stack pointer in ebp and finally restore it, but thats not the end of the story. ebp belongs to the registers whose values must remain unchanged or Delphi will run into problems (only eax, edx and ecx can be changed whithout being saved). A complete stackframe could look like this:

push ebp
mov ebp, esp
sub esp, 4
mov esp, ebp
pop ebp
ret

Alternatively you could code the following which does the same but is a little bit slower (well, you probably won't notice it):

enter 4
leave
ret

Final Words

You should be able to write your own programs now. Sit down, play with what you've got and look how smooth and fast your code gets. Well, I've only touched some subjects here so let me know if you have some additions or if you found a mistake.

wkx

« Previous Issue Back to assembly section