[APU] Proper register file for XMA.

This commit is contained in:
gibbed 2019-04-19 10:45:47 -05:00
parent 1dd15d4abc
commit d0d18ff570
5 changed files with 211 additions and 58 deletions

View File

@ -109,18 +109,18 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
sizeof(XMA_CONTEXT_DATA) * kContextCount, 256, kSystemHeapPhysical); sizeof(XMA_CONTEXT_DATA) * kContextCount, 256, kSystemHeapPhysical);
context_data_last_ptr_ = context_data_last_ptr_ =
context_data_first_ptr_ + (sizeof(XMA_CONTEXT_DATA) * kContextCount - 1); 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. // Setup XMA contexts.
for (int i = 0; i < kContextCount; ++i) { for (int i = 0; i < kContextCount; ++i) {
uint32_t guest_ptr = uint32_t guest_ptr = context_data_first_ptr_ + i * sizeof(XMA_CONTEXT_DATA);
registers_.context_array_ptr + i * sizeof(XMA_CONTEXT_DATA);
XmaContext& context = contexts_[i]; XmaContext& context = contexts_[i];
if (context.Setup(i, memory(), guest_ptr)) { if (context.Setup(i, memory(), guest_ptr)) {
assert_always(); assert_always();
} }
} }
registers_.next_context = 1; register_file_[XE_XMA_REG_NEXT_CONTEXT_INDEX].u32 = 1;
context_bitmap_.Resize(kContextCount); context_bitmap_.Resize(kContextCount);
worker_running_ = true; worker_running_ = true;
@ -185,11 +185,16 @@ void XmaDecoder::Shutdown() {
xe::threading::Wait(worker_thread_->thread(), false); xe::threading::Wait(worker_thread_->thread(), false);
worker_thread_.reset(); 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) { 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_ || if (guest_ptr < context_data_first_ptr_ ||
guest_ptr > context_data_last_ptr_) { guest_ptr > context_data_last_ptr_) {
return -1; return -1;
@ -229,51 +234,70 @@ bool XmaDecoder::BlockOnContext(uint32_t guest_ptr, bool poll) {
return context.Block(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 XmaDecoder::ReadRegister(uint32_t addr) {
uint32_t r = addr & 0xFFFF; uint32_t r = (addr & 0xFFFF) / 4;
// 1800h is read on startup and stored -- context? buffers?
// 1818h is read during a lock?
assert_true(r % 4 == 0); switch (r) {
uint32_t value = register_file_[r / 4]; default: {
if (!register_file_.GetRegisterInfo(r)) {
// 1818 is rotating context processing # set to hardware ID of context being XELOGE("XMA: Read from unknown register (%.4X)", r);
// 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?). #pragma warning(suppress : 4065)
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;
} }
value = xe::byte_swap(value); assert_true(r < XmaRegisterFile::kRegisterCount);
return value;
// 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) { void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) {
SCOPE_profile_cpu_f("apu"); SCOPE_profile_cpu_f("apu");
uint32_t r = addr & 0xFFFF; uint32_t r = (addr & 0xFFFF) / 4;
value = xe::byte_swap(value); value = xe::byte_swap(value);
// 1804h is written to with 0x02000000 and 0x03000000 around a lock operation
assert_true(r % 4 == 0); if ((r >= XE_XMA_REG_CONTEXT_KICK_0 && r <= XE_XMA_REG_CONTEXT_KICK_9) ||
register_file_[r / 4] = uint32_t(value); (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. // Context kick command.
// This will kick off the given hardware contexts. // This will kick off the given hardware contexts.
// Basically, this kicks the SPU and says "hey, decode that audio!" // Basically, this kicks the SPU and says "hey, decode that audio!"
// XMAEnableContext // XMAEnableContext
// The context ID is a bit in the range of the entire context array. // 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) { for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) { if (value & 1) {
uint32_t context_id = base_context_id + i; 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. // Signal the decoder thread to start processing.
work_event_->Set(); 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. // Context lock command.
// This requests a lock by flagging the context. // This requests a lock by flagging the context.
// XMADisableContext // 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) { for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) { if (value & 1) {
uint32_t context_id = base_context_id + i; 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. // Signal the decoder thread to start processing.
work_event_->Set(); 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. // Context clear command.
// This will reset the given hardware contexts. // 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) { for (int i = 0; value && i < 32; ++i, value >>= 1) {
if (value & 1) { if (value & 1) {
uint32_t context_id = base_context_id + i; uint32_t context_id = base_context_id + i;

View File

@ -15,6 +15,7 @@
#include <queue> #include <queue>
#include "xenia/apu/xma_context.h" #include "xenia/apu/xma_context.h"
#include "xenia/apu/xma_register_file.h"
#include "xenia/base/bit_map.h" #include "xenia/base/bit_map.h"
#include "xenia/kernel/xthread.h" #include "xenia/kernel/xthread.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
@ -35,7 +36,9 @@ class XmaDecoder {
X_STATUS Setup(kernel::KernelState* kernel_state); X_STATUS Setup(kernel::KernelState* kernel_state);
void Shutdown(); 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(); uint32_t AllocateContext();
void ReleaseContext(uint32_t guest_ptr); void ReleaseContext(uint32_t guest_ptr);
@ -75,26 +78,7 @@ class XmaDecoder {
xe::threading::Fence pause_fence_; // Signaled when worker paused. xe::threading::Fence pause_fence_; // Signaled when worker paused.
xe::threading::Fence resume_fence_; // Signaled when resume requested. xe::threading::Fence resume_fence_; // Signaled when resume requested.
// Stored little endian, accessed through 0x7FEA.... XmaRegisterFile register_file_;
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];
};
static const uint32_t kContextCount = 320; static const uint32_t kContextCount = 320;
XmaContext contexts_[kContextCount]; XmaContext contexts_[kContextCount];

View File

@ -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 <cstring>
#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 &reg_info; \
}
#include "xenia/apu/xma_register_table.inc"
#undef XE_XMA_REGISTER
default:
return nullptr;
}
}
} // namespace apu
} // namespace xe

View File

@ -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 <cstdint>
#include <cstdlib>
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_

View File

@ -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)