/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2013 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/cpu/ppc/ppc_frontend.h" #include "xenia/base/atomic.h" #include "xenia/cpu/ppc/ppc_context.h" #include "xenia/cpu/ppc/ppc_emit.h" #include "xenia/cpu/ppc/ppc_opcode_info.h" #include "xenia/cpu/ppc/ppc_translator.h" #include "xenia/cpu/processor.h" namespace xe { namespace cpu { namespace ppc { void InitializeIfNeeded(); void CleanupOnShutdown(); void InitializeIfNeeded() { static bool has_initialized = false; if (has_initialized) { return; } has_initialized = true; RegisterEmitCategoryAltivec(); RegisterEmitCategoryALU(); RegisterEmitCategoryControl(); RegisterEmitCategoryFPU(); RegisterEmitCategoryMemory(); atexit(CleanupOnShutdown); } void CleanupOnShutdown() {} PPCFrontend::PPCFrontend(Processor* processor) : processor_(processor) { InitializeIfNeeded(); } PPCFrontend::~PPCFrontend() { // Force cleanup now before we deinit. translator_pool_.Reset(); } Memory* PPCFrontend::memory() const { return processor_->memory(); } // Checks the state of the global lock and sets scratch to the current MSR // value. void CheckGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) { auto global_mutex = reinterpret_cast(arg0); auto global_lock_count = reinterpret_cast(arg1); std::lock_guard lock(*global_mutex); ppc_context->scratch = *global_lock_count ? 0 : 0x8000; } // Enters the global lock. Safe to recursion. void EnterGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) { auto global_mutex = reinterpret_cast(arg0); auto global_lock_count = reinterpret_cast(arg1); global_mutex->lock(); xe::atomic_inc(global_lock_count); } // Leaves the global lock. Safe to recursion. void LeaveGlobalLock(PPCContext* ppc_context, void* arg0, void* arg1) { auto global_mutex = reinterpret_cast(arg0); auto global_lock_count = reinterpret_cast(arg1); auto new_lock_count = xe::atomic_dec(global_lock_count); assert_true(new_lock_count >= 0); global_mutex->unlock(); } bool PPCFrontend::Initialize() { void* arg0 = reinterpret_cast(&xe::global_critical_region::mutex()); void* arg1 = reinterpret_cast(&builtins_.global_lock_count); builtins_.check_global_lock = processor_->DefineBuiltin("CheckGlobalLock", CheckGlobalLock, arg0, arg1); builtins_.enter_global_lock = processor_->DefineBuiltin("EnterGlobalLock", EnterGlobalLock, arg0, arg1); builtins_.leave_global_lock = processor_->DefineBuiltin("LeaveGlobalLock", LeaveGlobalLock, arg0, arg1); return true; } bool PPCFrontend::DeclareFunction(GuestFunction* function) { // Could scan or something here. // Could also check to see if it's a well-known function type and classify // for later. // Could also kick off a precompiler, since we know it's likely the function // will be demanded soon. return true; } bool PPCFrontend::DefineFunction(GuestFunction* function, uint32_t debug_info_flags) { auto translator = translator_pool_.Allocate(this); bool result = translator->Translate(function, debug_info_flags); translator_pool_.Release(translator); return result; } } // namespace ppc } // namespace cpu } // namespace xe