| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218 |
- /*
- AngelCode Scripting Library
- Copyright (c) 2003-2012 Andreas Jonsson
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any
- damages arising from the use of this software.
- Permission is granted to anyone to use this software for any
- purpose, including commercial applications, and to alter it and
- redistribute it freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you
- must not claim that you wrote the original software. If you use
- this software in a product, an acknowledgment in the product
- documentation would be appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and
- must not be misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source
- distribution.
- The original version of this library can be located at:
- http://www.angelcode.com/angelscript/
- Andreas Jonsson
- [email protected]
- */
- // Modified by Lasse Öörni for Urho3D
- //
- // as_callfunc_x86.cpp
- //
- // These functions handle the actual calling of system functions
- //
- #include "as_config.h"
- #ifndef AS_MAX_PORTABILITY
- #ifdef AS_X86
- #include "as_callfunc.h"
- #include "as_scriptengine.h"
- #include "as_texts.h"
- #include "as_tokendef.h"
- BEGIN_AS_NAMESPACE
- //
- // With some compile level optimizations the functions don't clear the FPU
- // stack themselves. So we have to do it as part of calling the native functions,
- // as the compiler will not be able to predict when it is supposed to do it by
- // itself due to the dynamic nature of scripts
- //
- // - fninit clears the FPU stack and the FPU control word
- // - emms only clears the FPU stack, while preserving the FPU control word
- //
- // By default I use fninit as it seems to be what works for most people,
- // but some may find it necessary to define this as emms instead.
- //
- // TODO: Figure out when one or the other must be used, and a way to
- // configure this automatically in as_config.h
- //
- #ifndef CLEAR_FPU_STACK
- #define CLEAR_FPU_STACK fninit
- #endif
- // These macros are just to allow me to use the above macro in the GNUC style inline assembly
- #define _S(x) _TOSTRING(x)
- #define _TOSTRING(x) #x
- typedef asQWORD (*t_CallCDeclQW)(const asDWORD *, int, asFUNCTION_t);
- typedef asQWORD (*t_CallCDeclQWObj)(void *obj, const asDWORD *, int, asFUNCTION_t);
- typedef asDWORD (*t_CallCDeclRetByRef)(const asDWORD *, int, asFUNCTION_t, void *);
- typedef asDWORD (*t_CallCDeclObjRetByRef)(void *obj, const asDWORD *, int, asFUNCTION_t, void *);
- typedef asQWORD (*t_CallSTDCallQW)(const asDWORD *, int, asFUNCTION_t);
- typedef asQWORD (*t_CallThisCallQW)(const void *, const asDWORD *, int, asFUNCTION_t);
- typedef asDWORD (*t_CallThisCallRetByRef)(const void *, const asDWORD *, int, asFUNCTION_t, void *);
- // Prototypes
- void CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
- void CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
- void CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
- void CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
- void CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
- void CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
- void CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
- void CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
- void CallThisCallFunctionRetByRef_impl(const void *, const asDWORD *, int, asFUNCTION_t, void *retPtr);
- // Initialize function pointers
- const t_CallCDeclQW CallCDeclFunctionQWord = (t_CallCDeclQW)CallCDeclFunction;
- const t_CallCDeclQWObj CallCDeclFunctionQWordObjLast = (t_CallCDeclQWObj)CallCDeclFunctionObjLast;
- const t_CallCDeclQWObj CallCDeclFunctionQWordObjFirst = (t_CallCDeclQWObj)CallCDeclFunctionObjFirst;
- const t_CallCDeclRetByRef CallCDeclFunctionRetByRef = (t_CallCDeclRetByRef)CallCDeclFunctionRetByRef_impl;
- const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjLast = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjLast_impl;
- const t_CallCDeclObjRetByRef CallCDeclFunctionRetByRefObjFirst = (t_CallCDeclObjRetByRef)CallCDeclFunctionRetByRefObjFirst_impl;
- const t_CallSTDCallQW CallSTDCallFunctionQWord = (t_CallSTDCallQW)CallSTDCallFunction;
- const t_CallThisCallQW CallThisCallFunctionQWord = (t_CallThisCallQW)CallThisCallFunction;
- const t_CallThisCallRetByRef CallThisCallFunctionRetByRef = (t_CallThisCallRetByRef)CallThisCallFunctionRetByRef_impl;
- asDWORD GetReturnedFloat();
- asQWORD GetReturnedDouble();
- asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/)
- {
- asCScriptEngine *engine = context->engine;
- asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
- // This needs to be volatile, as GCC 4.2 is optimizing
- // away the assignment of the return code on Mac OS X.
- // ref: http://www.gamedev.net/topic/621357-porting-dustforce-to-os-x/
- // Urho3D: do not apply this fix on Linux, as it causes problems in release builds
- #ifndef __linux__
- volatile asQWORD retQW = 0;
- #else
- asQWORD retQW = 0;
- #endif
- // Prepare the parameters
- int paramSize = sysFunc->paramSize;
- asDWORD paramBuffer[64];
- if( sysFunc->takesObjByVal )
- {
- paramSize = 0;
- int spos = 0;
- int dpos = 1;
- for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
- {
- if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
- {
- #ifdef COMPLEX_OBJS_PASSED_BY_REF
- if( descr->parameterTypes[n].GetObjectType()->flags & COMPLEX_MASK )
- {
- paramBuffer[dpos++] = args[spos++];
- paramSize++;
- }
- else
- #endif
- {
- // Copy the object's memory to the buffer
- memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());
- // Delete the original memory
- engine->CallFree(*(char**)(args+spos));
- spos++;
- dpos += descr->parameterTypes[n].GetSizeInMemoryDWords();
- paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords();
- }
- }
- else
- {
- // Copy the value directly
- paramBuffer[dpos++] = args[spos++];
- if( descr->parameterTypes[n].GetSizeOnStackDWords() > 1 )
- paramBuffer[dpos++] = args[spos++];
- paramSize += descr->parameterTypes[n].GetSizeOnStackDWords();
- }
- }
- // Keep a free location at the beginning
- args = ¶mBuffer[1];
- }
- // Make the actual call
- asFUNCTION_t func = sysFunc->func;
- int callConv = sysFunc->callConv;
- if( sysFunc->hostReturnInMemory )
- callConv++;
- switch( callConv )
- {
- case ICC_CDECL:
- retQW = CallCDeclFunctionQWord(args, paramSize<<2, func);
- break;
- case ICC_CDECL_RETURNINMEM:
- retQW = CallCDeclFunctionRetByRef(args, paramSize<<2, func, retPointer);
- break;
- case ICC_STDCALL:
- retQW = CallSTDCallFunctionQWord(args, paramSize<<2, func);
- break;
- case ICC_STDCALL_RETURNINMEM:
- // Push the return pointer on the stack
- paramSize++;
- args--;
- *(asPWORD*)args = (size_t)retPointer;
- retQW = CallSTDCallFunctionQWord(args, paramSize<<2, func);
- break;
- case ICC_THISCALL:
- retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, func);
- break;
- case ICC_THISCALL_RETURNINMEM:
- retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, func, retPointer);
- break;
- case ICC_VIRTUAL_THISCALL:
- {
- // Get virtual function table from the object pointer
- asFUNCTION_t *vftable = *(asFUNCTION_t**)obj;
- retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2]);
- }
- break;
- case ICC_VIRTUAL_THISCALL_RETURNINMEM:
- {
- // Get virtual function table from the object pointer
- asFUNCTION_t *vftable = *(asFUNCTION_t**)obj;
- retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2], retPointer);
- }
- break;
- case ICC_CDECL_OBJLAST:
- retQW = CallCDeclFunctionQWordObjLast(obj, args, paramSize<<2, func);
- break;
- case ICC_CDECL_OBJLAST_RETURNINMEM:
- // Call the system object method as a cdecl with the obj ref as the last parameter
- retQW = CallCDeclFunctionRetByRefObjLast(obj, args, paramSize<<2, func, retPointer);
- break;
- case ICC_CDECL_OBJFIRST:
- // Call the system object method as a cdecl with the obj ref as the first parameter
- retQW = CallCDeclFunctionQWordObjFirst(obj, args, paramSize<<2, func);
- break;
- case ICC_CDECL_OBJFIRST_RETURNINMEM:
- // Call the system object method as a cdecl with the obj ref as the first parameter
- retQW = CallCDeclFunctionRetByRefObjFirst(obj, args, paramSize<<2, func, retPointer);
- break;
- default:
- context->SetInternalException(TXT_INVALID_CALLING_CONVENTION);
- }
- // If the return is a float value we need to get the value from the FP register
- if( sysFunc->hostReturnFloat )
- {
- if( sysFunc->hostReturnSize == 1 )
- *(asDWORD*)&retQW = GetReturnedFloat();
- else
- retQW = GetReturnedDouble();
- }
- return retQW;
- }
- // On GCC we need to prevent the compiler from inlining these assembler routines when
- // optimizing for speed (-O3), as the loop labels get duplicated which cause compile errors.
- #ifdef __GNUC__
- #define NOINLINE __attribute ((__noinline__))
- #else
- #define NOINLINE
- #endif
- void NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- // Call function
- call [func]
- // Pop arguments from stack
- add esp, paramSize
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- // GNUC 4.6.1 seems to have a bug when compiling with -O2. This wasn't a problem in earlier versions.
- // Even though the clobber list is specifically listing the esp register, it still doesn't understand
- // that it cannot rely on esp for getting the function arguments. So in order to work around this
- // I'm passing the address of the first arg in edx to the inline assembly, and then copy it to ebx
- // where it is guaranteed to be maintained over the function call.
- asm __volatile__(
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 4(%%ebx), %%eax \n" // paramSize
- "addl $4, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "movl 4(%%ebx), %%ecx \n" // paramSize
- "movl 0(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy \n"
- "copyloop: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop \n"
- "endcopy: \n"
- "call *8(%%ebx) \n"
- "addl 4(%%ebx), %%esp \n" // pop arguments
-
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&args) // input - pass pointer of args in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Push the object pointer as the last argument to the function
- push obj
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- // Call function
- call [func]
- // Pop arguments from stack
- add esp, paramSize
- add esp, 4
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(obj);
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 8(%%ebx), %%eax \n" // paramSize
- "addl $8, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "pushl 0(%%ebx) \n" // obj
- "movl 8(%%ebx), %%ecx \n" // paramSize
- "movl 4(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy8 \n"
- "copyloop8: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop8 \n"
- "endcopy8: \n"
- "call *12(%%ebx) \n"
- "addl 8(%%ebx), %%esp \n" // pop arguments
- "addl $4, %%esp \n" // pop obj
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&obj) // input - pass pointer of obj in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- // push object as first parameter
- push obj
- // Call function
- call [func]
- // Pop arguments from stack
- add esp, paramSize
- add esp, 4
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(obj);
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 8(%%ebx), %%eax \n" // paramSize
- "addl $8, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "movl 8(%%ebx), %%ecx \n" // paramSize
- "movl 4(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy6 \n"
- "copyloop6: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop6 \n"
- "endcopy6: \n"
- "pushl 0(%%ebx) \n" // push obj
- "call *12(%%ebx) \n"
- "addl 8(%%ebx), %%esp \n" // pop arguments
- "addl $4, %%esp \n" // pop obj
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&obj) // input - pass pointer of obj in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallCDeclFunctionRetByRefObjFirst_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- // Push the object pointer
- push obj
- // Push the return pointer
- push retPtr;
- // Call function
- call [func]
- // Pop arguments from stack
- add esp, paramSize
- #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
- // Pop the return pointer
- add esp, 8
- #else
- add esp, 4
- #endif
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(obj);
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- UNUSED_VAR(retPtr);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 8(%%ebx), %%eax \n" // paramSize
- "addl $12, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "movl 8(%%ebx), %%ecx \n" // paramSize
- "movl 4(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy5 \n"
- "copyloop5: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop5 \n"
- "endcopy5: \n"
- "pushl 0(%%ebx) \n" // push object first
- "pushl 16(%%ebx) \n" // retPtr
- "call *12(%%ebx) \n" // func
- "addl 8(%%ebx), %%esp \n" // pop arguments
- #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
- "addl $8, %%esp \n" // Pop the return pointer and object pointer
- #else
- "addl $4, %%esp \n" // Pop the object pointer
- #endif
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&obj) // input - pass pointer of obj in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallCDeclFunctionRetByRef_impl(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- // Push the return pointer
- push retPtr;
- // Call function
- call [func]
- // Pop arguments from stack
- add esp, paramSize
- #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
- // Pop the return pointer
- add esp, 4
- #endif
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- UNUSED_VAR(retPtr);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 4(%%ebx), %%eax \n" // paramSize
- "addl $8, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "movl 4(%%ebx), %%ecx \n" // paramSize
- "movl 0(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy7 \n"
- "copyloop7: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop7 \n"
- "endcopy7: \n"
- "pushl 12(%%ebx) \n" // retPtr
- "call *8(%%ebx) \n" // func
- "addl 4(%%ebx), %%esp \n" // pop arguments
- #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
- "addl $4, %%esp \n" // Pop the return pointer
- #endif
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&args) // input - pass pointer of args in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallCDeclFunctionRetByRefObjLast_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- push obj
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- // Push the return pointer
- push retPtr;
- // Call function
- call [func]
- // Pop arguments from stack
- add esp, paramSize
- add esp, 4
- #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
- // Pop the return pointer
- add esp, 4
- #endif
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(obj);
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- UNUSED_VAR(retPtr);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 8(%%ebx), %%eax \n" // paramSize
- "addl $12, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "pushl 0(%%ebx) \n" // obj
- "movl 8(%%ebx), %%ecx \n" // paramSize
- "movl 4(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy4 \n"
- "copyloop4: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop4 \n"
- "endcopy4: \n"
- "pushl 16(%%ebx) \n" // retPtr
- "call *12(%%ebx) \n" // func
- "addl 8(%%ebx), %%esp \n" // pop arguments
- #ifndef CALLEE_POPS_HIDDEN_RETURN_POINTER
- "addl $8, %%esp \n" // Pop the return pointer and object pointer
- #else
- "addl $4, %%esp \n" // Pop the object pointer
- #endif
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&obj) // input - pass pointer of obj in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- // Call function
- call [func]
- // The callee already removed parameters from the stack
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 4(%%ebx), %%eax \n" // paramSize
- "addl $4, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "movl 4(%%ebx), %%ecx \n" // paramSize
- "movl 0(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy2 \n"
- "copyloop2: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop2 \n"
- "endcopy2: \n"
- "call *8(%%ebx) \n" // callee pops the arguments
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&args) // input - pass pointer of args in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- // Push the object pointer on the stack
- push obj
- #else
- // Move object pointer to ECX
- mov ecx, obj
- #endif
- // Call function
- call [func]
- #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
- // Pop arguments
- add esp, paramSize
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- // Pop object pointer
- add esp, 4
- #endif
- #endif
- // Restore registers
- pop ecx
- // Return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(obj);
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 8(%%ebx), %%eax \n" // paramSize
- "addl $8, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "movl 8(%%ebx), %%ecx \n" // paramSize
- "movl 4(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push all arguments on the stack
- "cmp $0, %%ecx \n"
- "je endcopy1 \n"
- "copyloop1: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop1 \n"
- "endcopy1: \n"
- "movl 0(%%ebx), %%ecx \n" // move obj into ECX
- "pushl %%ecx \n" // push obj on the stack
- "call *12(%%ebx) \n"
- "addl 8(%%ebx), %%esp \n" // pop arguments
- "addl $4, %%esp \n" // pop obj
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&obj) // input - pass pointer of obj in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- void NOINLINE CallThisCallFunctionRetByRef_impl(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- #if defined ASM_INTEL
- // Copy the data to the real stack. If we fail to do
- // this we may run into trouble in case of exceptions.
- __asm
- {
- // We must save registers that are used
- push ecx
- // Clear the FPU stack, in case the called function doesn't do it by itself
- CLEAR_FPU_STACK
- // Copy arguments from script
- // stack to application stack
- mov ecx, paramSize
- mov eax, args
- add eax, ecx
- cmp ecx, 0
- je endcopy
- copyloop:
- sub eax, 4
- push dword ptr [eax]
- sub ecx, 4
- jne copyloop
- endcopy:
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- // Push the object pointer on the stack
- push obj
- #else
- // Move object pointer to ECX
- mov ecx, obj
- #endif
- // Push the return pointer
- push retPtr
- // Call function
- call [func]
- #ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER
- // Pop the return pointer
- add esp, 4
- #endif
- #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
- // Pop arguments
- add esp, paramSize
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- // Pop object pointer
- add esp, 4
- #endif
- #endif
- // Restore registers
- pop ecx
- // Return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- UNUSED_VAR(obj);
- UNUSED_VAR(args);
- UNUSED_VAR(paramSize);
- UNUSED_VAR(func);
- UNUSED_VAR(retPtr);
- asm __volatile__ (
- _S(CLEAR_FPU_STACK) "\n"
- "pushl %%ebx \n"
- "movl %%edx, %%ebx \n"
- // Need to align the stack pointer so that it is aligned to 16 bytes when making the function call.
- // It is assumed that when entering this function, the stack pointer is already aligned, so we need
- // to calculate how much we will put on the stack during this call.
- "movl 8(%%ebx), %%eax \n" // paramSize
- "addl $12, %%eax \n" // counting esp that we will push on the stack
- "movl %%esp, %%ecx \n"
- "subl %%eax, %%ecx \n"
- "andl $15, %%ecx \n"
- "movl %%esp, %%eax \n"
- "subl %%ecx, %%esp \n"
- "pushl %%eax \n" // Store the original stack pointer
- "movl 8(%%ebx), %%ecx \n" // paramSize
- "movl 4(%%ebx), %%eax \n" // args
- "addl %%ecx, %%eax \n" // push all arguments to the stack
- "cmp $0, %%ecx \n"
- "je endcopy3 \n"
- "copyloop3: \n"
- "subl $4, %%eax \n"
- "pushl (%%eax) \n"
- "subl $4, %%ecx \n"
- "jne copyloop3 \n"
- "endcopy3: \n"
- "movl 0(%%ebx), %%ecx \n" // move obj into ECX
- "pushl %%ecx \n" // push obj on the stack
- "pushl 16(%%ebx) \n" // push retPtr on the stack
- "call *12(%%ebx) \n"
- #ifndef THISCALL_CALLEE_POPS_HIDDEN_RETURN_POINTER
- "addl $4, %%esp \n" // pop return pointer
- #endif
- "addl 8(%%ebx), %%esp \n" // pop arguments
- "addl $4, %%esp \n" // pop the object pointer
- // the return pointer was popped by the callee
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- : // output
- : "d"(&obj) // input - pass pointer of obj in edx
- : "%eax", "%ecx", "%esp" // clobber
- );
- #endif
- }
- asDWORD GetReturnedFloat()
- {
- asDWORD f;
- #if defined ASM_INTEL
- // Get the float value from ST0
- __asm fstp dword ptr [f]
- #elif defined ASM_AT_N_T
- asm("fstps %0 \n" : "=m" (f));
- #endif
- return f;
- }
- asQWORD GetReturnedDouble()
- {
- asQWORD d;
- #if defined ASM_INTEL
- // Get the double value from ST0
- __asm fstp qword ptr [d]
- #elif defined ASM_AT_N_T
- asm("fstpl %0 \n" : "=m" (d));
- #endif
- return d;
- }
- END_AS_NAMESPACE
- #endif // AS_X86
- #endif // AS_MAX_PORTABILITY
|