From 50b0746a26447b2485ed062237378e7d52204794 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Mon, 16 Feb 2015 17:18:07 -0800 Subject: [PATCH] XMA hardware spoofing when using direct register access. This works for recent games that don't use the XMA* methods. Upcoming CLs will add the XMA* method shims forthcoming. --- src/xenia/apu/audio_system.cc | 178 +++++++++++++++++++++++-- src/xenia/apu/audio_system.h | 28 +++- src/xenia/kernel/sources.gypi | 1 + src/xenia/kernel/xboxkrnl_audio.cc | 42 +----- src/xenia/kernel/xboxkrnl_audio_xma.cc | 166 +++++++++++++++++++++++ src/xenia/kernel/xboxkrnl_memory.cc | 19 +++ src/xenia/kernel/xboxkrnl_module.cc | 1 + src/xenia/kernel/xboxkrnl_private.h | 2 + src/xenia/xbox.h | 2 + 9 files changed, 383 insertions(+), 56 deletions(-) create mode 100644 src/xenia/kernel/xboxkrnl_audio_xma.cc diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index 7c646d58d..12e77d195 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -8,28 +8,62 @@ */ #include "xenia/apu/audio_system.h" -#include "xenia/apu/audio_driver.h" #include "poly/poly.h" +#include "xenia/apu/audio_driver.h" #include "xenia/emulator.h" #include "xenia/cpu/processor.h" #include "xenia/cpu/xenon_thread_state.h" -using namespace xe; -using namespace xe::apu; +// As with normal Microsoft, there are like twelve different ways to access +// the audio APIs. Early games use XMA*() methods almost exclusively to touch +// decoders. Later games use XAudio*() and direct memory writes to the XMA +// structures (as opposed to the XMA* calls), meaning that we have to support +// both. +// +// For ease of implementation, most audio related processing is handled in +// AudioSystem, and the functions here call off to it. +// The XMA*() functions just manipulate the audio system in the guest context +// and let the normal AudioSystem handling take it, to prevent duplicate +// implementations. They can be found in xboxkrnl_audio_xma.cc +// +// XMA details: +// https://devel.nuclex.org/external/svn/directx/trunk/include/xma2defs.h +// https://github.com/gdawg/fsbext/blob/master/src/xma_header.h +// +// XAudio2 uses XMA under the covers, and seems to map with the same +// restrictions of frame/subframe/etc: +// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.xaudio2.xaudio2_buffer(v=vs.85).aspx +// +// XMA contexts are 64b in size and tight bitfields. They are in physical +// memory not usually available to games. Games will use MmMapIoSpace to get +// the 64b pointer in user memory so they can party on it. If the game doesn't +// do this, it's likely they are either passing the context to XAudio or +// using the XMA* functions. + +namespace xe { +namespace apu { + using namespace xe::cpu; +// Size of a hardware XMA context. +const uint32_t kXmaContextSize = 64; +// Total number of XMA contexts available. +const uint32_t kXmaContextCount = 320; + AudioSystem::AudioSystem(Emulator* emulator) : emulator_(emulator), memory_(emulator->memory()), running_(false) { memset(clients_, 0, sizeof(clients_)); for (size_t i = 0; i < maximum_client_count_; ++i) { - client_wait_handles_[i] = CreateEvent(NULL, TRUE, FALSE, NULL); unused_clients_.push(i); } + for (size_t i = 0; i < poly::countof(client_wait_handles_); ++i) { + client_wait_handles_[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + } } AudioSystem::~AudioSystem() { - for (size_t i = 0; i < maximum_client_count_; ++i) { + for (size_t i = 0; i < poly::countof(client_wait_handles_); ++i) { CloseHandle(client_wait_handles_[i]); } } @@ -43,12 +77,22 @@ X_STATUS AudioSystem::Setup() { reinterpret_cast(MMIOReadRegisterThunk), reinterpret_cast(MMIOWriteRegisterThunk)); + // Setup XMA contexts ptr. + registers_.xma_context_array_ptr = uint32_t( + memory()->HeapAlloc(0, kXmaContextSize * kXmaContextCount, + MEMORY_FLAG_PHYSICAL | MEMORY_FLAG_ZERO, 256)); + // Add all contexts to the free list. + for (int i = kXmaContextCount - 1; i >= 0; --i) { + xma_context_free_list_.push_back(registers_.xma_context_array_ptr + + i * kXmaContextSize); + } + registers_.next_context = 1; + // Setup worker thread state. This lets us make calls into guest code. thread_state_ = new XenonThreadState(emulator_->processor()->runtime(), 0, 16 * 1024, 0); thread_state_->set_name("Audio Worker"); - thread_block_ = - (uint32_t)memory_->HeapAlloc(0, 2048, MEMORY_FLAG_ZERO); + thread_block_ = (uint32_t)memory()->HeapAlloc(0, 2048, MEMORY_FLAG_ZERO); thread_state_->context()->r[13] = thread_block_; // Create worker thread. @@ -72,12 +116,12 @@ void AudioSystem::ThreadStart() { // Main run loop. while (running_) { - auto result = WaitForMultipleObjectsEx( - maximum_client_count_, client_wait_handles_, FALSE, INFINITE, FALSE); - if (result == WAIT_FAILED) { - DWORD err = GetLastError(); - assert_always(); - break; + auto result = + WaitForMultipleObjectsEx(DWORD(poly::countof(client_wait_handles_)), + client_wait_handles_, FALSE, INFINITE, FALSE); + if (result == WAIT_FAILED || + result == WAIT_OBJECT_0 + maximum_client_count_) { + continue; } size_t pumped = 0; @@ -121,10 +165,38 @@ void AudioSystem::Initialize() {} void AudioSystem::Shutdown() { running_ = false; + ResetEvent(client_wait_handles_[maximum_client_count_]); thread_.join(); delete thread_state_; memory()->HeapFree(thread_block_, 0); + + memory()->HeapFree(registers_.xma_context_array_ptr, 0); +} + +uint32_t AudioSystem::AllocateXmaContext() { + std::lock_guard lock(lock_); + if (xma_context_free_list_.empty()) { + // No contexts available. + return 0; + } + + auto guest_ptr = xma_context_free_list_.back(); + xma_context_free_list_.pop_back(); + auto context_ptr = memory()->Translate(guest_ptr); + + // Initialize? + + return guest_ptr; +} + +void AudioSystem::ReleaseXmaContext(uint32_t guest_ptr) { + std::lock_guard lock(lock_); + + auto context_ptr = memory()->Translate(guest_ptr); + std::memset(context_ptr, 0, kXmaContextSize); + + xma_context_free_list_.push_back(guest_ptr); } X_STATUS AudioSystem::RegisterClient(uint32_t callback, uint32_t callback_arg, @@ -176,6 +248,7 @@ void AudioSystem::UnregisterClient(size_t index) { DestroyDriver(clients_[index].driver); clients_[index] = {0}; unused_clients_.push(index); + ResetEvent(client_wait_handles_[index]); } // free60 may be useful here, however it looks like it's using a different @@ -187,11 +260,88 @@ uint64_t AudioSystem::ReadRegister(uint64_t addr) { XELOGAPU("ReadRegister(%.4X)", r); // 1800h is read on startup and stored -- context? buffers? // 1818h is read during a lock? - return 0; + + assert_true(r % 4 == 0); + uint32_t value = register_file_[r / 4]; + + // 1818 is rotating context processing # set to hardware ID of context being + // processed. + // If bit 200h is set, the locking code will possibly collide on hardware IDs + // and error out, so we should never set it (I think?). + if (r == 0x1818) { + // To prevent games from seeing a stuck XMA context, return a rotating + // number + registers_.current_context = registers_.next_context; + registers_.next_context = + (registers_.next_context + 1) % kXmaContextCount; + value = registers_.current_context; + } + + value = poly::byte_swap(value); + return value; } void AudioSystem::WriteRegister(uint64_t addr, uint64_t value) { uint32_t r = addr & 0xFFFF; + value = poly::byte_swap(uint32_t(value)); XELOGAPU("WriteRegister(%.4X, %.8X)", r, value); // 1804h is written to with 0x02000000 and 0x03000000 around a lock operation + + assert_true(r % 4 == 0); + register_file_[r / 4] = uint32_t(value); + + if (r >= 0x1940 && r <= 0x1949) { + // Context kick command. + // This will kick off the given hardware contexts. + for (int i = 0; value && i < 32; ++i) { + if (value & 1) { + uint32_t context_id = i + (r - 0x1940) * 32; + XELOGD("AudioSystem: kicking context %d", context_id); + // Games check bits 20/21 of context[0]. + // If both bits are set buffer full, otherwise room available. + // Right after a kick we always set buffers to invalid so games keep + // feeding data. + uint32_t guest_ptr = registers_.xma_context_array_ptr + context_id * kXmaContextSize; + auto context_ptr = memory()->Translate(guest_ptr); + uint32_t dword0 = poly::load_and_swap(context_ptr + 0); + bool has_valid_input = (dword0 & 0x00300000) != 0; + if (has_valid_input) { + dword0 = dword0 & ~0x00300000; + poly::store_and_swap(context_ptr + 0, dword0); + // Set output buffer to invalid. + uint32_t dword1 = poly::load_and_swap(context_ptr + 4); + dword1 = dword1 & ~0x80000000; + poly::store_and_swap(context_ptr + 4, dword1); + } + } + value >>= 1; + } + } else if (r >= 0x1A40 && r <= 0x1A49) { + // Context lock command. + // This requests a lock by flagging the context. + for (int i = 0; value && i < 32; ++i) { + if (value & 1) { + uint32_t context_id = i + (r - 0x1A40) * 32; + XELOGD("AudioSystem: set context lock %d", context_id); + // TODO(benvanik): set lock? + } + value >>= 1; + } + } else if (r >= 0x1A80 && r <= 0x1A89) { + // Context clear command. + // This will reset the given hardware contexts. + for (int i = 0; value && i < 32; ++i) { + if (value & 1) { + uint32_t context_id = i + (r - 0x1A80) * 32; + XELOGD("AudioSystem: reset context %d", context_id); + // TODO(benvanik): something? + } + value >>= 1; + } + } else { + value = value; + } } + +} // namespace apu +} // namespace xe diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index 3dd3c1ad2..e83775e22 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -35,6 +35,9 @@ class AudioSystem { virtual X_STATUS Setup(); virtual void Shutdown(); + uint32_t AllocateXmaContext(); + void ReleaseXmaContext(uint32_t guest_ptr); + X_STATUS RegisterClient(uint32_t callback, uint32_t callback_arg, size_t* out_index); void UnregisterClient(size_t index); @@ -75,6 +78,28 @@ class AudioSystem { std::mutex lock_; + // Stored little endian, accessed through 0x7FEA.... + union { + struct { + union { + struct { + uint8_t ignored0[0x1800]; + // 1800h; points to guest-space physical block of 320 contexts. + uint32_t xma_context_array_ptr; + }; + struct { + uint8_t ignored1[0x1818]; + // 1818h; current context ID. + uint32_t current_context; + // 181Ch; next context ID to process. + uint32_t next_context; + }; + }; + } registers_; + uint32_t register_file_[0xFFFF / 4]; + }; + std::vector xma_context_free_list_; + static const size_t maximum_client_count_ = 8; struct { @@ -83,7 +108,8 @@ class AudioSystem { uint32_t callback_arg; uint32_t wrapped_callback_arg; } clients_[maximum_client_count_]; - HANDLE client_wait_handles_[maximum_client_count_]; + // Last handle is always there in case we have no clients. + HANDLE client_wait_handles_[maximum_client_count_ + 1]; std::queue unused_clients_; }; diff --git a/src/xenia/kernel/sources.gypi b/src/xenia/kernel/sources.gypi index db10757d3..8a8253b60 100644 --- a/src/xenia/kernel/sources.gypi +++ b/src/xenia/kernel/sources.gypi @@ -35,6 +35,7 @@ 'xam_video.cc', 'xam_voice.cc', 'xboxkrnl_audio.cc', + 'xboxkrnl_audio_xma.cc', 'xboxkrnl_debug.cc', 'xboxkrnl_hal.cc', 'xboxkrnl_io.cc', diff --git a/src/xenia/kernel/xboxkrnl_audio.cc b/src/xenia/kernel/xboxkrnl_audio.cc index c942eed79..d1f6d348b 100644 --- a/src/xenia/kernel/xboxkrnl_audio.cc +++ b/src/xenia/kernel/xboxkrnl_audio.cc @@ -18,26 +18,6 @@ namespace xe { namespace kernel { -SHIM_CALL XMACreateContext_shim(PPCContext* ppc_state, KernelState* state) { - uint32_t context_ptr = SHIM_GET_ARG_32(0); - - XELOGD("XMACreateContext(%.8X)", context_ptr); - - // TODO(benvanik): allocate and return -- see if size required or just dummy? - // Games will call MmGetPhysicalAddress on the result. - SHIM_SET_MEM_32(context_ptr, 0xAAAABABE); - - SHIM_SET_RETURN_32(X_STATUS_SUCCESS); -} - -SHIM_CALL XMAReleaseContext_shim(PPCContext* ppc_state, KernelState* state) { - uint32_t context_ptr = SHIM_GET_ARG_32(0); - - XELOGD("XMAReleaseContext(%.8X)", context_ptr); - - // TODO(benvanik): free -} - SHIM_CALL XAudioGetSpeakerConfig_shim(PPCContext* ppc_state, KernelState* state) { uint32_t config_ptr = SHIM_GET_ARG_32(0); @@ -148,27 +128,7 @@ SHIM_CALL XAudioSubmitRenderDriverFrame_shim(PPCContext* ppc_state, void xe::kernel::xboxkrnl::RegisterAudioExports(ExportResolver* export_resolver, KernelState* state) { - SHIM_SET_MAPPING("xboxkrnl.exe", XMACreateContext, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAInitializeContext, state); - SHIM_SET_MAPPING("xboxkrnl.exe", XMAReleaseContext, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAEnableContext, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMADisableContext, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferWriteOffset, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferReadOffset, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferReadOffset, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferValid, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsOutputBufferValid, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0Valid, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer0Valid, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1Valid, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer1Valid, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetPacketMetadata, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMABlockWhileInUse, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetLoopData, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBufferReadOffset, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetInputBufferReadOffset, state); + // Additional XMA* methods are in xboxkrnl_audio_xma.cc. SHIM_SET_MAPPING("xboxkrnl.exe", XAudioGetSpeakerConfig, state); SHIM_SET_MAPPING("xboxkrnl.exe", XAudioGetVoiceCategoryVolumeChangeMask, diff --git a/src/xenia/kernel/xboxkrnl_audio_xma.cc b/src/xenia/kernel/xboxkrnl_audio_xma.cc new file mode 100644 index 000000000..0fa3865db --- /dev/null +++ b/src/xenia/kernel/xboxkrnl_audio_xma.cc @@ -0,0 +1,166 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/apu/apu.h" +#include "xenia/common.h" +#include "xenia/emulator.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xboxkrnl_private.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { + +// See audio_system.cc for implementation details. +// +// XMA details: +// https://devel.nuclex.org/external/svn/directx/trunk/include/xma2defs.h +// https://github.com/gdawg/fsbext/blob/master/src/xma_header.h +// +// XMA is undocumented, but the methods are pretty simple. +// Games do this sequence to decode (now): +// (not sure we are setting buffer validity/offsets right) +// d> XMACreateContext(20656800) +// d> XMAIsInputBuffer0Valid(000103E0) +// d> XMAIsInputBuffer1Valid(000103E0) +// d> XMADisableContext(000103E0, 0) +// d> XMABlockWhileInUse(000103E0) +// d> XMAInitializeContext(000103E0, 20008810) +// d> XMASetOutputBufferValid(000103E0) +// d> XMASetInputBuffer0Valid(000103E0) +// d> XMAEnableContext(000103E0) +// d> XMAGetOutputBufferWriteOffset(000103E0) +// d> XMAGetOutputBufferReadOffset(000103E0) +// d> XMAIsOutputBufferValid(000103E0) +// d> XMAGetOutputBufferReadOffset(000103E0) +// d> XMAGetOutputBufferWriteOffset(000103E0) +// d> XMAIsInputBuffer0Valid(000103E0) +// d> XMAIsInputBuffer1Valid(000103E0) +// d> XMAIsInputBuffer0Valid(000103E0) +// d> XMAIsInputBuffer1Valid(000103E0) +// d> XMAReleaseContext(000103E0) +// +// XAudio2 uses XMA under the covers, and seems to map with the same +// restrictions of frame/subframe/etc: +// https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.xaudio2.xaudio2_buffer(v=vs.85).aspx +// +// +//static const uint32_t kBytesPerBlock = 2048; +//static const uint32_t kSamplesPerFrame = 512; +//static const uint32_t kSamplesPerSubframe = 128; +// +//// This is unused; just for documentation of reversing. +//struct XMAContextType { +// // DWORD 0 +// uint32_t input_buffer_0_block_count : 12; // XMASetInputBuffer0, number of +// // 2KB blocks. +// uint32_t loop_count : 8; // +12bit, XMASetLoopData +// uint32_t input_buffer_0_valid : 1; // +20bit, XMAIsInputBuffer0Valid +// uint32_t input_buffer_1_valid : 1; // +21bit, XMAIsInputBuffer1Valid +// uint32_t output_buffer_block_count : 5; // +22bit +// uint32_t +// output_buffer_write_offset : 5; // +27bit, XMAGetOutputBufferWriteOffset +// +// // DWORD 1 +// uint32_t input_buffer_1_block_count : 12; // XMASetInputBuffer1, number of +// // 2KB blocks. +// uint32_t loop_subframe_end : 2; // +12bit, XMASetLoopData +// uint32_t unk_dword_1_a : 3; // ? +// uint32_t loop_subframe_skip : 3; // +17bit, XMASetLoopData +// uint32_t subframe_decode_count : 4; // +20bit +// uint32_t unk_dword_1_b : 3; // ? +// uint32_t sample_rate : 2; // +27bit +// uint32_t is_stereo : 1; // +29bit +// uint32_t unk_dword_1_c : 1; // ? +// uint32_t output_buffer_valid : 1; // +31bit, XMAIsOutputBufferValid +// +// // DWORD 2 +// uint32_t input_buffer_read_offset : 30; // XMAGetInputBufferReadOffset +// uint32_t unk_dword_2 : 2; // ? +// +// // DWORD 3 +// uint32_t loop_start : 26; // XMASetLoopData +// uint32_t unk_dword_3 : 6; // ? +// +// // DWORD 4 +// uint32_t loop_end : 26; // XMASetLoopData +// uint32_t packet_metadata : 5; // XMAGetPacketMetadata +// uint32_t current_buffer : 1; // ? +// +// // DWORD 5 +// uint32_t input_buffer_0_ptr; // top bits lopped off? +// // DWORD 6 +// uint32_t input_buffer_1_ptr; // top bits lopped off? +// // DWORD 7 +// uint32_t output_buffer_ptr; // top bits lopped off? +// // DWORD 8 +// uint32_t unk_dword_8; // Some kind of pointer like output_buffer_ptr +// +// // DWORD 9 +// uint32_t +// output_buffer_read_offset : 5; // +0bit, XMAGetOutputBufferReadOffset +// uint32_t unk_dword_9 : 27; +//}; + +SHIM_CALL XMACreateContext_shim(PPCContext* ppc_state, KernelState* state) { + uint32_t context_out_ptr = SHIM_GET_ARG_32(0); + + XELOGD("XMACreateContext(%.8X)", context_out_ptr); + + auto audio_system = state->emulator()->audio_system(); + uint32_t context_ptr = audio_system->AllocateXmaContext(); + SHIM_SET_MEM_32(context_out_ptr, context_ptr); + if (!context_ptr) { + SHIM_SET_RETURN_32(X_STATUS_NO_MEMORY); + return; + } + + SHIM_SET_RETURN_32(X_STATUS_SUCCESS); +} + +SHIM_CALL XMAReleaseContext_shim(PPCContext* ppc_state, KernelState* state) { + uint32_t context_ptr = SHIM_GET_ARG_32(0); + + XELOGD("XMAReleaseContext(%.8X)", context_ptr); + + auto audio_system = state->emulator()->audio_system(); + audio_system->ReleaseXmaContext(context_ptr); + + SHIM_SET_RETURN_32(0); +} + +} // namespace kernel +} // namespace xe + +void xe::kernel::xboxkrnl::RegisterAudioXmaExports(ExportResolver* export_resolver, + KernelState* state) { + SHIM_SET_MAPPING("xboxkrnl.exe", XMACreateContext, state); + SHIM_SET_MAPPING("xboxkrnl.exe", XMAReleaseContext, state); + + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAInitializeContext, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetLoopData, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetInputBufferReadOffset, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBufferReadOffset, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer0Valid, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0Valid, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer1Valid, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1Valid, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsOutputBufferValid, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferValid, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferReadOffset, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferReadOffset, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferWriteOffset, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetPacketMetadata, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMAEnableContext, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMADisableContext, state); + //SHIM_SET_MAPPING("xboxkrnl.exe", XMABlockWhileInUse, state); +} diff --git a/src/xenia/kernel/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl_memory.cc index 3ed9f6f74..8926ee127 100644 --- a/src/xenia/kernel/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl_memory.cc @@ -384,6 +384,24 @@ SHIM_CALL MmGetPhysicalAddress_shim(PPCContext* ppc_state, KernelState* state) { SHIM_SET_RETURN_32(base_address); } +SHIM_CALL MmMapIoSpace_shim(PPCContext* ppc_state, KernelState* state) { + uint32_t unk0 = SHIM_GET_ARG_32(0); + uint32_t src_address = SHIM_GET_ARG_32(1); // from MmGetPhysicalAddress + uint32_t size = SHIM_GET_ARG_32(2); + uint32_t flags = SHIM_GET_ARG_32(3); + + XELOGD("MmMapIoSpace(%.8X, %.8X, %d, %.8X)", unk0, src_address, size, flags); + + // I've only seen this used to map XMA audio contexts. + // The code seems fine with taking the src address, so this just returns that. + // If others start using it there could be problems. + assert_true(unk0 == 2); + assert_true(size == 0x40); + assert_true(flags == 0x404); + + SHIM_SET_RETURN_32(src_address); +} + SHIM_CALL ExAllocatePoolTypeWithTag_shim(PPCContext* ppc_state, KernelState* state) { uint32_t size = SHIM_GET_ARG_32(0); @@ -442,6 +460,7 @@ void xe::kernel::xboxkrnl::RegisterMemoryExports( SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryAllocationSize, state); SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryStatistics, state); SHIM_SET_MAPPING("xboxkrnl.exe", MmGetPhysicalAddress, state); + SHIM_SET_MAPPING("xboxkrnl.exe", MmMapIoSpace, state); SHIM_SET_MAPPING("xboxkrnl.exe", ExAllocatePoolTypeWithTag, state); SHIM_SET_MAPPING("xboxkrnl.exe", ExFreePool, state); diff --git a/src/xenia/kernel/xboxkrnl_module.cc b/src/xenia/kernel/xboxkrnl_module.cc index 20e14df5b..fcb7414d7 100644 --- a/src/xenia/kernel/xboxkrnl_module.cc +++ b/src/xenia/kernel/xboxkrnl_module.cc @@ -37,6 +37,7 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) // Register all exported functions. xboxkrnl::RegisterAudioExports(export_resolver_, kernel_state); + xboxkrnl::RegisterAudioXmaExports(export_resolver_, kernel_state); xboxkrnl::RegisterDebugExports(export_resolver_, kernel_state); xboxkrnl::RegisterHalExports(export_resolver_, kernel_state); xboxkrnl::RegisterIoExports(export_resolver_, kernel_state); diff --git a/src/xenia/kernel/xboxkrnl_private.h b/src/xenia/kernel/xboxkrnl_private.h index 89e9aeaab..4550056dc 100644 --- a/src/xenia/kernel/xboxkrnl_private.h +++ b/src/xenia/kernel/xboxkrnl_private.h @@ -21,6 +21,8 @@ class KernelState; namespace xboxkrnl { // Registration functions, one per file. void RegisterAudioExports(ExportResolver* export_resolver, KernelState* state); +void RegisterAudioXmaExports(ExportResolver* export_resolver, + KernelState* state); void RegisterDebugExports(ExportResolver* export_resolver, KernelState* state); void RegisterHalExports(ExportResolver* export_resolver, KernelState* state); void RegisterIoExports(ExportResolver* export_resolver, KernelState* state); diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 2f0c66da8..bb64a7578 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -96,6 +96,8 @@ typedef uint32_t X_RESULT; #define X_ERROR_EMPTY X_RESULT_FROM_WIN32(0x000010D2L) typedef uint32_t X_HRESULT; +#define X_E_SUCCESS static_cast(0) +#define X_E_FALSE static_cast(0x80000000L) #define X_E_INVALIDARG static_cast(0x80070057L) // MEM_*, used by NtAllocateVirtualMemory