diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index 9767d66c7..778e0105d 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -109,18 +109,18 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { sizeof(XMA_CONTEXT_DATA) * kContextCount, 256, kSystemHeapPhysical); context_data_last_ptr_ = context_data_first_ptr_ + (sizeof(XMA_CONTEXT_DATA) * kContextCount - 1); - registers_.context_array_ptr = context_data_first_ptr_; + register_file_[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32 = + context_data_first_ptr_; // Setup XMA contexts. for (int i = 0; i < kContextCount; ++i) { - uint32_t guest_ptr = - registers_.context_array_ptr + i * sizeof(XMA_CONTEXT_DATA); + uint32_t guest_ptr = context_data_first_ptr_ + i * sizeof(XMA_CONTEXT_DATA); XmaContext& context = contexts_[i]; if (context.Setup(i, memory(), guest_ptr)) { assert_always(); } } - registers_.next_context = 1; + register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32 = 1; context_bitmap_.Resize(kContextCount); worker_running_ = true; @@ -185,11 +185,16 @@ void XmaDecoder::Shutdown() { xe::threading::Wait(worker_thread_->thread(), false); worker_thread_.reset(); - memory()->SystemHeapFree(registers_.context_array_ptr); + if (context_data_first_ptr_) { + memory()->SystemHeapFree(context_data_first_ptr_); + } + + context_data_first_ptr_ = 0; + context_data_last_ptr_ = 0; } int XmaDecoder::GetContextId(uint32_t guest_ptr) { - static_assert(sizeof(XMA_CONTEXT_DATA) == 64, "FIXME"); + static_assert_size(XMA_CONTEXT_DATA, 64); if (guest_ptr < context_data_first_ptr_ || guest_ptr > context_data_last_ptr_) { return -1; @@ -229,51 +234,70 @@ bool XmaDecoder::BlockOnContext(uint32_t guest_ptr, bool poll) { return context.Block(poll); } -// free60 may be useful here, however it looks like it's using a different -// piece of hardware: -// https://github.com/Free60Project/libxenon/blob/master/libxenon/drivers/xenon_sound/sound.c - uint32_t XmaDecoder::ReadRegister(uint32_t addr) { - uint32_t r = addr & 0xFFFF; - // 1800h is read on startup and stored -- context? buffers? - // 1818h is read during a lock? + uint32_t r = (addr & 0xFFFF) / 4; - 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) % kContextCount; + switch (r) { + default: { + if (!register_file_.GetRegisterInfo(r)) { + XELOGE("XMA: Read from unknown register (%.4X)", r); + } + } +#pragma warning(suppress : 4065) } - value = xe::byte_swap(value); - return value; + assert_true(r < XmaRegisterFile::kRegisterCount); + + // 0606h (1818h) 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 == XE_XMA_REG_CURRENT_CONTEXT_INDEX) { + // To prevent games from seeing a stuck XMA context, return a rotating + // number + uint32_t next_context_index = + register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32; + register_file_[XE_XMA_REG_CURRENT_CONTEXT_INDEX].u32 = next_context_index; + register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32 = + (next_context_index + 1) % kContextCount; + } + + return xe::byte_swap(register_file_.values[r].u32); } void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { SCOPE_profile_cpu_f("apu"); - uint32_t r = addr & 0xFFFF; + uint32_t r = (addr & 0xFFFF) / 4; value = xe::byte_swap(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 >= XE_XMA_REG_CONTEXT_KICK_0 && r <= XE_XMA_REG_CONTEXT_KICK_9) || + (r >= XE_XMA_REG_CONTEXT_LOCK_0 && r <= XE_XMA_REG_CONTEXT_LOCK_9) || + (r >= XE_XMA_REG_CONTEXT_CLEAR_0 && r <= XE_XMA_REG_CONTEXT_CLEAR_9)) { + } else { + switch (r) { + default: { + XELOGE("XMA: Write to unhandled register (%.4X): %.8X", r, value); + break; + } +#pragma warning(suppress : 4065) + } + } - if (r >= 0x1940 && r <= 0x1940 + 9 * 4) { + // 0601h (1804h) is written to with 0x02000000 and 0x03000000 around a lock + // operation + + assert_true(r < XmaRegisterFile::kRegisterCount); + register_file_.values[r].u32 = value; + + if (r >= XE_XMA_REG_CONTEXT_KICK_0 && r <= XE_XMA_REG_CONTEXT_KICK_9) { // Context kick command. // This will kick off the given hardware contexts. // Basically, this kicks the SPU and says "hey, decode that audio!" // XMAEnableContext // The context ID is a bit in the range of the entire context array. - uint32_t base_context_id = (r - 0x1940) / 4 * 32; + uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_KICK_0) * 32; for (int i = 0; value && i < 32; ++i, value >>= 1) { if (value & 1) { uint32_t context_id = base_context_id + i; @@ -284,11 +308,11 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { // Signal the decoder thread to start processing. work_event_->Set(); - } else if (r >= 0x1A40 && r <= 0x1A40 + 9 * 4) { + } else if (r >= XE_XMA_REG_CONTEXT_LOCK_0 && r <= XE_XMA_REG_CONTEXT_LOCK_9) { // Context lock command. // This requests a lock by flagging the context. // XMADisableContext - uint32_t base_context_id = (r - 0x1A40) / 4 * 32; + uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_LOCK_0) * 32; for (int i = 0; value && i < 32; ++i, value >>= 1) { if (value & 1) { uint32_t context_id = base_context_id + i; @@ -299,10 +323,11 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { // Signal the decoder thread to start processing. work_event_->Set(); - } else if (r >= 0x1A80 && r <= 0x1A80 + 9 * 4) { + } else if (r >= XE_XMA_REG_CONTEXT_CLEAR_0 && + r <= XE_XMA_REG_CONTEXT_CLEAR_9) { // Context clear command. // This will reset the given hardware contexts. - uint32_t base_context_id = (r - 0x1A80) / 4 * 32; + uint32_t base_context_id = (r - XE_XMA_REG_CONTEXT_CLEAR_0) * 32; for (int i = 0; value && i < 32; ++i, value >>= 1) { if (value & 1) { uint32_t context_id = base_context_id + i; diff --git a/src/xenia/apu/xma_decoder.h b/src/xenia/apu/xma_decoder.h index 8312b820d..228a7290d 100644 --- a/src/xenia/apu/xma_decoder.h +++ b/src/xenia/apu/xma_decoder.h @@ -15,6 +15,7 @@ #include #include "xenia/apu/xma_context.h" +#include "xenia/apu/xma_register_file.h" #include "xenia/base/bit_map.h" #include "xenia/kernel/xthread.h" #include "xenia/xbox.h" @@ -35,7 +36,9 @@ class XmaDecoder { X_STATUS Setup(kernel::KernelState* kernel_state); void Shutdown(); - uint32_t context_array_ptr() const { return registers_.context_array_ptr; } + uint32_t context_array_ptr() const { + return register_file_.values[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32; + } uint32_t AllocateContext(); void ReleaseContext(uint32_t guest_ptr); @@ -75,26 +78,7 @@ class XmaDecoder { xe::threading::Fence pause_fence_; // Signaled when worker paused. xe::threading::Fence resume_fence_; // Signaled when resume requested. - // 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 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]; - }; + XmaRegisterFile register_file_; static const uint32_t kContextCount = 320; XmaContext contexts_[kContextCount]; diff --git a/src/xenia/apu/xma_register_file.cc b/src/xenia/apu/xma_register_file.cc new file mode 100644 index 000000000..d3383b9cb --- /dev/null +++ b/src/xenia/apu/xma_register_file.cc @@ -0,0 +1,39 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/apu/xma_register_file.h" + +#include + +#include "xenia/base/math.h" + +namespace xe { +namespace apu { + +XmaRegisterFile::XmaRegisterFile() { std::memset(values, 0, sizeof(values)); } + +const XmaRegisterInfo* XmaRegisterFile::GetRegisterInfo(uint32_t index) { + switch (index) { +#define XE_XMA_REGISTER(index, type, name) \ + case index: { \ + static const XmaRegisterInfo reg_info = { \ + XmaRegisterInfo::Type::type, \ + #name, \ + }; \ + return ®_info; \ + } +#include "xenia/apu/xma_register_table.inc" +#undef XE_XMA_REGISTER + default: + return nullptr; + } +} + +} // namespace apu +} // namespace xe diff --git a/src/xenia/apu/xma_register_file.h b/src/xenia/apu/xma_register_file.h new file mode 100644 index 000000000..09154b9ad --- /dev/null +++ b/src/xenia/apu/xma_register_file.h @@ -0,0 +1,54 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_APU_XMA_REGISTER_FILE_H_ +#define XENIA_APU_XMA_REGISTER_FILE_H_ + +#include +#include + +namespace xe { +namespace apu { + +enum XmaRegister { +#define XE_XMA_REGISTER(index, type, name) XE_XMA_REG_##name = index, +#include "xenia/apu/xma_register_table.inc" +#undef XE_XMA_REGISTER +}; + +struct XmaRegisterInfo { + enum class Type { + kDword, + kFloat, + }; + Type type; + const char* name; +}; + +class XmaRegisterFile { + public: + XmaRegisterFile(); + + static const XmaRegisterInfo* GetRegisterInfo(uint32_t index); + + static const size_t kRegisterCount = (0xFFFF + 1) / 4; + union RegisterValue { + uint32_t u32; + float f32; + }; + RegisterValue values[kRegisterCount]; + + RegisterValue& operator[](int reg) { return values[reg]; } + RegisterValue& operator[](XmaRegister reg) { return values[reg]; } +}; + +} // namespace apu +} // namespace xe + +#endif // XENIA_APU_XMA_REGISTER_FILE_H_ diff --git a/src/xenia/apu/xma_register_table.inc b/src/xenia/apu/xma_register_table.inc new file mode 100644 index 000000000..49b32e26f --- /dev/null +++ b/src/xenia/apu/xma_register_table.inc @@ -0,0 +1,51 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +// This is a partial file designed to be included by other files when +// constructing various tables. + +//#define XE_XMA_REGISTER(index, type, name) + +XE_XMA_REGISTER(0x0600, kDword, CONTEXT_ARRAY_ADDRESS) + +XE_XMA_REGISTER(0x0606, kDword, CURRENT_CONTEXT_INDEX) +XE_XMA_REGISTER(0x0607, kDword, NEXT_CONTEXT_INDEX) + +XE_XMA_REGISTER(0x0650, kDword, CONTEXT_KICK_0) +XE_XMA_REGISTER(0x0651, kDword, CONTEXT_KICK_1) +XE_XMA_REGISTER(0x0652, kDword, CONTEXT_KICK_2) +XE_XMA_REGISTER(0x0653, kDword, CONTEXT_KICK_3) +XE_XMA_REGISTER(0x0654, kDword, CONTEXT_KICK_4) +XE_XMA_REGISTER(0x0655, kDword, CONTEXT_KICK_5) +XE_XMA_REGISTER(0x0656, kDword, CONTEXT_KICK_6) +XE_XMA_REGISTER(0x0657, kDword, CONTEXT_KICK_7) +XE_XMA_REGISTER(0x0658, kDword, CONTEXT_KICK_8) +XE_XMA_REGISTER(0x0659, kDword, CONTEXT_KICK_9) + +XE_XMA_REGISTER(0x0690, kDword, CONTEXT_LOCK_0) +XE_XMA_REGISTER(0x0691, kDword, CONTEXT_LOCK_1) +XE_XMA_REGISTER(0x0692, kDword, CONTEXT_LOCK_2) +XE_XMA_REGISTER(0x0693, kDword, CONTEXT_LOCK_3) +XE_XMA_REGISTER(0x0694, kDword, CONTEXT_LOCK_4) +XE_XMA_REGISTER(0x0695, kDword, CONTEXT_LOCK_5) +XE_XMA_REGISTER(0x0696, kDword, CONTEXT_LOCK_6) +XE_XMA_REGISTER(0x0697, kDword, CONTEXT_LOCK_7) +XE_XMA_REGISTER(0x0698, kDword, CONTEXT_LOCK_8) +XE_XMA_REGISTER(0x0699, kDword, CONTEXT_LOCK_9) + +XE_XMA_REGISTER(0x06A0, kDword, CONTEXT_CLEAR_0) +XE_XMA_REGISTER(0x06A1, kDword, CONTEXT_CLEAR_1) +XE_XMA_REGISTER(0x06A2, kDword, CONTEXT_CLEAR_2) +XE_XMA_REGISTER(0x06A3, kDword, CONTEXT_CLEAR_3) +XE_XMA_REGISTER(0x06A4, kDword, CONTEXT_CLEAR_4) +XE_XMA_REGISTER(0x06A5, kDword, CONTEXT_CLEAR_5) +XE_XMA_REGISTER(0x06A6, kDword, CONTEXT_CLEAR_6) +XE_XMA_REGISTER(0x06A7, kDword, CONTEXT_CLEAR_7) +XE_XMA_REGISTER(0x06A8, kDword, CONTEXT_CLEAR_8) +XE_XMA_REGISTER(0x06A9, kDword, CONTEXT_CLEAR_9)