code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2015 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
   andreas@angelcode.com
*/

//
// This code was adapted from as_callfunc_x64_msvc by _Vicious_ on August 20th, 2011.
//
// Added support for functor methods by Jordi Oliveras Rovira in April, 2014.
//

#include <stdio.h>

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_X64_MINGW

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_context.h"

BEGIN_AS_NAMESPACE

static asQWORD __attribute__((noinline)) CallX64(const asQWORD *args, const asQWORD *floatArgs, const int paramSize, asQWORD func)
{
	volatile asQWORD ret = 0;

	__asm__ __volatile__ (
		"# Move the parameters into registers before the rsp is modified\n"
		"mov %1, %%r10\n" // r10 = args
		"mov %2, %%r11\n" // r11 = floatArgs
		"xor %%r12, %%r12\n"
		"mov %3, %%r12d\n"
		"mov %4, %%r14\n" // r14 = func

        "# Store the stack pointer in r15 since it is guaranteed not to change over a function call\n"
        "mov %%rsp, %%r15\n"

		"# Allocate space on the stack for the arguments\n"
		"# Make room for at least 4 arguments even if there are less. When\n"
		"# the compiler does optimizations for speed it may use these for \n"
		"# temporary storage.\n"
		"mov %%r12, %%rdi\n"
		"add $32,%%edi\n"

		"# Make sure the stack pointer is 16byte aligned so the\n"
		"# whole program optimizations will work properly\n"
		"# TODO: runtime optimize: Can this be optimized with fewer instructions?\n"
		"mov %%rsp,%%rsi\n"
		"sub %%rdi,%%rsi\n"
		"and $0x8,%%rsi\n"
		"add %%rsi,%%rdi\n"
		"sub %%rdi,%%rsp\n"

		"# Jump straight to calling the function if no parameters\n"
		"cmp $0,%%r12 # Compare paramSize with 0\n"
		"je callfunc # Jump to call funtion if (paramSize == 0)\n"

		"# Copy arguments from script stack to application stack\n"
		"# Order is (first to last):\n"
		"# rcx, rdx, r8, r9 & everything else goes on stack\n"
		"movq   (%%r10),%%rcx\n"
		"movq  8(%%r10),%%rdx\n"
		"movq 16(%%r10),%%r8\n"
		"movq 24(%%r10),%%r9\n"

		"# Negate the 4 params from the size to be copied\n"
		"sub $32,%%r12d\n"
		"js copyfloat # Jump if negative result\n"
		"jz copyfloat # Jump if zero result\n"

		"# Now copy all remaining params onto stack allowing space for first four\n"
		"# params to be flushed back to the stack if required by the callee.\n"
		"add $32,%%r10 # Position input pointer 4 args ahead\n"
		"mov %%rsp,%%r13 # Put the stack pointer into r13\n"
		"add $32,%%r13 # Leave space for first 4 args on stack\n"

	"copyoverflow:\n"
		"movq (%%r10),%%rdi # Read param from source stack into rdi\n"
		"movq %%rdi,(%%r13) # Copy param to real stack\n"
		"add $8,%%r13 # Move virtual stack pointer\n"
		"add $8,%%r10 # Move source stack pointer\n"
		"sub $8,%%r12d # Decrement remaining count\n"
		"jnz copyoverflow # Continue if more params\n"

	"copyfloat:\n"
		"# Any floating point params?\n"
		"cmp $0,%%r11\n"
		"je callfunc\n"

		"movlpd   (%%r11),%%xmm0\n"
		"movlpd  8(%%r11),%%xmm1\n"
		"movlpd 16(%%r11),%%xmm2\n"
		"movlpd 24(%%r11),%%xmm3\n"

	"callfunc:\n"
		"call *%%r14\n"

        "# restore stack pointer\n"
        "mov %%r15, %%rsp\n"

		"lea  %0, %%rbx\n"     // Load the address of the ret variable into rbx
		"movq %%rax,(%%rbx)\n" // Copy the returned value into the ret variable

 		: // no output
		: "m" (ret), "r" (args), "r" (floatArgs), "r" (paramSize), "r" (func)
		: "rdi", "rsi", "rsp", "rbx", "r10", "r11", "%r12", "r13", "r14", "r15"
	);

	return ret;
}

static asDWORD GetReturnedFloat()
{
	volatile asDWORD ret = 0;

	__asm__ __volatile__ (
		"lea      %0, %%rax\n"
		"movss    %%xmm0, (%%rax)"
		: /* no output */
		: "m" (ret)
		: "%rax"
	);

	return ret;
}

