views:

152

answers:

2

When writing a Delphi procedure or function in assembly code, which registers must be saved and restored to the original value at the end of the procedure?

When calling another Delphi procedure or function from (inline) assembly code, what can I expect that other function to do with the registers? Which registers will be restored to their original values and which may not?

(Obviously, the same answer would apply to both questions)

I am assuming the default calling convention of Delphi. I know that EAX is used for 32-bit return values. And looking at the asm code in SysUtils.pas, it seems that EBX, ESI and EDI are pushed and restored, but the others are not. I cannot find any documentation about this, though.

+5  A: 

I don't know if the docs are up to date, but you should have a look at Using Inline Assembly Code (Win32 Only) at the Embarcardero Wiki:

Quote:

In general, the rules of register use in an asm statement are the same as those of an external procedure or function. An asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers, but can freely modify the EAX, ECX, and EDX registers. On entry to an asm statement, EBP points to the current stack frame and ESP points to the top of the stack. Except for ESP and EBP, an asm statement can assume nothing about register contents on entry to the statement.

Jens Mühlenhoff
Thanks for the link, even though it is broken (a closing parenthesis is missing in the URL).
Servaas
@Servaas: Don't you see the closing parenthesis above? :) The problem is that StackOverflow (or, actually, MarkDown) cannot interpret URLs contaning closing parentheses (naturally).
Andreas Rejbrand
Apparently the link has been fixed. Thank you, Rob Kennedy.
Servaas
Ah ok, didn't know that. I'm new to StackOverflow :). I'll keep that in mind.
Jens Mühlenhoff
+8  A: 

The three first arguments of a function are given in EAX, EDX, and ECX, respectively. Additional arguments are pushed on the stack. For a method of an object, the Self pointer is always the (invisible) first parameter. The result should be in EAX. For functions returning long strings, the (invisible) last parameter of the function is the pointer to the resulting string (which by itself is a pointer to the first character of the string).

EBX must not be altered (unless you restore it before the end of the procedure/function), and so must not ESP, EBP, ESI, or EDI either.(1) An excellent introduction to Delphi inline ASM used to be found here: http://www.delphi3000.com/articles/article_3766.asp

Andreas Rejbrand
This is the right information. And if the result is a Int64, it's located in EAX/EDX couple.
A.Bouchez
@A.Bouchez: Very true.
Andreas Rejbrand
Actually, what's in the registers depends on the calling convention. Nothing forbids to write "stdcall" and "cdecl" assembler functions, which do not use the registers to pass parameters. Also what's in the register, what's on the stack (and what's in the FPU register) depends on the parameters type.EBX, ESI, EDI can be altered inside the procedure, as long as they are saved and restored properly. Of course touching EBP and ESP is far more dangerous.
ldsandon
@Idsandon: True, but the OP did write "I am assuming the default [register] calling convention of Delphi."
Andreas Rejbrand
Too bad that delphi3000.com keeps responding with "500-13 - Server too busy". That leaves us with Google's [cached version](http://webcache.googleusercontent.com/search?q=cache:Z_4MYWLHengJ:www.delphi3000.com/articles/article_3766.asp).
Servaas
@Rejbrand: remember that "Real, method-pointer, variant, Int64, and structured types do not qualify as register parameters". Thereby even if you are using the default calling conventions, what you find in EAX, ECX, and EDX depends on the parameters type anyway.
ldsandon
@Idsandon: Yes, I know all that (been working with it quite a lot). But I didn't feel to write a book here, since the question did not mention function arguments at all.
Andreas Rejbrand
So why start an answer telling how parameter are passed (incorrectly)?
ldsandon
@Idsandon: I did not give any incorrect information; I just did not write about all the details concerning non-simple data types. The registers I specified are the default ones, used with simple types (integers and friends). (I sought long before I could find that information, so I just wanted to help out.) But of course non-simple types are slightly more complicated. For example, it is almost self-evident that an arbitrary record will not fit in a 32-bit register. But my info is still useful, for *the pointer* to the record fits in such a register. Are you trying to be a pain in my ...?
Andreas Rejbrand
To add another detail - floating point values are passed to a function via the stack, returned from a function in ST(0) under Delphi's default calling convention.
PhiS