22 ******************************************************************************
33 * Xenia : Xbox 360 Emulator Research Project *
44 ******************************************************************************
5- * Copyright 2019 Ben Vanik. All rights reserved. *
5+ * Copyright 2025 Ben Vanik. All rights reserved. *
66 * Released under the BSD license - see LICENSE in the root for more details. *
77 ******************************************************************************
88 */
99
1010#include " xenia/cpu/backend/x64/x64_backend.h"
1111
12- #include < stddef.h>
1312#include < algorithm>
13+ #include < cstddef>
1414#include " third_party/capstone/include/capstone/capstone.h"
1515#include " third_party/capstone/include/capstone/x86.h"
1616
@@ -629,6 +629,7 @@ void* X64HelperEmitter::EmitCurrentForOffsets(const _code_offsets& code_offsets,
629629 return fn;
630630}
631631HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk () {
632+ #ifdef XE_PLATFORM_WIN32
632633 // rcx = target
633634 // rdx = arg0 (context)
634635 // r8 = arg1 (guest return address)
@@ -666,6 +667,53 @@ HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
666667 mov (rdx, qword[rsp + 8 * 2 ]);
667668 mov (r8, qword[rsp + 8 * 3 ]);
668669 ret ();
670+ #elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC
671+ // System-V ABI args:
672+ // rdi = target
673+ // rsi = arg0 (context)
674+ // rdx = arg1 (guest return address)
675+
676+ struct _code_offsets {
677+ size_t prolog;
678+ size_t prolog_stack_alloc;
679+ size_t body;
680+ size_t epilog;
681+ size_t tail;
682+ } code_offsets = {};
683+
684+ const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
685+
686+ code_offsets.prolog = getSize ();
687+ // rsp + 0 = return address
688+ mov (qword[rsp + 8 * 3 ], rdx);
689+ mov (qword[rsp + 8 * 2 ], rsi);
690+ mov (qword[rsp + 8 * 1 ], rdi);
691+ sub (rsp, stack_size);
692+
693+ code_offsets.prolog_stack_alloc = getSize ();
694+ code_offsets.body = getSize ();
695+
696+ // Save nonvolatile registers.
697+ EmitSaveNonvolatileRegs ();
698+
699+ mov (rax, rdi);
700+ // mov(rsi, rsi); // context
701+ mov (rdi, ptr[rsi + offsetof (ppc::PPCContext, virtual_membase)]); // membase
702+ mov (rcx, rdx); // return address
703+ call (rax);
704+
705+ EmitLoadNonvolatileRegs ();
706+
707+ code_offsets.epilog = getSize ();
708+
709+ add (rsp, stack_size);
710+ mov (rdi, qword[rsp + 8 * 1 ]);
711+ mov (rsi, qword[rsp + 8 * 2 ]);
712+ mov (rdx, qword[rsp + 8 * 3 ]);
713+ ret ();
714+ #else
715+ assert_always (" Unknown platform ABI in host to guest thunk!" );
716+ #endif
669717
670718 code_offsets.tail = getSize ();
671719
@@ -685,6 +733,7 @@ HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() {
685733}
686734
687735GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk () {
736+ #if XE_PLATFORM_WIN32
688737 // rcx = target function
689738 // rdx = arg0
690739 // r8 = arg1
@@ -716,6 +765,57 @@ GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk() {
716765
717766 add (rsp, stack_size);
718767 ret ();
768+ #elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC
769+ // This function is being called using the Microsoft ABI from CallNative
770+ // rcx = target function
771+ // rdx = arg0
772+ // r8 = arg1
773+ // r9 = arg2
774+
775+ // Must be translated to System-V ABI:
776+ // rdi = target function
777+ // rsi = arg0
778+ // rdx = arg1
779+ // rcx = arg2
780+ // r8, r9 - unused argument registers
781+
782+ struct _code_offsets {
783+ size_t prolog;
784+ size_t prolog_stack_alloc;
785+ size_t body;
786+ size_t epilog;
787+ size_t tail;
788+ } code_offsets = {};
789+
790+ const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
791+
792+ code_offsets.prolog = getSize ();
793+
794+ // rsp + 0 = return address
795+ sub (rsp, stack_size);
796+
797+ code_offsets.prolog_stack_alloc = getSize ();
798+ code_offsets.body = getSize ();
799+
800+ // Save off volatile registers.
801+ EmitSaveVolatileRegs ();
802+
803+ mov (rax, rcx); // function
804+ mov (rdi, GetContextReg ()); // context
805+ mov (rsi, rdx); // arg0
806+ mov (rdx, r8); // arg1
807+ mov (rcx, r9); // arg2
808+ call (rax);
809+
810+ EmitLoadVolatileRegs ();
811+
812+ code_offsets.epilog = getSize ();
813+
814+ add (rsp, stack_size);
815+ ret ();
816+ #else
817+ assert_always (" Unknown platform ABI in guest to host thunk!" )
818+ #endif
719819
720820 code_offsets.tail = getSize ();
721821
@@ -738,6 +838,7 @@ GuestToHostThunk X64HelperEmitter::EmitGuestToHostThunk() {
738838uint64_t ResolveFunction (void * raw_context, uint64_t target_address);
739839
740840ResolveFunctionThunk X64HelperEmitter::EmitResolveFunctionThunk () {
841+ #if XE_PLATFORM_WIN32
741842 // ebx = target PPC address
742843 // rcx = context
743844
@@ -767,6 +868,48 @@ ResolveFunctionThunk X64HelperEmitter::EmitResolveFunctionThunk() {
767868
768869 add (rsp, stack_size);
769870 jmp (rax);
871+ #elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC
872+ // Function is called with the following params:
873+ // ebx = target PPC address
874+ // rsi = context
875+
876+ // System-V ABI args:
877+ // rdi = context
878+ // rsi = target PPC address
879+
880+ struct _code_offsets {
881+ size_t prolog;
882+ size_t prolog_stack_alloc;
883+ size_t body;
884+ size_t epilog;
885+ size_t tail;
886+ } code_offsets = {};
887+ const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
888+
889+ code_offsets.prolog = getSize ();
890+
891+ // rsp + 0 = return address
892+ sub (rsp, stack_size);
893+
894+ code_offsets.prolog_stack_alloc = getSize ();
895+ code_offsets.body = getSize ();
896+
897+ // Save volatile registers
898+ EmitSaveVolatileRegs ();
899+ mov (rdi, rsi); // context
900+ mov (rsi, rbx); // target PPC address
901+ mov (rax, reinterpret_cast <uint64_t >(&ResolveFunction));
902+ call (rax);
903+
904+ EmitLoadVolatileRegs ();
905+
906+ code_offsets.epilog = getSize ();
907+
908+ add (rsp, stack_size);
909+ jmp (rax);
910+ #else
911+ assert_always (" Unknown platform ABI in resolve function!" );
912+ #endif
770913
771914 code_offsets.tail = getSize ();
772915
0 commit comments