static asQWORD GetReturnedDouble()
{
	volatile asQWORD ret = 0;

	__asm__ __volatile__ (
		"lea     %0, %%rax\n"
		"movlpd  %%xmm0, (%%rax)"
		: /* no optput */
		: "m" (ret)
		: "%rax"
	);

	return ret;
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject)
{
	asCScriptEngine *engine = context->m_engine;
	asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;

	asQWORD  retQW             = 0;
	void    *func              = (void*)sysFunc->func;
	asUINT   paramSize         = 0; // QWords
	void   **vftable;

	asQWORD  allArgBuffer[64];
	asQWORD  floatArgBuffer[4];

	int callConv = sysFunc->callConv;

	if( sysFunc->hostReturnInMemory )
	{
		// The return is made in memory
		callConv++;

		// Set the return pointer as the first argument
		allArgBuffer[paramSize++] = (asQWORD)retPointer;
	}

#ifdef AS_NO_THISCALL_FUNCTOR_METHOD
	if( callConv == ICC_THISCALL ||
		callConv == ICC_THISCALL_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL ||
		callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM )
#else
	// Optimization to avoid check 12 values (all ICC_ that contains THISCALL)
	if( (callConv >= ICC_THISCALL && callConv <= ICC_VIRTUAL_THISCALL_RETURNINMEM) ||
		(callConv >= ICC_THISCALL_OBJLAST && callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) )
#endif
	{
		// Add the object pointer as the first parameter
		allArgBuffer[paramSize++] = (asQWORD)obj;
	}

	if( callConv == ICC_CDECL_OBJFIRST ||
		callConv == ICC_CDECL_OBJFIRST_RETURNINMEM )
	{
		// Add the object pointer as the first parameter
		allArgBuffer[paramSize++] = (asQWORD)obj;
	}
#ifndef AS_NO_THISCALL_FUNCTOR_METHOD
	else if( callConv == ICC_THISCALL_OBJFIRST ||
		callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM )
	{
		// Add the object pointer as the first parameter
		allArgBuffer[paramSize++] = (asQWORD)secondObject;
	}
#endif

#ifdef AS_NO_THISCALL_FUNCTOR_METHOD
	if( callConv == ICC_VIRTUAL_THISCALL ||
		callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM )
#else
	if( callConv == ICC_VIRTUAL_THISCALL ||
		callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM )
#endif
	{
		// Get the true function pointer from the virtual function table
		vftable = *(void***)obj;
		func = vftable[asPWORD(func)>>3];
	}

	// Move the arguments to the buffer
	asUINT dpos = paramSize;
	asUINT spos = 0;
	for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
	{
		if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
		{
			if( descr->parameterTypes[n].GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE ||
				(descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK) )
			{
				allArgBuffer[dpos++] = *(asQWORD*)&args[spos];
				spos += AS_PTR_SIZE;
				paramSize++;
			}
			else
			{
				// Copy the object's memory to the buffer
				memcpy(&allArgBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());

				// Delete the original memory
				engine->CallFree(*(char**)(args+spos));
				spos += AS_PTR_SIZE;
				asUINT dwords = descr->parameterTypes[n].GetSizeInMemoryDWords();
				asUINT qwords = (dwords >> 1) + (dwords & 1);
				dpos      += qwords;
				paramSize += qwords;
			}
		}
		else if( descr->parameterTypes[n].GetTokenType() == ttQuestion )
		{
			// Copy the reference and the type id
			allArgBuffer[dpos++] = *(asQWORD*)&args[spos];
			spos += 2;
			allArgBuffer[dpos++] = args[spos++];
			paramSize += 2;
		}
		else
		{
			// Copy the value directly
			asUINT dwords = descr->parameterTypes[n].GetSizeOnStackDWords();
			if( dwords > 1 )
			{
				allArgBuffer[dpos] = *(asQWORD*)&args[spos];

				// Double arguments are moved to a separate buffer in order to be placed in the XMM registers,
				// though this is only done for first 4 arguments, the rest are placed on the stack
				if( paramSize < 4 && descr->parameterTypes[n].IsDoubleType() )
					floatArgBuffer[dpos] = *(asQWORD*)&args[spos];

				dpos++;
				spos += 2;
			}
			else
			{
				allArgBuffer[dpos] = args[spos];

				// Float arguments are moved to a separate buffer in order to be placed in the XMM registers,
				// though this is only done for first 4 arguments, the rest are placed on the stack
				if( paramSize < 4 && descr->parameterTypes[n].IsFloatType() )
					floatArgBuffer[dpos] = args[spos];

				dpos++;
				spos++;
			}

			paramSize++;
		}
	}

	if( callConv == ICC_CDECL_OBJLAST ||
		callConv == ICC_CDECL_OBJLAST_RETURNINMEM )
	{
		// Add the object pointer as the last parameter
		allArgBuffer[paramSize++] = (asQWORD)obj;
	}
#ifndef AS_NO_THISCALL_FUNCTOR_METHOD
	else if( callConv == ICC_THISCALL_OBJLAST ||
		callConv == ICC_THISCALL_OBJLAST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM )
	{
		// Add the object pointer as the last parameter
		allArgBuffer[paramSize++] = (asQWORD)secondObject;
	}
#endif

	retQW = CallX64(allArgBuffer, floatArgBuffer, paramSize*8, (asPWORD)func);

	// 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;
}

END_AS_NAMESPACE

#endif // AS_X64_MSVC
#endif // AS_MAX_PORTABILITY


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2015 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
   andreas@angelcode.com
*/

//
// Added support for thiscall methods by Jordi Oliveras Rovira in April, 2014.
//

#include <stdio.h>

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#ifdef AS_X64_MSVC

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_context.h"

BEGIN_AS_NAMESPACE

