139 lines
4.9 KiB
C++
139 lines
4.9 KiB
C++
/**
|
|
******************************************************************************
|
|
* 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/thread_state.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#include "xenia/base/assert.h"
|
|
#include "xenia/base/logging.h"
|
|
#include "xenia/base/threading.h"
|
|
#include "xenia/cpu/processor.h"
|
|
|
|
#include "xenia/xbox.h"
|
|
namespace xe {
|
|
namespace cpu {
|
|
|
|
thread_local ThreadState* thread_state_ = nullptr;
|
|
|
|
static void* AllocateContext() {
|
|
size_t granularity = xe::memory::allocation_granularity();
|
|
for (unsigned pos32 = 0x40; pos32 < 8192; ++pos32) {
|
|
/*
|
|
we want our register which points to the context to have 0xE0000000 in
|
|
the low 32 bits, for checking for whether we need the 4k offset, but also
|
|
if we allocate starting from the page before we allow backends to index
|
|
negatively to get to their own backend specific data, which makes full
|
|
use of int8 displacement
|
|
|
|
|
|
the downside is we waste most of one granula and probably a fair bit of
|
|
the one starting at 0xE0 by using a direct virtual memory allocation
|
|
instead of malloc
|
|
*/
|
|
uintptr_t context_pre =
|
|
((static_cast<uint64_t>(pos32) << 32) | 0xE0000000) - granularity;
|
|
|
|
void* p = memory::AllocFixed(
|
|
(void*)context_pre, granularity + sizeof(ppc::PPCContext),
|
|
memory::AllocationType::kReserveCommit, memory::PageAccess::kReadWrite);
|
|
if (p) {
|
|
return reinterpret_cast<char*>(p) +
|
|
granularity; // now we have a ctx ptr with the e0 constant in low,
|
|
// and one page allocated before it
|
|
}
|
|
}
|
|
|
|
assert_always("giving up on allocating context, likely leaking contexts");
|
|
return nullptr;
|
|
}
|
|
|
|
static void FreeContext(void* ctx) {
|
|
size_t granularity = xe::memory::allocation_granularity();
|
|
char* true_start_of_ctx =
|
|
&reinterpret_cast<char*>(ctx)[-static_cast<ptrdiff_t>(granularity)];
|
|
memory::DeallocFixed(true_start_of_ctx, granularity + sizeof(ppc::PPCContext),
|
|
memory::DeallocationType::kRelease);
|
|
}
|
|
|
|
ThreadState::ThreadState(Processor* processor, uint32_t thread_id,
|
|
uint32_t stack_base, uint32_t pcr_address)
|
|
: processor_(processor),
|
|
memory_(processor->memory()),
|
|
thread_id_(thread_id) {
|
|
if (thread_id_ == UINT_MAX) {
|
|
// System thread. Assign the system thread ID with a high bit
|
|
// set so people know what's up.
|
|
uint32_t system_thread_handle = xe::threading::current_thread_system_id();
|
|
thread_id_ = 0x80000000 | system_thread_handle;
|
|
}
|
|
backend_data_ = processor->backend()->AllocThreadData();
|
|
|
|
// Allocate with 64b alignment.
|
|
|
|
context_ = reinterpret_cast<ppc::PPCContext*>(AllocateContext());
|
|
processor->backend()->InitializeBackendContext(context_);
|
|
assert_true(((uint64_t)context_ & 0x3F) == 0);
|
|
std::memset(context_, 0, sizeof(ppc::PPCContext));
|
|
|
|
// Stash pointers to common structures that callbacks may need.
|
|
context_->global_mutex = &xe::global_critical_region::mutex();
|
|
context_->virtual_membase = memory_->virtual_membase();
|
|
context_->physical_membase = memory_->physical_membase();
|
|
context_->processor = processor_;
|
|
context_->thread_state = this;
|
|
context_->thread_id = thread_id_;
|
|
|
|
// Set initial registers.
|
|
context_->r[1] = stack_base;
|
|
|
|
// constant register, used by hv only i think
|
|
context_->r[2] = 0x20000000;
|
|
|
|
context_->r[13] = pcr_address;
|
|
// VSCR - Vector Status and Control Register
|
|
// NJ bit (bit 16) = 1: Non-Java IEEE mode (default for Xbox 360)
|
|
context_->vscr_vec = vec128i(0, 0, 0, 0x00010000);
|
|
context_->msr = 0x9030; // dumped from a real 360, 0x8000
|
|
|
|
// this register can be used for arbitrary data according to the PPC docs
|
|
// but the suggested use is to mark which vector registers are in use, for
|
|
// faster save/restore it seems unlikely anything uses this, especially since
|
|
// we have way more than 32 vrs, but setting it to all ones seems closer to
|
|
// correct than 0
|
|
context_->vrsave = ~0u;
|
|
}
|
|
|
|
ThreadState::~ThreadState() {
|
|
if (backend_data_) {
|
|
processor_->backend()->FreeThreadData(backend_data_);
|
|
}
|
|
if (thread_state_ == this) {
|
|
thread_state_ = nullptr;
|
|
}
|
|
if (context_) {
|
|
processor_->backend()->DeinitializeBackendContext(context_);
|
|
FreeContext(reinterpret_cast<void*>(context_));
|
|
}
|
|
}
|
|
|
|
void ThreadState::Bind(ThreadState* thread_state) {
|
|
thread_state_ = thread_state;
|
|
}
|
|
|
|
ThreadState* ThreadState::Get() { return thread_state_; }
|
|
|
|
uint32_t ThreadState::GetThreadID() {
|
|
return thread_state_ ? thread_state_->thread_id_ : 0xFFFFFFFF;
|
|
}
|
|
|
|
} // namespace cpu
|
|
} // namespace xe
|