#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
 * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
 *
 * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
 * For more information, visit https://github.com/OpenRCT2/OpenRCT2
 *
 * OpenRCT2 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * A full copy of the GNU General Public License can be found in licence.txt
 *****************************************************************************/
#pragma endregion

#include "addresses.h"

#if defined(__GNUC__)
	#ifdef __clang__
		#define DISABLE_OPT __attribute__((noinline,optnone))
	#else
		#define DISABLE_OPT __attribute__((noinline,optimize("O0")))
	#endif // __clang__
#else
#define DISABLE_OPT
#endif // defined(__GNUC__)

int DISABLE_OPT RCT2_CALLPROC_X(int address, int _eax, int _ebx, int _ecx, int _edx, int _esi, int _edi, int _ebp)
{
	int result = 0;
#ifdef PLATFORM_X86
	#ifdef _MSC_VER
	__asm {
		push ebp
		push address
		mov eax, _eax
		mov ebx, _ebx
		mov ecx, _ecx
		mov edx, _edx
		mov esi, _esi
		mov edi, _edi
		mov ebp, _ebp
		call [esp]
		lahf
		pop ebp
		pop ebp
		/* Load result with flags */
		mov result, eax
	}
	#else
	__asm__ volatile ( "\
		\n\
		push %%ebx \n\
		push %%ebp \n\
		push %[address] 	\n\
		mov %[eax], %%eax 	\n\
		mov %[ebx], %%ebx 	\n\
		mov %[ecx], %%ecx 	\n\
		mov %[edx], %%edx 	\n\
		mov %[esi], %%esi 	\n\
		mov %[edi], %%edi 	\n\
		mov %[ebp], %%ebp 	\n\
		call *(%%esp) 	\n\
		lahf \n\
		add $4, %%esp 	\n\
		pop %%ebp \n\
		pop %%ebx \n\
		/* Load result with flags */ \n\
		mov %%eax, %[result] \n\
		" : [address] "+m" (address), [eax] "+m" (_eax), [ebx] "+m" (_ebx), [ecx] "+m" (_ecx), [edx] "+m" (_edx), [esi] "+m" (_esi), [edi] "+m" (_edi), [ebp] "+m" (_ebp), [result] "+m" (result)
		:
		: "eax","ecx","edx","esi","edi","memory"
	);
	#endif
#endif // PLATFORM_X86
	// lahf only modifies ah, zero out the rest
	return result & 0xFF00;
}

int DISABLE_OPT RCT2_CALLFUNC_X(int address, int *_eax, int *_ebx, int *_ecx, int *_edx, int *_esi, int *_edi, int *_ebp)
{
	int result = 0;
#ifdef PLATFORM_X86
	#ifdef _MSC_VER
	__asm {
		// Store C's base pointer
		push ebp
		push ebx
		// Store address to call
		push address

		// Set all registers to the input values
		mov eax, [_eax]
		mov eax, [eax]
		mov ebx, [_ebx]
		mov ebx, [ebx]
		mov ecx, [_ecx]
		mov ecx, [ecx]
		mov edx, [_edx]
		mov edx, [edx]
		mov esi, [_esi]
		mov esi, [esi]
		mov edi, [_edi]
		mov edi, [edi]
		mov ebp, [_ebp]
		mov ebp, [ebp]

		// Call function
		call [esp]

		// Store output eax
		push eax
		push ebp
		push ebx
		mov ebp, [esp + 20]
		mov ebx, [esp + 16]

		// Get resulting ecx, edx, esi, edi registers

		mov eax, [_edi]
		mov [eax], edi
		mov eax, [_esi]
		mov [eax], esi
		mov eax, [_edx]
		mov [eax], edx
		mov eax, [_ecx]
		mov [eax], ecx

		// Pop ebx reg into ecx
		pop ecx
		mov eax, [_ebx]
		mov[eax], ecx

		// Pop ebp reg into ecx
		pop ecx
		mov eax, [_ebp]
		mov[eax], ecx

		pop eax
		// Get resulting eax register
		mov ecx, [_eax]
		mov [ecx], eax

		// Save flags as return in eax
		lahf
		// Pop address
		pop ebp

		pop ebx
		pop ebp
		/* Load result with flags */
		mov result, eax
	}
	#else
	__asm__ volatile ( "\
		\n\
		/* Store C's base pointer*/     \n\
		push %%ebp        \n\
		push %%ebx        \n\
		\n\
		/* Store %[address] to call*/   \n\
		push %[address]         \n\
		\n\
		/* Set all registers to the input values*/      \n\
		mov %[_eax], %%eax      \n\
		mov (%%eax), %%eax  \n\
		mov %[_ebx], %%ebx      \n\
		mov (%%ebx), %%ebx  \n\
		mov %[_ecx], %%ecx      \n\
		mov (%%ecx), %%ecx  \n\
		mov %[_edx], %%edx      \n\
		mov (%%edx), %%edx  \n\
		mov %[_esi], %%esi      \n\
		mov (%%esi), %%esi  \n\
		mov %[_edi], %%edi      \n\
		mov (%%edi), %%edi  \n\
		mov %[_ebp], %%ebp      \n\
		mov (%%ebp), %%ebp  \n\
		\n\
		/* Call function*/      \n\
		call *(%%esp)      \n\
		\n\
		/* Store output eax */ \n\
		push %%eax \n\
		push %%ebp \n\
		push %%ebx \n\
		mov 20(%%esp), %%ebp \n\
		mov 16(%%esp), %%ebx \n\
		/* Get resulting ecx, edx, esi, edi registers*/       \n\
		mov %[_edi], %%eax      \n\
		mov %%edi, (%%eax)  \n\
		mov %[_esi], %%eax      \n\
		mov %%esi, (%%eax)  \n\
		mov %[_edx], %%eax      \n\
		mov %%edx, (%%eax)  \n\
		mov %[_ecx], %%eax      \n\
		mov %%ecx, (%%eax)  \n\
		/* Pop ebx reg into ecx*/ \n\
		pop %%ecx		\n\
		mov %[_ebx], %%eax \n\
		mov %%ecx, (%%eax) \n\
		\n\
		/* Pop ebp reg into ecx */\n\
		pop %%ecx \n\
		mov %[_ebp], %%eax \n\
		mov %%ecx, (%%eax) \n\
		\n\
		pop %%eax \n\
		/* Get resulting eax register*/ \n\
		mov %[_eax], %%ecx \n\
		mov %%eax, (%%ecx) \n\
		\n\
		/* Save flags as return in eax*/  \n\
		lahf \n\
		/* Pop address*/ \n\
		pop %%ebp \n\
		\n\
		pop %%ebx \n\
		pop %%ebp \n\
		/* Load result with flags */ \n\
		mov %%eax, %[result] \n\
		" : [address] "+m" (address), [_eax] "+m" (_eax), [_ebx] "+m" (_ebx), [_ecx] "+m" (_ecx), [_edx] "+m" (_edx), [_esi] "+m" (_esi), [_edi] "+m" (_edi), [_ebp] "+m" (_ebp), [result] "+m" (result)

		:
		: "eax","ecx","edx","esi","edi","memory"
	);
	#endif
#endif // PLATFORM_X86
	// lahf only modifies ah, zero out the rest
	return result & 0xFF00;
}