// These functions are implemented in as_callfunc_x64_msvc.asm
extern "C" asQWORD CallX64(const asQWORD *args, const asQWORD *floatArgs, int paramSize, asQWORD func);
extern "C" asDWORD GetReturnedFloat();
extern "C" asQWORD GetReturnedDouble();

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void *secondObject)
{
	asCScriptEngine *engine = context->m_engine;
	asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;

	asQWORD  retQW             = 0;
	void    *func              = (void*)sysFunc->func;
	asUINT   paramSize         = 0; // QWords
	void   **vftable;

	asQWORD  allArgBuffer[64];
	asQWORD  floatArgBuffer[4];

	int callConv = sysFunc->callConv;

	// Optimization to avoid check 12 values (all ICC_ that contains THISCALL)
	if( (callConv >= ICC_THISCALL && callConv <= ICC_VIRTUAL_THISCALL_RETURNINMEM) ||
		(callConv >= ICC_THISCALL_OBJLAST && callConv <= ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM) )
	{
		// Add the object pointer as the first parameter
		allArgBuffer[paramSize++] = (asQWORD)obj;
	}

	if( sysFunc->hostReturnInMemory )
	{
		// The return is made in memory
		callConv++;

		// Set the return pointer as the first argument
		allArgBuffer[paramSize++] = (asQWORD)retPointer;
	}

	if( callConv == ICC_CDECL_OBJFIRST ||
		callConv == ICC_CDECL_OBJFIRST_RETURNINMEM )
	{
		// Add the object pointer as the first parameter
		allArgBuffer[paramSize++] = (asQWORD)obj;
	}
	else if( callConv == ICC_THISCALL_OBJFIRST ||
		callConv == ICC_THISCALL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM )
	{
		// Add the object pointer as the first parameter
		allArgBuffer[paramSize++] = (asQWORD)secondObject;
	}

	if( callConv == ICC_VIRTUAL_THISCALL ||
		callConv == ICC_VIRTUAL_THISCALL_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM )
	{
		// Get the true function pointer from the virtual function table
		vftable = *(void***)obj;
		func = vftable[asPWORD(func)>>2];
	}

	// Move the arguments to the buffer
	asUINT dpos = paramSize;
	asUINT spos = 0;
	for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
	{
		asCDataType &dt = descr->parameterTypes[n];
		if( dt.IsObject() && !dt.IsObjectHandle() && !dt.IsReference() )
		{
			if( dt.GetSizeInMemoryDWords() >= AS_LARGE_OBJ_MIN_SIZE ||
				(dt.GetTypeInfo()->flags & COMPLEX_MASK) )
			{
				allArgBuffer[dpos++] = *(asQWORD*)&args[spos];
				spos += AS_PTR_SIZE;
				paramSize++;
			}
			else
			{
				// Copy the object's memory to the buffer
				memcpy(&allArgBuffer[dpos], *(void**)(args+spos), dt.GetSizeInMemoryBytes());

				// Delete the original memory
				engine->CallFree(*(char**)(args+spos));
				spos += AS_PTR_SIZE;
				asUINT dwords = dt.GetSizeInMemoryDWords();
				asUINT qwords = (dwords >> 1) + (dwords & 1);
				dpos      += qwords;
				paramSize += qwords;
			}
		}
		else if( dt.GetTokenType() == ttQuestion )
		{
			// Copy the reference and the type id
			allArgBuffer[dpos++] = *(asQWORD*)&args[spos];
			spos += 2;
			allArgBuffer[dpos++] = args[spos++];
			paramSize += 2;
		}
		else
		{
			// Copy the value directly
			asUINT dwords = dt.GetSizeOnStackDWords();
			if( dwords > 1 )
			{
				allArgBuffer[dpos] = *(asQWORD*)&args[spos];

				// Double arguments are moved to a separate buffer in order to be placed in the XMM registers,
				// though this is only done for first 4 arguments, the rest are placed on the stack
				if( paramSize < 4 && dt.IsDoubleType() )
					floatArgBuffer[dpos] = *(asQWORD*)&args[spos];

				dpos++;
				spos += 2;
			}
			else
			{
				allArgBuffer[dpos] = args[spos];

				// Float arguments are moved to a separate buffer in order to be placed in the XMM registers,
				// though this is only done for first 4 arguments, the rest are placed on the stack
				if( paramSize < 4 && dt.IsFloatType() )
					floatArgBuffer[dpos] = args[spos];

				dpos++;
				spos++;
			}

			paramSize++;
		}
	}

	if( callConv == ICC_CDECL_OBJLAST ||
		callConv == ICC_CDECL_OBJLAST_RETURNINMEM )
	{
		// Add the object pointer as the last parameter
		allArgBuffer[paramSize++] = (asQWORD)obj;
	}
	else if( callConv == ICC_THISCALL_OBJLAST ||
		callConv == ICC_THISCALL_OBJLAST_RETURNINMEM ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST ||
		callConv == ICC_VIRTUAL_THISCALL_OBJLAST_RETURNINMEM )
	{
		// Add the object pointer as the last parameter
		allArgBuffer[paramSize++] = (asQWORD)secondObject;
	}

	retQW = CallX64(allArgBuffer, floatArgBuffer, paramSize*8, (asPWORD)func);

	// 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;
}

END_AS_NAMESPACE

#endif // AS_X64_MSVC
#endif // AS_MAX_PORTABILITY

repo_name: Antidote/openrpgeditor

license: mit


code:
;
;  AngelCode Scripting Library
;  Copyright (c) 2003-2011 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
;  andreas@angelcode.com
;

.code
PUBLIC CallX64

; asQWORD CallX64(const asQWORD *args, const asQWORD *floatArgs, int paramSize, asQWORD func)

CallX64 PROC FRAME

	; PROLOG

	; We must save preserved registers that are used
	; TODO: No need to save unused registers

	push rbp
.pushreg rbp
	push rsi
.pushreg rsi
	push r11
.pushreg r11
	push rdi
.pushreg rdi
	push r12
.pushreg r12
	push r13
.pushreg r13
	push r14
.pushreg r14
	push r15
.pushreg r15
	push rbx
.pushreg rbx
	sub rsp, 050h
.allocstack 050h
	mov rbp, rsp
