diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index c497a8f0f..b65cd4225 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -156,11 +156,12 @@ class AudioSystem { void ProcessXmaContext(XMAContext& context, XMAContextData& data); - static uint64_t MMIOReadRegisterThunk(AudioSystem* as, uint32_t addr) { + static uint64_t MMIOReadRegisterThunk(void* ppc_context, AudioSystem* as, + uint32_t addr) { return as->ReadRegister(addr); } - static void MMIOWriteRegisterThunk(AudioSystem* as, uint32_t addr, - uint64_t value) { + static void MMIOWriteRegisterThunk(void* ppc_context, AudioSystem* as, + uint32_t addr, uint64_t value) { as->WriteRegister(addr, value); } diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index e3cf5cba7..485dfe12d 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -481,9 +481,10 @@ void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0), void X64Emitter::CallNativeSafe(void* fn) { // rcx = context - // rdx = target host function + // rdx = target function // r8 = arg0 // r9 = arg1 + // r10 = arg2 mov(rdx, reinterpret_cast(fn)); auto thunk = backend()->guest_to_host_thunk(); mov(rax, reinterpret_cast(thunk)); diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index a3fc19ba1..28873b94f 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -1425,6 +1425,68 @@ EMITTER_OPCODE_TABLE( STORE_CONTEXT_V128); + + +// ============================================================================ +// OPCODE_LOAD_MMIO +// ============================================================================ +// Note: all types are always aligned in the context. +EMITTER(LOAD_MMIO_I32, MATCH(I, OffsetOp, OffsetOp>)) { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // uint64_t (context, addr) + auto mmio_range = reinterpret_cast(i.src1.value); + auto read_address = uint32_t(i.src2.value); + e.mov(e.r8, uint64_t(mmio_range->callback_context)); + e.mov(e.r9d, read_address); + e.CallNativeSafe(mmio_range->read); + e.bswap(e.eax); + e.mov(i.dest, e.eax); + if (IsTracingData()) { + e.mov(e.r8, i.dest); + e.mov(e.edx, read_address); + e.CallNative(reinterpret_cast(TraceContextLoadI32)); + } + } +}; +EMITTER_OPCODE_TABLE( + OPCODE_LOAD_MMIO, + LOAD_MMIO_I32); + + +// ============================================================================ +// OPCODE_STORE_MMIO +// ============================================================================ +// Note: all types are always aligned on the stack. +EMITTER(STORE_MMIO_I32, MATCH(I>)) { + static void Emit(X64Emitter& e, const EmitArgType& i) { + // void (context, addr, value) + auto mmio_range = reinterpret_cast(i.src1.value); + auto write_address = uint32_t(i.src2.value); + e.mov(e.r8, uint64_t(mmio_range->callback_context)); + e.mov(e.r9d, write_address); + if (i.src3.is_constant) { + e.mov(e.r10d, xe::byte_swap(i.src3.constant())); + } else { + e.mov(e.r10d, i.src3); + e.bswap(e.r10d); + } + e.CallNativeSafe(mmio_range->write); + if (IsTracingData()) { + if (i.src3.is_constant) { + e.mov(e.r8d, i.src3.constant()); + } else { + e.mov(e.r8d, i.src3); + } + e.mov(e.edx, write_address); + e.CallNative(reinterpret_cast(TraceContextStoreI32)); + } + } +}; +EMITTER_OPCODE_TABLE( + OPCODE_STORE_MMIO, + STORE_MMIO_I32); + + // ============================================================================ // OPCODE_LOAD // ============================================================================ @@ -6374,6 +6436,8 @@ void RegisterSequences() { REGISTER_EMITTER_OPCODE_TABLE(OPCODE_STORE_LOCAL); REGISTER_EMITTER_OPCODE_TABLE(OPCODE_LOAD_CONTEXT); REGISTER_EMITTER_OPCODE_TABLE(OPCODE_STORE_CONTEXT); + REGISTER_EMITTER_OPCODE_TABLE(OPCODE_LOAD_MMIO); + REGISTER_EMITTER_OPCODE_TABLE(OPCODE_STORE_MMIO); REGISTER_EMITTER_OPCODE_TABLE(OPCODE_LOAD); REGISTER_EMITTER_OPCODE_TABLE(OPCODE_STORE); REGISTER_EMITTER_OPCODE_TABLE(OPCODE_MEMSET); diff --git a/src/xenia/cpu/backend/x64/x64_thunk_emitter.cc b/src/xenia/cpu/backend/x64/x64_thunk_emitter.cc index b3e38e59a..6b2ce971e 100644 --- a/src/xenia/cpu/backend/x64/x64_thunk_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_thunk_emitter.cc @@ -97,6 +97,7 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() { // rdx = target function // r8 = arg0 // r9 = arg1 + // r10 = arg2 const size_t stack_size = StackLayout::THUNK_STACK_SIZE; // rsp + 0 = return address diff --git a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc index b8690f48d..aa25a53bd 100644 --- a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc +++ b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc @@ -174,6 +174,51 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { } break; + case OPCODE_LOAD: + if (i->src1.value->IsConstant()) { + auto memory = processor_->memory(); + auto address = i->src1.value->constant.i32; + auto mmio_range = + processor_->memory()->LookupVirtualMappedRange(address); + if (mmio_range) { + i->Replace(&OPCODE_LOAD_MMIO_info, 0); + i->src1.offset = reinterpret_cast(mmio_range); + i->src2.offset = address; + } else { + auto heap = memory->LookupHeap(address); + uint32_t protect; + if (heap->QueryProtect(address, &protect) && + !(protect & kMemoryProtectWrite)) { + // Memory is readonly - can just return the value. + switch (v->type) { + case INT32_TYPE: + v->set_constant(xe::load_and_swap( + memory->TranslateVirtual(address))); + break; + default: + assert_unhandled_case(v->type); + break; + } + i->Remove(); + } + } + } + break; + case OPCODE_STORE: + if (i->src1.value->IsConstant()) { + auto address = i->src1.value->constant.i32; + auto mmio_range = + processor_->memory()->LookupVirtualMappedRange(address); + if (mmio_range) { + auto value = i->src2.value; + i->Replace(&OPCODE_STORE_MMIO_info, 0); + i->src1.offset = reinterpret_cast(mmio_range); + i->src2.offset = address; + i->set_src3(value); + } + } + break; + case OPCODE_SELECT: if (i->src1.value->IsConstant()) { if (i->src1.value->IsConstantTrue()) { diff --git a/src/xenia/cpu/hir/hir_builder.cc b/src/xenia/cpu/hir/hir_builder.cc index 828accca0..9e9f7ee7d 100644 --- a/src/xenia/cpu/hir/hir_builder.cc +++ b/src/xenia/cpu/hir/hir_builder.cc @@ -1087,6 +1087,23 @@ void HIRBuilder::StoreContext(size_t offset, Value* value) { i->src3.value = NULL; } +Value* HIRBuilder::LoadMmio(cpu::MMIORange* mmio_range, uint32_t address, + TypeName type) { + Instr* i = AppendInstr(OPCODE_LOAD_MMIO_info, 0, AllocValue(type)); + i->src1.offset = reinterpret_cast(mmio_range); + i->src2.offset = address; + i->src3.value = NULL; + return i->dest; +} + +void HIRBuilder::StoreMmio(cpu::MMIORange* mmio_range, uint32_t address, + Value* value) { + Instr* i = AppendInstr(OPCODE_STORE_MMIO_info, 0); + i->src1.offset = reinterpret_cast(mmio_range); + i->src2.offset = address; + i->set_src3(value); +} + Value* HIRBuilder::Load(Value* address, TypeName type, uint32_t load_flags) { ASSERT_ADDRESS_TYPE(address); Instr* i = AppendInstr(OPCODE_LOAD_info, load_flags, AllocValue(type)); diff --git a/src/xenia/cpu/hir/hir_builder.h b/src/xenia/cpu/hir/hir_builder.h index e189f65c2..0d679cf68 100644 --- a/src/xenia/cpu/hir/hir_builder.h +++ b/src/xenia/cpu/hir/hir_builder.h @@ -19,6 +19,7 @@ #include "xenia/cpu/hir/label.h" #include "xenia/cpu/hir/opcodes.h" #include "xenia/cpu/hir/value.h" +#include "xenia/cpu/mmio_handler.h" namespace xe { namespace cpu { @@ -130,6 +131,9 @@ class HIRBuilder { Value* LoadContext(size_t offset, TypeName type); void StoreContext(size_t offset, Value* value); + Value* LoadMmio(cpu::MMIORange* mmio_range, uint32_t address, TypeName type); + void StoreMmio(cpu::MMIORange* mmio_range, uint32_t address, Value* value); + Value* Load(Value* address, TypeName type, uint32_t load_flags = 0); void Store(Value* address, Value* value, uint32_t store_flags = 0); void Memset(Value* address, Value* value, Value* length); diff --git a/src/xenia/cpu/hir/opcodes.h b/src/xenia/cpu/hir/opcodes.h index e904903a2..4bdb53218 100644 --- a/src/xenia/cpu/hir/opcodes.h +++ b/src/xenia/cpu/hir/opcodes.h @@ -140,6 +140,8 @@ enum Opcode { OPCODE_STORE_LOCAL, OPCODE_LOAD_CONTEXT, OPCODE_STORE_CONTEXT, + OPCODE_LOAD_MMIO, + OPCODE_STORE_MMIO, OPCODE_LOAD, OPCODE_STORE, OPCODE_MEMSET, @@ -243,6 +245,8 @@ enum OpcodeSignature { (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_V << 6), OPCODE_SIG_X_O_V_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_V << 6) | (OPCODE_SIG_TYPE_V << 9), + OPCODE_SIG_X_O_O_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) | + (OPCODE_SIG_TYPE_O << 6) | (OPCODE_SIG_TYPE_V << 9), OPCODE_SIG_X_S = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_S << 3), OPCODE_SIG_X_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3), OPCODE_SIG_X_V_L = @@ -260,6 +264,8 @@ enum OpcodeSignature { OPCODE_SIG_V = (OPCODE_SIG_TYPE_V), OPCODE_SIG_V_O = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_O << 3), OPCODE_SIG_V_V = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3), + OPCODE_SIG_V_O_O = + (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_O << 6), OPCODE_SIG_V_V_O = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_O << 6), OPCODE_SIG_V_V_O_V = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3) | diff --git a/src/xenia/cpu/hir/opcodes.inl b/src/xenia/cpu/hir/opcodes.inl index a023e21e2..a80a38e75 100644 --- a/src/xenia/cpu/hir/opcodes.inl +++ b/src/xenia/cpu/hir/opcodes.inl @@ -212,6 +212,18 @@ DEFINE_OPCODE( OPCODE_SIG_X_O_V, 0) +DEFINE_OPCODE( + OPCODE_LOAD_MMIO, + "load_mmio", + OPCODE_SIG_V_O_O, + OPCODE_FLAG_MEMORY) + +DEFINE_OPCODE( + OPCODE_STORE_MMIO, + "store_mmio", + OPCODE_SIG_X_O_O_V, + OPCODE_FLAG_MEMORY) + DEFINE_OPCODE( OPCODE_LOAD, "load", diff --git a/src/xenia/cpu/mmio_handler.cc b/src/xenia/cpu/mmio_handler.cc index 0be69a72b..4de22c4ec 100644 --- a/src/xenia/cpu/mmio_handler.cc +++ b/src/xenia/cpu/mmio_handler.cc @@ -62,11 +62,20 @@ bool MMIOHandler::RegisterRange(uint32_t virtual_address, uint32_t mask, return true; } +MMIORange* MMIOHandler::LookupRange(uint32_t virtual_address) { + for (auto& range : mapped_ranges_) { + if ((virtual_address & range.mask) == range.address) { + return ⦥ + } + } + return nullptr; +} + bool MMIOHandler::CheckLoad(uint32_t virtual_address, uint64_t* out_value) { for (const auto& range : mapped_ranges_) { if ((virtual_address & range.mask) == range.address) { - *out_value = - static_cast(range.read(range.context, virtual_address)); + *out_value = static_cast( + range.read(nullptr, range.callback_context, virtual_address)); return true; } } @@ -76,7 +85,7 @@ bool MMIOHandler::CheckLoad(uint32_t virtual_address, uint64_t* out_value) { bool MMIOHandler::CheckStore(uint32_t virtual_address, uint64_t value) { for (const auto& range : mapped_ranges_) { if ((virtual_address & range.mask) == range.address) { - range.write(range.context, virtual_address, value); + range.write(nullptr, range.callback_context, virtual_address, value); return true; } } @@ -243,7 +252,8 @@ bool MMIOHandler::HandleAccessFault(void* thread_state, if (is_load) { // Load of a memory value - read from range, swap, and store in the // register. - uint64_t value = range->read(range->context, fault_address & 0xFFFFFFFF); + uint64_t value = range->read(nullptr, range->callback_context, + fault_address & 0xFFFFFFFF); uint32_t be_reg_index; if (!xe::bit_scan_forward(arg1_type & 0xFFFF, &be_reg_index)) { be_reg_index = 0; @@ -293,7 +303,8 @@ bool MMIOHandler::HandleAccessFault(void* thread_state, value = xe::byte_swap(static_cast(value)); break; } - range->write(range->context, fault_address & 0xFFFFFFFF, value); + range->write(nullptr, range->callback_context, fault_address & 0xFFFFFFFF, + value); } else { assert_always("Unknown MMIO instruction type"); return false; diff --git a/src/xenia/cpu/mmio_handler.h b/src/xenia/cpu/mmio_handler.h index 11765f007..24b5e2416 100644 --- a/src/xenia/cpu/mmio_handler.h +++ b/src/xenia/cpu/mmio_handler.h @@ -20,12 +20,23 @@ namespace xe { namespace cpu { -typedef uint64_t (*MMIOReadCallback)(void* context, uint32_t addr); -typedef void (*MMIOWriteCallback)(void* context, uint32_t addr, uint64_t value); +typedef uint64_t (*MMIOReadCallback)(void* ppc_context, void* callback_context, + uint32_t addr); +typedef void (*MMIOWriteCallback)(void* ppc_context, void* callback_context, + uint32_t addr, uint64_t value); typedef void (*WriteWatchCallback)(void* context_ptr, void* data_ptr, uint32_t address); +struct MMIORange { + uint32_t address; + uint32_t mask; + uint32_t size; + void* callback_context; + MMIOReadCallback read; + MMIOWriteCallback write; +}; + // NOTE: only one can exist at a time! class MMIOHandler { public: @@ -38,6 +49,7 @@ class MMIOHandler { bool RegisterRange(uint32_t virtual_address, uint32_t mask, uint32_t size, void* context, MMIOReadCallback read_callback, MMIOWriteCallback write_callback); + MMIORange* LookupRange(uint32_t virtual_address); bool CheckLoad(uint32_t virtual_address, uint64_t* out_value); bool CheckStore(uint32_t virtual_address, uint64_t value); @@ -76,14 +88,6 @@ class MMIOHandler { uint8_t* virtual_membase_; uint8_t* physical_membase_; - struct MMIORange { - uint32_t address; - uint32_t mask; - uint32_t size; - void* context; - MMIOReadCallback read; - MMIOWriteCallback write; - }; std::vector mapped_ranges_; // TODO(benvanik): data structure magic. diff --git a/src/xenia/cpu/mmio_handler_win.cc b/src/xenia/cpu/mmio_handler_win.cc index 184cf1163..2c7979230 100644 --- a/src/xenia/cpu/mmio_handler_win.cc +++ b/src/xenia/cpu/mmio_handler_win.cc @@ -63,6 +63,11 @@ WinMMIOHandler::~WinMMIOHandler() { // addresses in our range and call into the registered handlers, if any. // If there are none, we continue. LONG CALLBACK MMIOExceptionHandler(PEXCEPTION_POINTERS ex_info) { + // Fast path for SetThreadName. + if (ex_info->ExceptionRecord->ExceptionCode == 0x406D1388) { + return EXCEPTION_CONTINUE_SEARCH; + } + SCOPE_profile_cpu_i("cpu", "MMIOExceptionHandler"); // http://msdn.microsoft.com/en-us/library/ms679331(v=vs.85).aspx diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.h b/src/xenia/gpu/gl4/gl4_graphics_system.h index 5cab2e560..9f3a3cd2c 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.h +++ b/src/xenia/gpu/gl4/gl4_graphics_system.h @@ -54,11 +54,12 @@ class GL4GraphicsSystem : public GraphicsSystem { uint64_t ReadRegister(uint32_t addr); void WriteRegister(uint32_t addr, uint64_t value); - static uint64_t MMIOReadRegisterThunk(GL4GraphicsSystem* gs, uint32_t addr) { + static uint64_t MMIOReadRegisterThunk(void* ppc_context, + GL4GraphicsSystem* gs, uint32_t addr) { return gs->ReadRegister(addr); } - static void MMIOWriteRegisterThunk(GL4GraphicsSystem* gs, uint32_t addr, - uint64_t value) { + static void MMIOWriteRegisterThunk(void* ppc_context, GL4GraphicsSystem* gs, + uint32_t addr, uint64_t value) { gs->WriteRegister(addr, value); } diff --git a/src/xenia/memory.cc b/src/xenia/memory.cc index b98290e9a..a56cea6d4 100644 --- a/src/xenia/memory.cc +++ b/src/xenia/memory.cc @@ -362,6 +362,10 @@ bool Memory::AddVirtualMappedRange(uint32_t virtual_address, uint32_t mask, read_callback, write_callback); } +cpu::MMIORange* Memory::LookupVirtualMappedRange(uint32_t virtual_address) { + return mmio_handler_->LookupRange(virtual_address); +} + uintptr_t Memory::AddPhysicalWriteWatch(uint32_t physical_address, uint32_t length, cpu::WriteWatchCallback callback, diff --git a/src/xenia/memory.h b/src/xenia/memory.h index 47a1a7d37..2e509dff1 100644 --- a/src/xenia/memory.h +++ b/src/xenia/memory.h @@ -195,6 +195,7 @@ class Memory { uint32_t size, void* context, cpu::MMIOReadCallback read_callback, cpu::MMIOWriteCallback write_callback); + cpu::MMIORange* LookupVirtualMappedRange(uint32_t virtual_address); uintptr_t AddPhysicalWriteWatch(uint32_t physical_address, uint32_t length, cpu::WriteWatchCallback callback,