| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376 |
- /*
- AngelCode Scripting Library
- Copyright (c) 2003-2014 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]
- */
- //
- // as_callfunc_x86.cpp
- //
- // These functions handle the actual calling of system functions
- //
- // Added support for functor methods by Jordi Oliveras Rovira in April, 2014.
- //
- #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"
- #include "as_context.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
- // Prototypes
- asQWORD CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
- asQWORD CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
- asQWORD CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
- asQWORD CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
- asQWORD CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
- asQWORD CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr);
- asQWORD CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func);
- asQWORD CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func);
- asQWORD CallThisCallFunctionRetByRef(const void *, const asDWORD *, int, asFUNCTION_t, void *retPtr);
- asDWORD GetReturnedFloat();
- asQWORD GetReturnedDouble();
- asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/)
- {
- asCScriptEngine *engine = context->m_engine;
- asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
- asQWORD retQW = 0;
- // Prepare the parameters
- asDWORD paramBuffer[64];
- int callConv = sysFunc->callConv;
- #ifdef AS_NO_THISCALL_FUNCTOR_METHOD
- int paramSize = sysFunc->paramSize;
- if( sysFunc->takesObjByVal )
- {
- paramSize = 0;
- int spos = 0;
- int dpos = 1;
- #else
- // Unpack the two object pointers
- void **objectsPtrs = (void**)obj;
- void *secondObject = objectsPtrs[1];
- obj = objectsPtrs[0];
- // Changed because need check for ICC_THISCALL_OBJFIRST or
- // ICC_THISCALL_OBJLAST if sysFunc->takesObjByVal (avoid copy code)
- // Check if is THISCALL_OBJ* calling convention (in this case needs to add secondObject pointer into stack).
- bool isThisCallMethod = callConv >= ICC_THISCALL_OBJLAST;
- int paramSize = isThisCallMethod || sysFunc->takesObjByVal ? 0 : sysFunc->paramSize;
- int dpos = 1;
- if( isThisCallMethod &&
- (callConv >= ICC_THISCALL_OBJFIRST &&
- callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) )
- {
- // Add the object pointer as the first parameter
- paramBuffer[dpos++] = (asDWORD)secondObject;
- paramSize++;
- }
- if( sysFunc->takesObjByVal || isThisCallMethod )
- {
- int spos = 0;
- #endif // AS_NO_THISCALL_FUNCTOR_METHOD
- 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
- // TODO: bug: Must call the object's copy constructor instead of doing a memcpy,
- // as the object may hold a pointer to itself. It's not enough to
- // change only this memcpy as the assembler routine also makes a copy
- // of paramBuffer to the final stack location. To avoid the second
- // copy the C++ routine should point paramBuffer to the final stack
- // position and copy the values directly to that location. The assembler
- // routines then don't need to copy anything, and will just be
- // responsible for setting up the registers and the stack frame appropriately.
- 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];
- }
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- if( isThisCallMethod &&
- (callConv >= ICC_THISCALL_OBJLAST &&
- callConv <= ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM) )
- {
- // Add the object pointer as the last parameter
- paramBuffer[dpos++] = (asDWORD)secondObject;
- paramSize++;
- }
- #endif // AS_NO_THISCALL_FUNCTOR_METHOD
- // Make the actual call
- asFUNCTION_t func = sysFunc->func;
- if( sysFunc->hostReturnInMemory )
- callConv++;
- switch( callConv )
- {
- case ICC_CDECL:
- retQW = CallCDeclFunction(args, paramSize<<2, func);
- break;
- case ICC_CDECL_RETURNINMEM:
- retQW = CallCDeclFunctionRetByRef(args, paramSize<<2, func, retPointer);
- break;
- case ICC_STDCALL:
- retQW = CallSTDCallFunction(args, paramSize<<2, func);
- break;
- case ICC_STDCALL_RETURNINMEM:
- // Push the return pointer on the stack
- paramSize++;
- args--;
- *(asPWORD*)args = (size_t)retPointer;
- retQW = CallSTDCallFunction(args, paramSize<<2, func);
- break;
- case ICC_THISCALL:
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- case ICC_THISCALL_OBJFIRST:
- case ICC_THISCALL_OBJLAST:
- #endif
- retQW = CallThisCallFunction(obj, args, paramSize<<2, func);
- break;
- case ICC_THISCALL_RETURNINMEM:
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- case ICC_THISCALL_OBJFIRST_RETURNINMEM:
- case ICC_THISCALL_OBJLAST_RETURNINMEM:
- #endif
- retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, func, retPointer);
- break;
- case ICC_VIRTUAL_THISCALL:
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- case ICC_VIRTUAL_THISCALL_OBJFIRST:
- case ICC_VIRTUAL_THISCALL_OBJLAST:
- #endif
- {
- // Get virtual function table from the object pointer
- asFUNCTION_t *vftable = *(asFUNCTION_t**)obj;
- retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[FuncPtrToUInt(func)>>2]);
- }
- break;
- case ICC_VIRTUAL_THISCALL_RETURNINMEM:
- #ifndef AS_NO_THISCALL_FUNCTOR_METHOD
- case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM:
- case ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM:
- #endif
- {
- // 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 = CallCDeclFunctionObjLast(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 = CallCDeclFunctionObjFirst(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
- asQWORD NOINLINE CallCDeclFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- // It is not possible to rely on ESP or BSP to refer to variables or arguments on the stack
- // depending on compiler settings BSP may not even be used, and the ESP is not always on the
- // same offset from the local variables. Because the code adjusts the ESP register it is not
- // possible to inform the arguments through symbolic names below.
- // It's not also not possible to rely on the memory layout of the function arguments, because
- // on some compiler versions and settings the arguments may be copied to local variables with a
- // different ordering before they are accessed by the rest of the code.
- // I'm copying the arguments into this array where I know the exact memory layout. The address
- // of this array will then be passed to the inline asm in the EDX register.
- volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(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
- // Copy all arguments to the stack and call the function
- "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"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallCDeclFunctionObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(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"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallCDeclFunctionObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(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"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallCDeclFunctionRetByRefObjFirst(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(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"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallCDeclFunctionRetByRef(const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- // return value in EAX or EAX:EDX
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(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"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallCDeclFunctionRetByRefObjLast(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(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"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallSTDCallFunction(const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(args), asPWORD(paramSize), asPWORD(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"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallThisCallFunction(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(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
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- "pushl %%ecx \n" // push obj on the stack
- #endif
- "call *12(%%ebx) \n"
- #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
- "addl 8(%%ebx), %%esp \n" // pop arguments
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- "addl $4, %%esp \n" // pop obj
- #endif
- #endif
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- asQWORD NOINLINE CallThisCallFunctionRetByRef(const void *obj, const asDWORD *args, int paramSize, asFUNCTION_t func, void *retPtr)
- {
- volatile asQWORD retQW = 0;
- #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
- // Copy return value from EAX:EDX
- lea ecx, retQW
- mov [ecx], eax
- mov 4[ecx], edx
- // Restore registers
- pop ecx
- }
- #elif defined ASM_AT_N_T
- volatile asPWORD a[] = {asPWORD(obj), asPWORD(args), asPWORD(paramSize), asPWORD(func), asPWORD(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"
- #if defined(__MINGW32__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || __GNUC__ > 4)
- // MinGW made some strange choices with 4.7, and the thiscall calling convention
- // when returning an object in memory is completely different from when not returning
- // in memory
- "pushl 0(%%ebx) \n" // push obj on the stack
- "movl 16(%%ebx), %%ecx \n" // move the return pointer into ECX
- "call *12(%%ebx) \n" // call the function
- #else
- "movl 0(%%ebx), %%ecx \n" // move obj into ECX
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- "pushl %%ecx \n" // push obj on the stack
- #endif
- "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
- #ifndef THISCALL_CALLEE_POPS_ARGUMENTS
- "addl 8(%%ebx), %%esp \n" // pop arguments
- #ifdef THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK
- "addl $4, %%esp \n" // pop the object pointer
- #endif
- #endif
- #endif // MINGW
- // Pop the alignment bytes
- "popl %%esp \n"
- "popl %%ebx \n"
- // Copy EAX:EDX to retQW. As the stack pointer has been
- // restored it is now safe to access the local variable
- "leal %1, %%ecx \n"
- "movl %%eax, 0(%%ecx) \n"
- "movl %%edx, 4(%%ecx) \n"
- : // output
- : "d"(a), "m"(retQW) // input - pass pointer of args in edx, pass pointer of retQW in memory argument
- : "%eax", "%ecx" // clobber
- );
- #endif
- return retQW;
- }
- 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
|