.setframe rbp, 0
.endprolog

	; Move function param to non-scratch register
	mov r14, r9		; r14 = function

	; Allocate space on the stack for the arguments
	; Make room for at least 4 arguments even if there are less. When
    ; the compiler does optimizations for speed it may use these for
	; temporary storage.
	mov rdi, r8
	add rdi, 32

	; Make sure the stack pointer is 16byte aligned so the
	; whole program optimizations will work properly
	; TODO: optimize: Can this be optimized with fewer instructions?
	mov rsi, rsp
	sub rsi, rdi
	and rsi, 8h
	add rdi, rsi
	sub rsp, rdi

	; Jump straight to calling the function if no parameters
	cmp r8d, 0		; Compare paramSize with 0
	je	callfunc	; Jump to call funtion if (paramSize == 0)

	; Move params to non-scratch registers
	mov rsi, rcx	; rsi = pArgs
	mov r11, rdx	; r11 = pFloatArgs (can be NULL)
	mov r12d, r8d	; r12 = paramSize

	; Copy arguments from script stack to application stack
	; Order is (first to last):
	; rcx, rdx, r8, r9 & everything else goes on stack
	mov rcx, qword ptr [rsi]
	mov rdx, qword ptr [rsi + 8]
	mov r8,  qword ptr [rsi + 16]
	mov r9,  qword ptr [rsi + 24]

	; Negate the 4 params from the size to be copied
	sub r12d, 32
	js  copyfloat	; Jump if negative result
	jz	copyfloat	; Jump if zero result

	; Now copy all remaining params onto stack allowing space for first four
	; params to be flushed back to the stack if required by the callee.

	add rsi, 32		; Position input pointer 4 args ahead
	mov r13, rsp	; Put the stack pointer into r13
	add r13, 32	 	; Leave space for first 4 args on stack

copyoverflow:
	mov r15, qword ptr [rsi]	; Read param from source stack into r15
	mov qword ptr [r13], r15	; Copy param to real stack
	add r13, 8					; Move virtual stack pointer
	add rsi, 8					; Move source stack pointer
	sub r12d, 8					; Decrement remaining count
	jnz copyoverflow			; Continue if more params

copyfloat:
	; Any floating point params?
	cmp r11, 0
	je  callfunc

	movlpd xmm0, qword ptr [r11]
	movlpd xmm1, qword ptr [r11 + 8]
	movlpd xmm2, qword ptr [r11 + 16]
	movlpd xmm3, qword ptr [r11 + 24]

callfunc:

	; Call function
	call r14

	; Restore the stack
	mov rsp, rbp

	; EPILOG: Restore stack & preserved registers
	add rsp, 050h
	pop rbx
	pop r15
	pop r14
	pop r13
	pop r12
	pop rdi
	pop r11
	pop rsi
	pop rbp

	; return value in RAX
	ret

CallX64 ENDP

PUBLIC GetReturnedFloat

; asDWORD GetReturnedFloat()

GetReturnedFloat PROC FRAME

	; PROLOG: Store registers and allocate stack space

	sub rsp, 8   ; We'll need 4 bytes for temporary storage (8 bytes with alignment)
.allocstack 8
.endprolog

	; Move the float value from the XMM0 register to RAX register
	movss dword ptr [rsp], xmm0
	mov   eax, dword ptr [rsp]

	; EPILOG: Clean up

	add rsp, 8

	ret

GetReturnedFloat ENDP

PUBLIC GetReturnedDouble

; asDWORD GetReturnedDouble()

GetReturnedDouble PROC FRAME

	; PROLOG: Store registers and allocate stack space

	sub rsp, 8	; We'll need 8 bytes for temporary storage
.allocstack 8
.endprolog

	; Move the double value from the XMM0 register to the RAX register
	movlpd qword ptr [rsp], xmm0
	mov    rax, qword ptr [rsp]

	; EPILOG: Clean up

	add rsp, 8

	ret

GetReturnedDouble ENDP

END


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2018 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
   andreas@angelcode.com
*/

