Archived

This topic is now archived and is closed to further replies.

Willm

Calling assembly from C

Recommended Posts

Hi, Can anyone tell me what registers I should preserve when calling an external assembly (not inline) function from C++? Specifically VC6, if it makes a difference. I cant seem to find it documented anywhere. Thanks in advance..

Share this post


Link to post
Share on other sites
First, it depends of what calling convention you want to have.

Second, VC6 does no saves for you, so it''s better to save all you change and restore it at the end of the function.

Another idea is to look at the assembly of a compiled function, and see what is really done, the you can do the same

To finnish, a question : Why don''t you include the asm directly in a function in VC6 with the __asm keyword?

Hope it help.

Crazyjul

Share this post


Link to post
Share on other sites

quote:
Original post by crazyjul
To finnish, a question : Why don''t you include the asm directly in a function in VC6 with the __asm keyword?


Its a big function, and I''m using MMX. An external asm file is just more convenient here.

Share this post


Link to post
Share on other sites
quote:
Original post by Jan Wassenberg
You''re allowed to trash eax, ecx, edx; that''s it.


Thanks. No ebx then?

Share this post


Link to post
Share on other sites
From what I can gather, registers esi and edi can also be used.

In fact, according to Visual Studio .NET online docs:

///////////////////////////////////////////////////////////////
Microsoft Specific

In general, you should not assume that a register will have a given value when an __asm block begins. Register values are not guaranteed to be preserved across separate __asm blocks. If you end a block of inline code and begin another, you cannot rely on the registers in the second block to retain their values from the first block. An __asm block inherits whatever register values result from the normal flow of control.

If you use the __fastcall calling convention, the compiler passes function arguments in registers instead of on the stack. This can create problems in functions with __asm blocks because a function has no way to tell which parameter is in which register. If the function happens to receive a parameter in EAX and immediately stores something else in EAX, the original parameter is lost. In addition, you must preserve the ECX register in any function declared with __fastcall.

To avoid such register conflicts, don''t use the __fastcall convention for functions that contain an __asm block. If you specify the __fastcall convention globally with the /Gr compiler option, declare every function containing an __asm block with __cdecl or __stdcall. (The __cdecl attribute tells the compiler to use the C calling convention for that function.) If you are not compiling with /Gr, avoid declaring the function with the __fastcall attribute.

When using __asm to write assembly language in C/C++ functions, you don''t need to preserve the EAX, EBX, ECX, EDX, ESI, or EDI registers. For example, in the POWER2.C example in Writing Functions with Inline Assembly, the power2 function doesn''t preserve the value in the EAX register. However, using these registers will affect code quality because the register allocator cannot use them to store values across __asm blocks. In addition, by using EBX, ESI or EDI in inline assembly code, you force the compiler to save and restore those registers in the function prologue and epilogue.

You should preserve other registers you use (such as DS, SS, SP, BP, and flags registers) for the scope of the __asm block. You should preserve the ESP and EBP registers unless you have some reason to change them (to switch stacks, for example). Also see Optimizing Inline Assembly.

Note If your inline assembly code changes the direction flag using the STD or CLD instructions, you must restore the flag to its original value.
END Microsoft Specific
//////////////////////////////////////////////////////////////

Hope this helps.... And remember, this is coming from the
horses mouth...

Beware however, this pertains to MS VC++ 6.0... I''ve no idea
how other compilers enforce their rules on register usage.

By the way, if you need to use more registers (I''m currently
trying to write a asm function which may have to use EIGHT
registers)... simply preserve them onto the stack before u
use them.

Peace!

Share this post


Link to post
Share on other sites
quote:
Original post by TerrorFLOP
From what I can gather, registers esi and edi can also be used.



Thanks TerrorFLOP, but that just applies to inline assembly. I''m linking in a obj file created from an external asm file.

Share this post


Link to post
Share on other sites
quote:
I''m currently trying to write a asm function which may have to use EIGHT registers)... simply preserve them onto the stack before u use them.

Heh, that should prove interesting

> Thanks. No ebx then?
Correct. If the function is large, I''d just do a pushad; if you need to return something, update the value of eax on the stack before popad.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
hm. do like this:

__asm {
pusha

// Do whatever you''d like in here


popa
}


The only regiser you don''t wanna mess with is the program counter ... basicly you can use eax, ebx, ecx, edx, edi, esi .. plus the MMX registers, the SIMD registers and the float stack... Note that then using floats, avoid using MMX, because MMX uses the float stack to store the registers, so that means it will push&pop the stack all the time and that''s not fast. So either do float, or do MMX. I''m not sure about other architectures...

Albert

Share this post


Link to post
Share on other sites
I've never written intel assembly, but I've done plenty of other architectures, and in all of them, it was the callee's responsibility to save the register, not the caller.

ie, for MIPS, you'd do this

asm void AssemblyFunctionToCall(void)
{
addiu sp, sp, -12 /* make room on the stack */
sw s0, 0(sp) /* store regiser s0 and s1 */
sw s1, 4(sp)
sw ra, 8(sp) /* store return address */

/* now you can use s0 and s1 */

/* restore the registers and return */
lw ra, 8(sp)
lw s1, 4(sp)
lw s0, 0(sp)
addiu sp, sp, 12
jr ra
nop
}



[edited by - beernutts on April 3, 2004 1:05:37 PM]

Share this post


Link to post
Share on other sites