//
// 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*/, void *secondObject)
{
	asCScriptEngine            *engine    = context->m_engine;
	asSSystemFunctionInterface *sysFunc   = descr->sysFuncIntf;

	asQWORD retQW = 0;

	// Prepare the parameters
	asDWORD paramBuffer[64];
	int callConv = sysFunc->callConv;

	// 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;

		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].GetTypeInfo()->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(&paramBuffer[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 = &paramBuffer[1];
	}

	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++;
	}

	// 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:
	case ICC_THISCALL_OBJFIRST:
	case ICC_THISCALL_OBJLAST:
		retQW = CallThisCallFunction(obj, args, paramSize<<2, func);
		break;

	case ICC_THISCALL_RETURNINMEM:
	case ICC_THISCALL_OBJFIRST_RETURNINMEM:
	case ICC_THISCALL_OBJLAST_RETURNINMEM:
		retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, func, retPointer);
		break;

	case ICC_VIRTUAL_THISCALL:
	case ICC_VIRTUAL_THISCALL_OBJFIRST:
	case ICC_VIRTUAL_THISCALL_OBJLAST:
		{
			// 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:
	case ICC_VIRTUAL_THISCALL_OBJFIRST_RETURNINMEM:
	case ICC_VIRTUAL_THISCALL_OBJLAST_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 = 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__(
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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__ (
#ifdef __OPTIMIZE__
		// When compiled with optimizations the stack unwind doesn't work properly,
		// causing exceptions to crash the application. By adding this prologue
		// and the epilogue below, the stack unwind works as it should.
		// TODO: runtime optimize: The prologue/epilogue shouldn't be needed if the correct cfi directives are used below
		"pushl %%ebp               \n"
		".cfi_adjust_cfa_offset 4  \n"
		".cfi_rel_offset ebp, 0    \n"
		"movl %%esp, %%ebp         \n"
		".cfi_def_cfa_register ebp \n"
#endif
		_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"
#ifdef AS_MINGW47
		// MinGW made some strange choices with 4.7 and the thiscall calling convention,
		// 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 // AS_MINGW47
		// Pop the alignment bytes
		"popl  %%esp           \n"
		"popl  %%ebx           \n"
#ifdef __OPTIMIZE__
		// Epilogue
		"movl %%ebp, %%esp         \n"
		".cfi_def_cfa_register esp \n"
		"popl %%ebp                \n"
		".cfi_adjust_cfa_offset -4 \n"
		".cfi_restore ebp          \n"
#endif
		// 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


code:
/*
   AngelCode Scripting Library
   Copyright (c) 2003-2015 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
   andreas@angelcode.com
*/

//
// as_callfunc_xenon.cpp
//
// These functions handle the actual calling of system functions
//
// This version is Xenon specific
// Modified from as_callfunc_ppc.cpp by Laszlo Perneky February 2007
//
// Modified by Cyril Tissier March 2010:
// various fixes in 'float' args passing / function return
// properly handling 'double' type
// various fixes in asm ppcFunc
// fix for variable arguments
//
// Modified by Anthony Clark May 2015
// Fixed the issue where int64 and uint64 could not be passed nativly
// few minor fixes within asm ppcFunc to handle int64 and uint64

// XBox 360 calling convention
// ===========================
// I've yet to find an official document with the ABI for XBox 360,
// but I'll describe what I've gathered from the code and tests
// performed by the AngelScript community.
//
// Arguments are passed in the following registers:
// r3  - r10   : integer/pointer arguments (each register is 64bit)
// fr1 - fr13  : float/double arguments    (each register is 64bit)
//
// Arguments that don't fit in the registers will be pushed on the stack.
//
// When a float or double is passed as argument, its value will be placed
// in the next available float register, but it will also reserve general
// purpose register.
//
// Example: void foo(float a, int b). a will be passed in fr1 and b in r4.
//
// For each argument passed to a function an 8byte slot is reserved on the
// stack, so that the function can offload the value there if needed. The
// first slot is at r1+20, the next at r1+28, etc.
//
// If the function is a class method, the this pointer is passed as hidden
// first argument. If the function returns an object in memory, the address
// for that memory is passed as hidden first argument.
//
// Return value are placed in the following registers:
// r3  : integer/pointer values
// fr1 : float/double values
//
// Rules for registers
// r1          : stack pointer
// r14-r31     : nonvolatile, i.e. their values must be preserved
// fr14-fr31   : nonvolatile, i.e. their values must be preserved
// r0, r2, r13 : dedicated. I'm not sure what it means, but it is probably best not to use them
//
// The stack pointer must always be aligned at 8 bytes.
//
// References:
// https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF77852569970071B0D6/$file/eabi_app.pdf
//
// TODO: The code doesn't handle objects passed by value (unless they are max 4 bytes in size)

#include "as_config.h"

#ifndef AS_MAX_PORTABILITY
#if defined(AS_XENON)

#include "as_callfunc.h"
#include "as_scriptengine.h"
#include "as_texts.h"
#include "as_tokendef.h"
#include "as_context.h"

#include <stdio.h>
#include <stdlib.h>
#include <xtl.h>

BEGIN_AS_NAMESPACE

#define AS_PPC_MAX_ARGS         32
#define AS_PPC_THISCALL_REG     1
#define AS_PPC_RETURNINMEM_REG  1
#define AS_PPC_ENDOFARGS        1

// The array used to send values to the correct places.
// Contains a byte of argTypes to indicate the register type to load, or zero if end of arguments
enum argTypes
{
	ppcENDARG    = 0,
	ppcINTARG    = 1,
	ppcFLOATARG  = 2,
	ppcDOUBLEARG = 3
};

// Loads all data into the correct places and calls the function.
// pArgs     is the array of the argument values
// pArgTypes is an array containing a byte indicating the type (enum argTypes) for each argument.
// dwFunc    is the address of the function that will be called
asQWORD __declspec( naked ) ppcFunc(const asQWORD* pArgs, asDWORD dwFunc, const asBYTE* pArgTypes)
{
	__asm
	{
_ppcFunc:
		// Prologue
		// Read and stack the link register (return address)
		mflr    r12
		stw     r12,-8(r1)

		// Backup all non-volatile registers we use in this function
		std     r31,-10h(r1) // stack pointer for pushing arguments
		std     r27,-18h(r1) // dwFunc
		std     r26,-20h(r1) // pArgs
		std     r25,-28h(r1) // pArgTypes
		std     r24,-30h(r1) // current arg type
		std     r23,-38h(r1) // counter for used GPRs
		std     r22,-40h(r1) // counter for used float registers

		// Setup the stack frame to make room for the backup of registers
		// and the arguments that will be passed to the application function.
		// 512 bytes is enough for about 50 arguments plus backup of 8
		// TODO: Should perhaps make this dynamic based on number of arguments
		stwu	r1,-200h(r1)

//////////////////////////////////////////////////////////////////////////
// Initialize local variables
//////////////////////////////////////////////////////////////////////////

		// r31 is our pointer into the stack where the arguments will be place
		// The MSVC optimizer seems to rely on nobody copying the r1 register directly
		// so we can't just do a simple 'addi r31, r1, 14h' as the optimizer may
		// end up moving this instruction to before the update of r1 above.
		// Instead we'll read the previous stack pointer from the stack, and then
		// subtract to get the correct offset.
		lwz		r31, 0(r1)
		subi	r31, r31, 1ECh	// prev r1 - 512 + 20 = curr r1 + 20

		mr r26, r3			// pArgs
		mr r27, r4			// dwFunc
		mr r25, r5			// pArgTypes

		// Counting of used/assigned GPR's
		sub  r23, r23, r23
		// Counting of used/assigned Float Registers
		sub  r22, r22, r22

		// Begin loading and stacking registers
		subi r25, r25, 1

//////////////////////////////////////////////////////////////////////////
// Fetch the next argument
//////////////////////////////////////////////////////////////////////////
ppcNextArg:
		// Increment rArgTypePtr
		addi r25, r25, 1
		// Get data type
		lbz r24, 0(r25)

		// r24 holds the data type
		cmplwi cr6, r24, 0
		beq    cr6, ppcArgsEnd
		cmplwi cr6, r24, 1
		beq    cr6, ppcArgIsInteger
		cmplwi cr6, r24, 2
		beq    cr6, ppcArgIsFloat
		cmplwi cr6, r24, 3
		beq    cr6, ppcArgIsDouble

//////////////////////////////////////////////////////////////////////////
// Load and stack integer arguments
//////////////////////////////////////////////////////////////////////////
ppcArgIsInteger:
		// Get the arg from the stack
		ld  r12, 0(r26)

		// r23 holds the integer arg count so far
		cmplwi cr6, r23, 0
		beq    cr6, ppcLoadIntReg0
		cmplwi cr6, r23, 1
		beq    cr6, ppcLoadIntReg1
		cmplwi cr6, r23, 2
		beq    cr6, ppcLoadIntReg2
		cmplwi cr6, r23, 3
		beq    cr6, ppcLoadIntReg3
		cmplwi cr6, r23, 4
		beq    cr6, ppcLoadIntReg4
		cmplwi cr6, r23, 5
		beq    cr6, ppcLoadIntReg5
		cmplwi cr6, r23, 6
		beq    cr6, ppcLoadIntReg6
		cmplwi cr6, r23, 7
		beq    cr6, ppcLoadIntReg7

		// no more than 8 parameters
		b		ppcLoadIntRegUpd

		ppcLoadIntReg0:
		mr r3, r12
		b ppcLoadIntRegUpd
		ppcLoadIntReg1:
		mr r4, r12
		b ppcLoadIntRegUpd
		ppcLoadIntReg2:
		mr r5, r12
		b ppcLoadIntRegUpd
		ppcLoadIntReg3:
		mr r6, r12
		b ppcLoadIntRegUpd
		ppcLoadIntReg4:
		mr r7, r12
		b ppcLoadIntRegUpd
		ppcLoadIntReg5:
		mr r8, r12
		b ppcLoadIntRegUpd
		ppcLoadIntReg6:
		mr r9, r12
		b ppcLoadIntRegUpd
		ppcLoadIntReg7:
		mr r10, r12
		b ppcLoadIntRegUpd

		ppcLoadIntRegUpd:
		std	    r12, 0(r31)			// push on the stack
		addi	r31, r31, 8			// inc stack by 1 reg

		addi r23, r23, 1			// Increment used int register count
		addi r26, r26, 8			// Increment pArgs
		b ppcNextArg				// Call next arg

//////////////////////////////////////////////////////////////////////////
// Load and stack float arguments
//////////////////////////////////////////////////////////////////////////
ppcArgIsFloat:
		// Get the arg from the stack
		lfs fr0, 0(r26)

		// r22 holds the float arg count so far
		cmplwi cr6, r22, 0
		beq    cr6, ppcLoadFloatReg0
		cmplwi cr6, r22, 1
		beq    cr6, ppcLoadFloatReg1
		cmplwi cr6, r22, 2
		beq    cr6, ppcLoadFloatReg2
		cmplwi cr6, r22, 3
		beq    cr6, ppcLoadFloatReg3
		cmplwi cr6, r22, 4
		beq    cr6, ppcLoadFloatReg4
		cmplwi cr6, r22, 5
		beq    cr6, ppcLoadFloatReg5
		cmplwi cr6, r22, 6
		beq    cr6, ppcLoadFloatReg6
		cmplwi cr6, r22, 7
		beq    cr6, ppcLoadFloatReg7
		cmplwi cr6, r22, 8
		beq    cr6, ppcLoadFloatReg8
		cmplwi cr6, r22, 9
		beq    cr6, ppcLoadFloatReg9
		cmplwi cr6, r22, 10
		beq    cr6, ppcLoadFloatReg10
		cmplwi cr6, r22, 11
		beq    cr6, ppcLoadFloatReg11
		cmplwi cr6, r22, 12
		beq    cr6, ppcLoadFloatReg12

		// no more than 12 parameters
		b		ppcLoadFloatRegUpd

		ppcLoadFloatReg0:
		fmr fr1, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg1:
		fmr fr2, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg2:
		fmr fr3, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg3:
		fmr fr4, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg4:
		fmr fr5, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg5:
		fmr fr6, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg6:
		fmr fr7, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg7:
		fmr fr8, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg8:
		fmr fr9, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg9:
		fmr fr10, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg10:
		fmr fr11, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg11:
		fmr fr12, fr0
		b ppcLoadFloatRegUpd
		ppcLoadFloatReg12:
		fmr fr13, fr0
		b ppcLoadFloatRegUpd

		ppcLoadFloatRegUpd:
		stfs	fr0, 0(r31)			// push on the stack
		addi	r31, r31, 8			// inc stack by 1 reg

		addi r22, r22, 1			// Increment used float register count
		addi r23, r23, 1			// Increment used int register count - a float reg eats up a GPR
		addi r26, r26, 4			// Increment pArgs
		b ppcNextArg				// Call next arg

//////////////////////////////////////////////////////////////////////////
// Load and stack double float arguments
//////////////////////////////////////////////////////////////////////////
ppcArgIsDouble:
		// Get the arg from the stack
		lfd fr0, 0(r26)

		// r22 holds the float arg count so far
		cmplwi cr6, r22, 0
		beq    cr6, ppcLoadDoubleReg0
		cmplwi cr6, r22, 1
		beq    cr6, ppcLoadDoubleReg1
		cmplwi cr6, r22, 2
		beq    cr6, ppcLoadDoubleReg2
		cmplwi cr6, r22, 3
		beq    cr6, ppcLoadDoubleReg3
		cmplwi cr6, r22, 4
		beq    cr6, ppcLoadDoubleReg4
		cmplwi cr6, r22, 5
		beq    cr6, ppcLoadDoubleReg5
		cmplwi cr6, r22, 6
		beq    cr6, ppcLoadDoubleReg6
		cmplwi cr6, r22, 7
		beq    cr6, ppcLoadDoubleReg7
		cmplwi cr6, r22, 8
		beq    cr6, ppcLoadDoubleReg8
		cmplwi cr6, r22, 9
		beq    cr6, ppcLoadDoubleReg9
		cmplwi cr6, r22, 10
		beq    cr6, ppcLoadDoubleReg10
		cmplwi cr6, r22, 11
		beq    cr6, ppcLoadDoubleReg11
		cmplwi cr6, r22, 12
		beq    cr6, ppcLoadDoubleReg12

		// no more than 12 parameters
		b      ppcLoadDoubleRegUpd

		ppcLoadDoubleReg0:
		fmr fr1, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg1:
		fmr fr2, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg2:
		fmr fr3, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg3:
		fmr fr4, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg4:
		fmr fr5, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg5:
		fmr fr6, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg6:
		fmr fr7, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg7:
		fmr fr8, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg8:
		fmr fr9, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg9:
		fmr fr10, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg10:
		fmr fr11, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg11:
		fmr fr12, fr0
		b ppcLoadDoubleRegUpd
		ppcLoadDoubleReg12:
		fmr fr13, fr0
		b ppcLoadDoubleRegUpd

		ppcLoadDoubleRegUpd:
		stfd	fr0, 0(r31)			// push on the stack
		addi	r31, r31, 8			// inc stack by 1 reg

		addi r22, r22, 1			// Increment used float register count
		addi r23, r23, 1			// Increment used int register count
		addi r26, r26, 8			// Increment pArgs
		b ppcNextArg

//////////////////////////////////////////////////////////////////////////
// Finished
//////////////////////////////////////////////////////////////////////////
ppcArgsEnd:
		// Call the function
		mtctr r27
		bctrl

		// Epilogue
		// Restore callers stack
		addi	r1, r1, 200h

		// restore all registers we used in this fct
		ld     r22,-40h(r1)
		ld     r23,-38h(r1)
		ld     r24,-30h(r1)
		ld     r25,-28h(r1)
		ld     r26,-20h(r1)
		ld     r27,-18h(r1)
		ld     r31,-10h(r1)

		// Fetch return link to caller
		lwz     r12,-8(r1)
		mtlr	r12
		blr
	}
}

asDWORD GetReturnedFloat()
{
	// This variable must be declared volatile so that the
	// compiler optimizations do not remove its initialization
	// with the fr1 register due to believing the fr1 register
	// isn't initialized.
	volatile asDWORD f;

	__asm
	{
		stfs fr1, f
	}

	return f;
}

asQWORD GetReturnedDouble()
{
	// This variable must be declared volatile so that the
	// compiler optimizations do not remove its initialization
	// with the fr1 register due to believing the fr1 register
	// isn't initialized.
	volatile asQWORD f;

	__asm
	{
		stfd fr1, f
	}

	return f;
}

// returns true if the given parameter is a 'variable argument'
inline bool IsVariableArgument( asCDataType type )
{
	return (type.GetTokenType() == ttQuestion) ? true : false;
}

asQWORD CallSystemFunctionNative(asCContext *context, asCScriptFunction *descr, void *obj, asDWORD *args, void *retPointer, asQWORD &/*retQW2*/, void */*secondObject*/)
{
	// TODO: Xenon does not yet support THISCALL_OBJFIRST/LAST

	asCScriptEngine            *engine    = context->m_engine;
	asSSystemFunctionInterface *sysFunc   = descr->sysFuncIntf;
	int                         callConv  = sysFunc->callConv;
	asQWORD                     retQW     = 0;
	void                       *func      = (void*)sysFunc->func;
	asDWORD                    *vftable;

	// Pack the arguments into an array that ppcFunc() can use to load each CPU register properly
	asBYTE  ppcArgsType[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG + AS_PPC_ENDOFARGS];
	asQWORD ppcArgs[AS_PPC_MAX_ARGS + AS_PPC_RETURNINMEM_REG + AS_PPC_THISCALL_REG];
	int     argsCnt = 0;

	// If the function returns an object in memory, we allocate the memory and put the ptr to the front (will go to r3)
	if( sysFunc->hostReturnInMemory )
	{
		ppcArgs[argsCnt] = (asDWORD)retPointer;
		ppcArgsType[argsCnt] = ppcINTARG;
		argsCnt++;
	}

	// If we have an object and it's not objectlast, then we put it as the first arg
	if ( obj &&
		 callConv != ICC_CDECL_OBJLAST &&
		 callConv != ICC_CDECL_OBJLAST_RETURNINMEM )
	{
		ppcArgs[argsCnt] = (asDWORD)obj;
		ppcArgsType[argsCnt] = ppcINTARG;
		argsCnt++;
	}

	// If the function takes any objects by value, they must be copied
	// to the stack, shifting the other arguments as necessary. paramBuffer
	// will then replace the args pointer that was received from the VM.
	// TODO: Is this really how XBox 360 passes objects by value?
	asDWORD paramBuffer[AS_PPC_MAX_ARGS];
	if( sysFunc->takesObjByVal )
	{
		int paramSize = 0;
		int spos = 0;
		int dpos = 1;

		for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
		{
			// Parameter object by value
			if( descr->parameterTypes[n].IsObject() &&
				!descr->parameterTypes[n].IsObjectHandle() &&
				!descr->parameterTypes[n].IsReference() )
			{
#ifdef COMPLEX_OBJS_PASSED_BY_REF
				if( descr->parameterTypes[n].GetTypeInfo()->flags & COMPLEX_MASK )
				{
					paramBuffer[dpos++] = args[spos++];
					paramSize++;
				}
				else
#endif
				{
					// Copy the object's memory to the buffer
					memcpy( &paramBuffer[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();
			}

			// If this was a variable argument parameter, then account for the implicit typeId
			if( IsVariableArgument( descr->parameterTypes[n] ) )
			{
				// the TypeId is just a DWORD
				paramBuffer[dpos++] = args[spos++];
				++paramSize;
			}
		}

		// Keep a free location at the beginning
		args = &paramBuffer[1];

		asASSERT( paramSize <= AS_PPC_MAX_ARGS );
	}

	const asUINT paramCount = (asUINT)descr->parameterTypes.GetLength();

	asBYTE * pCurArgType		= (asBYTE*)&ppcArgsType[argsCnt];
	asBYTE * pCurFixedArgValue	= (asBYTE*)&ppcArgs[argsCnt];
	asBYTE * pCurStackArgValue	= (asBYTE*)args;

	for( asUINT n = 0; n < paramCount; n++ )
	{
		argsCnt++;

		if (descr->parameterTypes[n].IsFloatType() && !descr->parameterTypes[n].IsReference())
		{
			*pCurArgType++ = ppcFLOATARG;

			*((float*) pCurFixedArgValue) = *((float*) pCurStackArgValue);

			pCurFixedArgValue += 4;
			pCurStackArgValue += 4;
		}
		else if (descr->parameterTypes[n].IsDoubleType() && !descr->parameterTypes[n].IsReference())
		{
			*pCurArgType++ = ppcDOUBLEARG;

			*((double*) pCurFixedArgValue) = *((double*) pCurStackArgValue);

			pCurFixedArgValue += 8;
			pCurStackArgValue += 8;
		}
		else
		{
			// TODO: The code also ignore the fact that large objects
			//       passed by value has been copied to the stack
			//       in the above loop.

			*pCurArgType++ = ppcINTARG;

			*((asQWORD*) pCurFixedArgValue) = *((asUINT*) pCurStackArgValue);

			if( !descr->parameterTypes[n].IsReference() )
			{
				// If the arg is not 4 bytes which we coppied, lets do it again the right way
				asUINT numBytes = descr->parameterTypes[n].GetSizeInMemoryBytes();
				if( numBytes == 1 )
				{
					*((asQWORD*) pCurFixedArgValue) = *((asBYTE*) pCurStackArgValue);
				}
				else if( numBytes == 2 )
				{
					*((asQWORD*) pCurFixedArgValue) = *((asWORD*) pCurStackArgValue);
				}
				else if( numBytes == 8 )
				{
					*((asQWORD*) pCurFixedArgValue) = *((asQWORD*) pCurStackArgValue);
					pCurStackArgValue += 4; // Increase our cur stack arg value by 4 bytes to = 8 total later
				}
			}

			pCurFixedArgValue += 8;
			pCurStackArgValue += 4;

			// if it is a variable argument, account for the typeId
			// implicitly add another parameter (AFTER the parameter above) for the typeId
			if( IsVariableArgument(descr->parameterTypes[n]) )
			{
				argsCnt++;

				*pCurArgType++ = ppcINTARG;

				*((int*) pCurFixedArgValue) = *((int*) pCurStackArgValue);

				pCurFixedArgValue += 4;
				pCurStackArgValue += 4;
			}
		}
	}

	// Add the arg list end indicator
	ppcArgsType[argsCnt] = ppcENDARG;

	switch( callConv )
	{
	case ICC_CDECL:
	case ICC_CDECL_RETURNINMEM:
	case ICC_STDCALL:
	case ICC_STDCALL_RETURNINMEM:
	case ICC_THISCALL:
	case ICC_THISCALL_RETURNINMEM:
	case ICC_CDECL_OBJFIRST:
	case ICC_CDECL_OBJFIRST_RETURNINMEM:
	{
		retQW = ppcFunc( ppcArgs, (asDWORD)func, ppcArgsType );
		break;
	}
	case ICC_VIRTUAL_THISCALL:
	case ICC_VIRTUAL_THISCALL_RETURNINMEM:
	{
		// Get virtual function table from the object pointer
		vftable = *(asDWORD**)obj;
		retQW = ppcFunc( ppcArgs, vftable[asDWORD(func)>>2], ppcArgsType );
		break;
	}
	case ICC_CDECL_OBJLAST:
	case ICC_CDECL_OBJLAST_RETURNINMEM:
	{
		// Add the object pointer as the last argument
		ppcArgsType[argsCnt++] = ppcINTARG;
		ppcArgsType[argsCnt] = ppcENDARG;
		*((asQWORD*)pCurFixedArgValue) = (asPWORD)obj;
		retQW = ppcFunc( ppcArgs, (asDWORD)func, ppcArgsType );
		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();
	}
	else if( sysFunc->hostReturnSize == 1 )
	{
		// Move the bits to the higher value to compensate for the adjustment that the caller does
		retQW <<= 32;
	}

	return retQW;
}

END_AS_NAMESPACE

#endif // AS_XENON
#endif // AS_MAX_PORTABILITY