Migration to named_thread<>

Add atomic_t<>::try_dec instead of fetch_dec_sat
Add atomic_t<>::try_inc
GDBDebugServer is broken (needs rewrite)
Removed old_thread class (former named_thread)
Removed storing/rethrowing exceptions from thread
Emu.Stop doesn't inject an exception anymore
task_stack helper class removed
thread_base simplified (no shared_from_this)
thread_ctrl::spawn simplified (creates detached thread)
Implemented overrideable thread detaching logic
Disabled cellAdec, cellDmux, cellFsAio
SPUThread renamed to spu_thread
RawSPUThread removed, spu_thread used instead
Disabled deriving from ppu_thread
Partial support for thread renaming
lv2_timer... simplified, screw it
idm/fxm: butchered support for on_stop/on_init
vm: improved allocation structure (added size)
This commit is contained in:
Nekotekina 2018-10-11 01:17:19 +03:00
parent 8ca6c9fff0
commit 1b37e775be
82 changed files with 1820 additions and 2023 deletions

View File

@ -1023,7 +1023,7 @@ public:
} }
// Conditionally decrement // Conditionally decrement
simple_type fetch_dec_sat(simple_type greater_than = std::numeric_limits<simple_type>::min(), simple_type amount = 1) bool try_dec(simple_type greater_than = std::numeric_limits<simple_type>::min())
{ {
type _new, old = atomic_storage<type>::load(m_data); type _new, old = atomic_storage<type>::load(m_data);
@ -1031,17 +1031,39 @@ public:
{ {
_new = old; _new = old;
if (_new <= greater_than) if (!(_new > greater_than))
{ {
// Early exit return false;
return old;
} }
_new -= amount; _new -= 1;
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new))) if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new)))
{ {
return old; return true;
}
}
}
// Conditionally increment
bool try_inc(simple_type less_than = std::numeric_limits<simple_type>::max())
{
type _new, old = atomic_storage<type>::load(m_data);
while (true)
{
_new = old;
if (!(_new < less_than))
{
return false;
}
_new += 1;
if (LIKELY(atomic_storage<type>::compare_exchange(m_data, old, _new)))
{
return true;
} }
} }
} }

View File

@ -40,7 +40,7 @@ public:
const u64 ALL_THREADS = 0xffffffffffffffff; const u64 ALL_THREADS = 0xffffffffffffffff;
const u64 ANY_THREAD = 0; const u64 ANY_THREAD = 0;
class GDBDebugServer : public old_thread class GDBDebugServer
{ {
socket_t server_socket; socket_t server_socket;
socket_t client_socket; socket_t client_socket;
@ -112,29 +112,16 @@ class GDBDebugServer : public old_thread
bool cmd_set_breakpoint(gdb_cmd& cmd); bool cmd_set_breakpoint(gdb_cmd& cmd);
bool cmd_remove_breakpoint(gdb_cmd& cmd); bool cmd_remove_breakpoint(gdb_cmd& cmd);
protected:
void on_task() override final;
void on_exit() override final;
public: public:
bool from_breakpoint = true; bool from_breakpoint = true;
bool stop = false; bool stop = false;
bool paused = false; bool paused = false;
u64 pausedBy; u64 pausedBy;
virtual std::string get_name() const; void operator()();
virtual void on_stop() override final;
void pause_from(cpu_thread* t); void pause_from(cpu_thread* t);
}; };
extern u32 g_gdb_debugger_id; extern u32 g_gdb_debugger_id;
template <>
struct id_manager::on_stop<GDBDebugServer> {
static inline void func(GDBDebugServer* ptr)
{
if (ptr) ptr->on_stop();
}
};
#endif #endif

View File

@ -1091,33 +1091,40 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
const auto cpu = get_current_cpu_thread(); const auto cpu = get_current_cpu_thread();
if (rsx::g_access_violation_handler) if (rsx::g_access_violation_handler)
{ {
bool handled = false; bool handled = false;
try try
{ {
handled = rsx::g_access_violation_handler(addr, is_writing); handled = rsx::g_access_violation_handler(addr, is_writing);
} }
catch (std::runtime_error &e) catch (const std::exception& e)
{ {
LOG_FATAL(RSX, "g_access_violation_handler(0x%x, %d): %s", addr, is_writing, e.what()); LOG_FATAL(RSX, "g_access_violation_handler(0x%x, %d): %s", addr, is_writing, e.what());
if (cpu) if (cpu)
{ {
vm::temporary_unlock(*cpu); vm::temporary_unlock(*cpu);
cpu->state += cpu_flag::dbg_pause; cpu->state += cpu_flag::dbg_pause;
cpu->test_state();
return false; if (cpu->test_stopped())
{
std::terminate();
} }
} }
return false;
}
if (handled) if (handled)
{ {
g_tls_fault_rsx++; g_tls_fault_rsx++;
if (cpu) if (cpu && cpu->test_stopped())
{ {
cpu->test_state(); std::terminate();
} }
return true; return true;
} }
} }
@ -1160,7 +1167,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
// check if address is RawSPU MMIO register // check if address is RawSPU MMIO register
if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET)
{ {
auto thread = idm::get<RawSPUThread>((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET); auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET));
if (!thread) if (!thread)
{ {
@ -1255,9 +1262,9 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
if (vm::check_addr(addr, std::max<std::size_t>(1, d_size), vm::page_allocated | (is_writing ? vm::page_writable : vm::page_readable))) if (vm::check_addr(addr, std::max<std::size_t>(1, d_size), vm::page_allocated | (is_writing ? vm::page_writable : vm::page_readable)))
{ {
if (cpu) if (cpu && cpu->test_stopped())
{ {
cpu->test_state(); std::terminate();
} }
return true; return true;
@ -1321,6 +1328,11 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
LOG_FATAL(MEMORY, "Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr); LOG_FATAL(MEMORY, "Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr);
cpu->state += cpu_flag::dbg_pause; cpu->state += cpu_flag::dbg_pause;
cpu->check_state(); cpu->check_state();
if (cpu->test_stopped())
{
std::terminate();
}
} }
return true; return true;
@ -1571,53 +1583,6 @@ thread_local DECLARE(thread_ctrl::g_tls_this_thread) = nullptr;
DECLARE(thread_ctrl::g_native_core_layout) { native_core_arrangement::undefined }; DECLARE(thread_ctrl::g_native_core_layout) { native_core_arrangement::undefined };
void thread_base::start(const std::shared_ptr<thread_base>& ctrl, task_stack task)
{
#ifdef _WIN32
using thread_result = uint;
#else
using thread_result = void*;
#endif
// Thread entry point
const native_entry entry = [](void* arg) -> thread_result
{
// Recover shared_ptr from short-circuited thread_base object pointer
std::shared_ptr<thread_base> ctrl = static_cast<thread_base*>(arg)->m_self;
try
{
ctrl->initialize();
task_stack{std::move(ctrl->m_task)}.invoke();
}
catch (...)
{
// Capture exception
ctrl->finalize(std::current_exception());
finalize();
return 0;
}
ctrl->finalize(nullptr);
finalize();
return 0;
};
ctrl->m_self = ctrl;
ctrl->m_task = std::move(task);
#ifdef _WIN32
std::uintptr_t thread = _beginthreadex(nullptr, 0, entry, ctrl.get(), 0, nullptr);
verify("thread_ctrl::start" HERE), thread != 0;
#else
pthread_t thread;
verify("thread_ctrl::start" HERE), pthread_create(&thread, nullptr, entry, ctrl.get()) == 0;
#endif
// TODO: this is unsafe and must be duplicated in thread_ctrl::initialize
ctrl->m_thread = (uintptr_t)thread;
}
void thread_base::start(native_entry entry) void thread_base::start(native_entry entry)
{ {
#ifdef _WIN32 #ifdef _WIN32
@ -1679,7 +1644,7 @@ void thread_base::initialize()
#endif #endif
} }
std::shared_ptr<thread_base> thread_base::finalize(std::exception_ptr eptr) noexcept bool thread_base::finalize(int) noexcept
{ {
// Report pending errors // Report pending errors
error_code::error_report(0, 0, 0, 0); error_code::error_report(0, 0, 0, 0);
@ -1712,17 +1677,13 @@ std::shared_ptr<thread_base> thread_base::finalize(std::exception_ptr eptr) noex
g_tls_fault_rsx, g_tls_fault_rsx,
g_tls_fault_spu); g_tls_fault_spu);
// Untangle circular reference, set exception // Return true if need to delete thread object
std::unique_lock lock(m_mutex); const bool result = m_state.exchange(thread_state::finished) == thread_state::detached;
// Possibly last reference to the thread object
std::shared_ptr<thread_base> self = std::move(m_self);
m_state = thread_state::finished;
m_exception = eptr;
// Signal waiting threads // Signal waiting threads
lock.unlock(), m_jcv.notify_all(); m_mutex.lock_unlock();
return self; m_jcv.notify_all();
return result;
} }
void thread_base::finalize() noexcept void thread_base::finalize() noexcept
@ -1741,8 +1702,6 @@ bool thread_ctrl::_wait_for(u64 usec)
// Mutex is unlocked at the start and after the waiting // Mutex is unlocked at the start and after the waiting
if (u32 sig = _this->m_signal.load()) if (u32 sig = _this->m_signal.load())
{ {
thread_ctrl::test();
if (sig & 1) if (sig & 1)
{ {
_this->m_signal &= ~1; _this->m_signal &= ~1;
@ -1761,11 +1720,6 @@ bool thread_ctrl::_wait_for(u64 usec)
// Double-check the value // Double-check the value
if (u32 sig = _this->m_signal.load()) if (u32 sig = _this->m_signal.load())
{ {
if (sig & 2 && _this->m_exception)
{
_this->_throw();
}
if (sig & 1) if (sig & 1)
{ {
_this->m_signal &= ~1; _this->m_signal &= ~1;
@ -1780,20 +1734,6 @@ bool thread_ctrl::_wait_for(u64 usec)
return false; return false;
} }
[[noreturn]] void thread_base::_throw()
{
std::exception_ptr ex = std::exchange(m_exception, std::exception_ptr{});
m_signal &= ~3;
m_mutex.unlock();
std::rethrow_exception(std::move(ex));
}
void thread_base::_notify(cond_variable thread_base::* ptr)
{
m_mutex.lock_unlock();
(this->*ptr).notify_one();
}
thread_base::thread_base(std::string_view name) thread_base::thread_base(std::string_view name)
: m_name(name) : m_name(name)
{ {
@ -1811,22 +1751,6 @@ thread_base::~thread_base()
} }
} }
void thread_base::set_exception(std::exception_ptr ptr)
{
std::lock_guard lock(m_mutex);
m_exception = ptr;
if (m_exception)
{
m_signal |= 2;
m_cond.notify_one();
}
else
{
m_signal &= ~2;
}
}
void thread_base::join() const void thread_base::join() const
{ {
if (m_state == thread_state::finished) if (m_state == thread_state::finished)
@ -1842,33 +1766,13 @@ void thread_base::join() const
} }
} }
void thread_base::detach()
{
auto self = weak_from_this().lock();
if (!self)
{
LOG_FATAL(GENERAL, "Cannot detach thread '%s'", get_name());
return;
}
if (self->m_state.compare_and_swap_test(thread_state::created, thread_state::detached))
{
std::lock_guard lock(m_mutex);
if (m_state == thread_state::detached)
{
m_self = std::move(self);
}
}
}
void thread_base::notify() void thread_base::notify()
{ {
if (!(m_signal & 1)) if (!(m_signal & 1))
{ {
m_signal |= 1; m_signal |= 1;
_notify(&thread_base::m_cond); m_mutex.lock_unlock();
m_cond.notify_one();
} }
} }
@ -1886,16 +1790,13 @@ u64 thread_base::get_cycles()
{ {
cycles = static_cast<u64>(thread_time.tv_sec) * 1'000'000'000 + thread_time.tv_nsec; cycles = static_cast<u64>(thread_time.tv_sec) * 1'000'000'000 + thread_time.tv_nsec;
#endif #endif
// Report 0 the first time this function is called if (const u64 old_cycles = m_cycles.exchange(cycles))
if (m_cycles == 0)
{ {
m_cycles = cycles; return cycles - old_cycles;
return 0;
} }
const auto diff_cycles = cycles - m_cycles; // Report 0 the first time this function is called
m_cycles = cycles; return 0;
return diff_cycles;
} }
else else
{ {
@ -1903,23 +1804,6 @@ u64 thread_base::get_cycles()
} }
} }
void thread_ctrl::test()
{
const auto _this = g_tls_this_thread;
if (_this->m_signal & 2)
{
_this->m_mutex.lock();
if (_this->m_exception)
{
_this->_throw();
}
_this->m_mutex.unlock();
}
}
void thread_ctrl::detect_cpu_layout() void thread_ctrl::detect_cpu_layout()
{ {
if (!g_native_core_layout.compare_and_swap_test(native_core_arrangement::undefined, native_core_arrangement::generic)) if (!g_native_core_layout.compare_and_swap_test(native_core_arrangement::undefined, native_core_arrangement::generic))
@ -2067,45 +1951,3 @@ void thread_ctrl::set_thread_affinity_mask(u16 mask)
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs);
#endif #endif
} }
old_thread::old_thread()
{
}
old_thread::~old_thread()
{
}
std::string old_thread::get_name() const
{
return fmt::format("('%s') Unnamed Thread", typeid(*this).name());
}
void old_thread::start_thread(const std::shared_ptr<void>& _this)
{
// Ensure it's not called from the constructor and the correct object is passed
verify("old_thread::start_thread" HERE), _this.get() == this;
// Run thread
thread_ctrl::spawn(m_thread, get_name(), [this, _this]()
{
try
{
LOG_TRACE(GENERAL, "Thread started");
on_spawn();
on_task();
LOG_TRACE(GENERAL, "Thread ended");
}
catch (const std::exception& e)
{
LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
Emu.Pause();
}
on_exit();
});
}
task_stack::task_base::~task_base()
{
}

View File

@ -3,7 +3,6 @@
#include "types.h" #include "types.h"
#include "Atomic.h" #include "Atomic.h"
#include <exception>
#include <string> #include <string>
#include <memory> #include <memory>
#include <string_view> #include <string_view>
@ -38,8 +37,8 @@ enum class thread_class : u32
enum class thread_state enum class thread_state
{ {
created, // Initial state created, // Initial state
detached, // Set if the thread has been detached successfully (only possible via shared_ptr) detached, // The thread has been detached to destroy its own named_thread object (can be dangerously misused)
aborting, // Set if the thread has been joined in destructor (mutually exclusive with detached) aborting, // The thread has been joined in the destructor or explicitly aborted (mutually exclusive with detached)
finished // Final state, always set at the end of thread execution finished // Final state, always set at the end of thread execution
}; };
@ -89,84 +88,15 @@ struct thread_on_abort : std::bool_constant<false> {};
template <typename T> template <typename T>
struct thread_on_abort<T, decltype(std::declval<named_thread<T>&>().on_abort())> : std::bool_constant<true> {}; struct thread_on_abort<T, decltype(std::declval<named_thread<T>&>().on_abort())> : std::bool_constant<true> {};
// Detect on_cleanup() static function (should return void) // Detect on_cleanup() static member function (should return void) (in C++20 can use destroying delete instead)
template <typename T, typename = void> template <typename T, typename = void>
struct thread_on_cleanup : std::bool_constant<false> {}; struct thread_on_cleanup : std::bool_constant<false> {};
template <typename T> template <typename T>
struct thread_on_cleanup<T, decltype(named_thread<T>::on_cleanup(std::declval<named_thread<T>*>()))> : std::bool_constant<true> {}; struct thread_on_cleanup<T, decltype(named_thread<T>::on_cleanup(std::declval<named_thread<T>*>()))> : std::bool_constant<true> {};
// Simple list of void() functors // Thread base class
class task_stack class thread_base
{
struct task_base
{
std::unique_ptr<task_base> next;
virtual ~task_base();
virtual void invoke()
{
if (next)
{
next->invoke();
}
}
};
template <typename F>
struct task_type final : task_base
{
std::remove_reference_t<F> func;
task_type(F&& func)
: func(std::forward<F>(func))
{
}
void invoke() final override
{
func();
task_base::invoke();
}
};
std::unique_ptr<task_base> m_stack;
public:
task_stack() = default;
template <typename F>
task_stack(F&& func)
: m_stack(new task_type<F>(std::forward<F>(func)))
{
}
void push(task_stack stack)
{
auto _top = stack.m_stack.release();
auto _next = m_stack.release();
m_stack.reset(_top);
while (UNLIKELY(_top->next)) _top = _top->next.get();
_top->next.reset(_next);
}
void reset()
{
m_stack.reset();
}
void invoke() const
{
if (m_stack)
{
m_stack->invoke();
}
}
};
// Thread base class (TODO: remove shared_ptr, make private base)
class thread_base : public std::enable_shared_from_this<thread_base>
{ {
// Native thread entry point function type // Native thread entry point function type
#ifdef _WIN32 #ifdef _WIN32
@ -175,9 +105,6 @@ class thread_base : public std::enable_shared_from_this<thread_base>
using native_entry = void*(*)(void* arg); using native_entry = void*(*)(void* arg);
#endif #endif
// Self pointer for detached thread
std::shared_ptr<thread_base> m_self;
// Thread handle (platform-specific) // Thread handle (platform-specific)
atomic_t<std::uintptr_t> m_thread{0}; atomic_t<std::uintptr_t> m_thread{0};
@ -196,71 +123,41 @@ class thread_base : public std::enable_shared_from_this<thread_base>
// Thread state // Thread state
atomic_t<thread_state> m_state = thread_state::created; atomic_t<thread_state> m_state = thread_state::created;
// Remotely set or caught exception
std::exception_ptr m_exception;
// Thread initial task
task_stack m_task;
// Thread name // Thread name
lf_value<std::string> m_name; lf_value<std::string> m_name;
// CPU cycles thread has run for //
u64 m_cycles{0}; atomic_t<u64> m_cycles = 0;
// Start thread // Start thread
static void start(const std::shared_ptr<thread_base>&, task_stack);
void start(native_entry); void start(native_entry);
// Called at the thread start // Called at the thread start
void initialize(); void initialize();
// Called at the thread end, returns moved m_self (may be null) // Called at the thread end, returns true if needs destruction
std::shared_ptr<thread_base> finalize(std::exception_ptr) noexcept; bool finalize(int) noexcept;
// Cleanup after possibly deleting the thread instance
static void finalize() noexcept; static void finalize() noexcept;
// Internal throwing function. Mutex must be locked and will be unlocked.
[[noreturn]] void _throw();
// Internal notification function
void _notify(cond_variable thread_base::*);
friend class thread_ctrl; friend class thread_ctrl;
template <class Context> template <class Context>
friend class named_thread; friend class named_thread;
public: protected:
thread_base(std::string_view name); thread_base(std::string_view name);
~thread_base(); ~thread_base();
// Get thread name public:
const std::string& get_name() const
{
return m_name;
}
// Set thread name (not recommended)
void set_name(std::string_view name)
{
m_name.assign(name);
}
// Get CPU cycles since last time this function was called. First call returns 0. // Get CPU cycles since last time this function was called. First call returns 0.
u64 get_cycles(); u64 get_cycles();
// Set exception
void set_exception(std::exception_ptr ptr);
// Wait for the thread (it does NOT change thread state, and can be called from multiple threads) // Wait for the thread (it does NOT change thread state, and can be called from multiple threads)
void join() const; void join() const;
// Make thread to manage a shared_ptr of itself
void detach();
// Notify the thread // Notify the thread
void notify(); void notify();
}; };
@ -306,25 +203,37 @@ public:
static_cast<thread_base&>(thread).m_name.assign(name); static_cast<thread_base&>(thread).m_name.assign(name);
} }
template <typename T>
static u64 get_cycles(named_thread<T>& thread)
{
return static_cast<thread_base&>(thread).get_cycles();
}
template <typename T>
static void notify(named_thread<T>& thread)
{
static_cast<thread_base&>(thread).notify();
}
// Read current state // Read current state
static inline thread_state state() static inline thread_state state()
{ {
return g_tls_this_thread->m_state; return g_tls_this_thread->m_state;
} }
// Wait once with timeout. Abortable, may throw. May spuriously return false. // Wait once with timeout. May spuriously return false.
static inline bool wait_for(u64 usec) static inline bool wait_for(u64 usec)
{ {
return _wait_for(usec); return _wait_for(usec);
} }
// Wait. Abortable, may throw. // Wait.
static inline void wait() static inline void wait()
{ {
_wait_for(-1); _wait_for(-1);
} }
// Wait until pred(). Abortable, may throw. // Wait until pred().
template <typename F, typename RT = std::invoke_result_t<F>> template <typename F, typename RT = std::invoke_result_t<F>>
static inline RT wait(F&& pred) static inline RT wait(F&& pred)
{ {
@ -339,42 +248,12 @@ public:
} }
} }
// Wait eternally until aborted.
[[noreturn]] static inline void eternalize()
{
while (true)
{
_wait_for(-1);
}
}
// Test exception (may throw).
static void test();
// Get current thread (may be nullptr) // Get current thread (may be nullptr)
static thread_base* get_current() static thread_base* get_current()
{ {
return g_tls_this_thread; return g_tls_this_thread;
} }
// Create detached named thread
template <typename N, typename F>
static inline void spawn(N&& name, F&& func)
{
auto out = std::make_shared<thread_base>(std::forward<N>(name));
thread_base::start(out, std::forward<F>(func));
}
// Named thread factory
template <typename N, typename F>
static inline void spawn(std::shared_ptr<thread_base>& out, N&& name, F&& func)
{
out = std::make_shared<thread_base>(std::forward<N>(name));
thread_base::start(out, std::forward<F>(func));
}
// Detect layout // Detect layout
static void detect_cpu_layout(); static void detect_cpu_layout();
@ -387,22 +266,17 @@ public:
// Sets the preferred affinity mask for this thread // Sets the preferred affinity mask for this thread
static void set_thread_affinity_mask(u16 mask); static void set_thread_affinity_mask(u16 mask);
// Spawn a detached named thread
template <typename F> template <typename F>
static inline std::shared_ptr<named_thread<F>> make_shared(std::string_view name, F&& lambda) static void spawn(std::string_view name, F&& func)
{ {
return std::make_shared<named_thread<F>>(name, std::forward<F>(lambda)); new named_thread<F>(thread_state::detached, name, std::forward<F>(func));
}
template <typename T, typename... Args>
static inline std::shared_ptr<named_thread<T>> make_shared(std::string_view name, Args&&... args)
{
return std::make_shared<named_thread<T>>(name, std::forward<Args>(args)...);
} }
}; };
// Derived from the callable object Context, possibly a lambda // Derived from the callable object Context, possibly a lambda
template <class Context> template <class Context>
class named_thread final : public Context, result_storage_t<Context>, public thread_base class named_thread final : public Context, result_storage_t<Context>, thread_base
{ {
using result = result_storage_t<Context>; using result = result_storage_t<Context>;
using thread = thread_base; using thread = thread_base;
@ -414,7 +288,22 @@ class named_thread final : public Context, result_storage_t<Context>, public thr
static inline void* entry_point(void* arg) try static inline void* entry_point(void* arg) try
#endif #endif
{ {
const auto maybe_last_ptr = static_cast<named_thread*>(static_cast<thread*>(arg))->entry_point(); const auto _this = static_cast<named_thread*>(static_cast<thread*>(arg));
// Perform self-cleanup if necessary
if (_this->entry_point())
{
// Call on_cleanup() static member function if it's available
if constexpr (thread_on_cleanup<Context>())
{
Context::on_cleanup(_this);
}
else
{
delete _this;
}
}
thread::finalize(); thread::finalize();
return 0; return 0;
} }
@ -423,7 +312,7 @@ class named_thread final : public Context, result_storage_t<Context>, public thr
catch_all_exceptions(); catch_all_exceptions();
} }
std::shared_ptr<thread> entry_point() bool entry_point()
{ {
thread::initialize(); thread::initialize();
@ -438,7 +327,16 @@ class named_thread final : public Context, result_storage_t<Context>, public thr
new (result::get()) typename result::type(Context::operator()()); new (result::get()) typename result::type(Context::operator()());
} }
return thread::finalize(nullptr); return thread::finalize(0);
}
// Detached thread constructor
named_thread(thread_state s, std::string_view name, Context&& f)
: Context(std::forward<Context>(f))
, thread(name)
{
thread::m_state.raw() = s;
thread::start(&named_thread::entry_point);
} }
friend class thread_ctrl; friend class thread_ctrl;
@ -493,22 +391,24 @@ public:
return thread::m_state.load(); return thread::m_state.load();
} }
// Try to set thread_state::aborting // Try to abort/detach
named_thread& operator=(thread_state s) named_thread& operator=(thread_state s)
{ {
if (s != thread_state::aborting) if (s != thread_state::aborting && s != thread_state::detached)
{ {
ASSUME(0); ASSUME(0);
} }
// Notify thread if not detached or terminated if (thread::m_state.compare_and_swap_test(thread_state::created, s))
if (thread::m_state.compare_and_swap_test(thread_state::created, thread_state::aborting)) {
if (s == thread_state::aborting)
{ {
// Call on_abort() method if it's available // Call on_abort() method if it's available
if constexpr (thread_on_abort<Context>()) if constexpr (thread_on_abort<Context>())
{ {
Context::on_abort(); Context::on_abort();
} }
}
thread::notify(); thread::notify();
} }
@ -528,63 +428,3 @@ public:
} }
} }
}; };
// Old named_thread
class old_thread
{
// Pointer to managed resource (shared with actual thread)
std::shared_ptr<thread_base> m_thread;
public:
old_thread();
virtual ~old_thread();
old_thread(const old_thread&) = delete;
old_thread& operator=(const old_thread&) = delete;
// Get thread name
virtual std::string get_name() const;
protected:
// Start thread (cannot be called from the constructor: should throw in such case)
void start_thread(const std::shared_ptr<void>& _this);
// Thread task (called in the thread)
virtual void on_task() = 0;
// Thread finalization (called after on_task)
virtual void on_exit() {}
// Called once upon thread spawn within the thread's own context
virtual void on_spawn() {}
public:
// ID initialization
virtual void on_init(const std::shared_ptr<void>& _this)
{
return start_thread(_this);
}
// ID finalization
virtual void on_stop()
{
m_thread->join();
}
thread_base* get() const
{
return m_thread.get();
}
void join() const
{
return m_thread->join();
}
void notify() const
{
return m_thread->notify();
}
};

View File

@ -21,7 +21,7 @@ bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
verify(HERE), rc == WAIT_TIMEOUT; verify(HERE), rc == WAIT_TIMEOUT;
// Retire // Retire
while (!m_value.fetch_dec_sat()) while (!m_value.try_dec())
{ {
timeout.QuadPart = 0; timeout.QuadPart = 0;

View File

@ -34,7 +34,7 @@ protected:
bool try_wait() bool try_wait()
{ {
return m_value.fetch_dec_sat(0) > 0; return m_value.try_dec(0);
} }
void post(s32 _max) void post(s32 _max)

View File

@ -774,10 +774,8 @@ namespace utils
// If max_count > 1 only id_new is supported // If max_count > 1 only id_new is supported
static_assert(std::is_same_v<id_tag, id_new_t> && !std::is_const_v<std::remove_reference_t<Type>>); static_assert(std::is_same_v<id_tag, id_new_t> && !std::is_const_v<std::remove_reference_t<Type>>);
// Try to acquire the semaphore (conditional increment) // Try to acquire the semaphore
const uint old_sema = head->m_sema.load(); if (UNLIKELY(!head->m_sema.try_inc(last + 1)))
if (UNLIKELY(old_sema > last || !head->m_sema.compare_and_swap_test(old_sema, old_sema + 1)))
{ {
block = nullptr; block = nullptr;
} }
@ -1225,7 +1223,7 @@ namespace utils
template <typename Type> template <typename Type>
std::shared_lock<::notifier> get_free_notifier() const std::shared_lock<::notifier> get_free_notifier() const
{ {
return std::shared_lock{get_head<Type>()->m_free_notifier}; return std::shared_lock(get_head<Type>()->m_free_notifier, std::try_to_lock);
} }
}; };
} // namespace utils } // namespace utils

View File

@ -4,11 +4,8 @@
#include "CPUThread.h" #include "CPUThread.h"
#include "Emu/IdManager.h" #include "Emu/IdManager.h"
#include "Utilities/GDBDebugServer.h" #include "Utilities/GDBDebugServer.h"
#include <typeinfo> #include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/SPUThread.h"
#ifdef _WIN32
#include <Windows.h>
#endif
DECLARE(cpu_thread::g_threads_created){0}; DECLARE(cpu_thread::g_threads_created){0};
DECLARE(cpu_thread::g_threads_deleted){0}; DECLARE(cpu_thread::g_threads_deleted){0};
@ -45,12 +42,22 @@ void fmt_class_string<bs_t<cpu_flag>>::format(std::string& out, u64 arg)
thread_local cpu_thread* g_tls_current_cpu_thread = nullptr; thread_local cpu_thread* g_tls_current_cpu_thread = nullptr;
void cpu_thread::on_task() void cpu_thread::operator()()
{ {
state -= cpu_flag::exit; state -= cpu_flag::exit;
g_tls_current_cpu_thread = this; g_tls_current_cpu_thread = this;
if (g_cfg.core.thread_scheduler_enabled)
{
thread_ctrl::set_thread_affinity_mask(thread_ctrl::get_affinity_mask(id_type() == 1 ? thread_class::ppu : thread_class::spu));
}
if (g_cfg.core.lower_spu_priority && id_type() == 2)
{
thread_ctrl::set_native_priority(-1);
}
// Check thread status // Check thread status
while (!(state & (cpu_flag::exit + cpu_flag::dbg_global_stop))) while (!(state & (cpu_flag::exit + cpu_flag::dbg_global_stop)))
{ {
@ -65,10 +72,12 @@ void cpu_thread::on_task()
{ {
state += _s; state += _s;
} }
catch (const std::exception&) catch (const std::exception& e)
{ {
LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
LOG_NOTICE(GENERAL, "\n%s", dump()); LOG_NOTICE(GENERAL, "\n%s", dump());
throw; Emu.Pause();
break;
} }
state -= cpu_flag::ret; state -= cpu_flag::ret;
@ -79,10 +88,9 @@ void cpu_thread::on_task()
} }
} }
void cpu_thread::on_stop() void cpu_thread::on_abort()
{ {
state += cpu_flag::exit; state += cpu_flag::exit;
notify();
} }
cpu_thread::~cpu_thread() cpu_thread::~cpu_thread()
@ -132,7 +140,7 @@ bool cpu_thread::check_state()
cpu_sleep_called = false; cpu_sleep_called = false;
} }
if (!(state & cpu_state_pause)) if (!is_paused())
{ {
if (cpu_flag_memory) if (cpu_flag_memory)
{ {
@ -167,23 +175,22 @@ bool cpu_thread::check_state()
return false; return false;
} }
void cpu_thread::test_state() void cpu_thread::notify()
{ {
if (UNLIKELY(state)) if (id_type() == 1)
{ {
if (check_state()) thread_ctrl::notify(*static_cast<named_thread<ppu_thread>*>(this));
}
else if (id_type() == 2)
{ {
throw cpu_flag::ret; thread_ctrl::notify(*static_cast<named_thread<spu_thread>*>(this));
} }
else
{
fmt::throw_exception("Invalid cpu_thread type");
} }
} }
void cpu_thread::run()
{
state -= cpu_flag::stop;
notify();
}
std::string cpu_thread::dump() const std::string cpu_thread::dump() const
{ {
return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load()); return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load());

View File

@ -21,32 +21,53 @@ enum class cpu_flag : u32
__bitset_enum_max __bitset_enum_max
}; };
// Flag set for pause state class cpu_thread
constexpr bs_t<cpu_flag> cpu_state_pause = cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause;
class cpu_thread : public old_thread
{ {
void on_task() override final; // PPU cache backward compatibility hack
char dummy[sizeof(std::shared_ptr<void>)];
protected:
cpu_thread(u32 id);
public: public:
virtual void on_stop() override; virtual ~cpu_thread();
virtual ~cpu_thread() override; void operator()();
void on_abort();
// Self identifier
const u32 id; const u32 id;
cpu_thread(u32 id);
// Public thread state // Public thread state
atomic_bs_t<cpu_flag> state{+cpu_flag::stop}; atomic_bs_t<cpu_flag> state{+cpu_flag::stop};
// Process thread state, return true if the checker must return // Process thread state, return true if the checker must return
bool check_state(); bool check_state();
// Process thread state // Process thread state (pause)
void test_state(); [[nodiscard]] bool test_stopped()
{
if (UNLIKELY(state))
{
if (check_state())
{
return true;
}
}
// Run thread return false;
void run(); }
// Test stopped state
bool is_stopped()
{
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::dbg_global_stop));
}
// Test paused state
bool is_paused()
{
return !!(state & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
}
// Check thread type // Check thread type
u32 id_type() u32 id_type()
@ -54,10 +75,16 @@ public:
return id >> 24; return id >> 24;
} }
// Upcast and notify
void notify();
// Thread stats for external observation // Thread stats for external observation
static atomic_t<u64> g_threads_created, g_threads_deleted; static atomic_t<u64> g_threads_created, g_threads_deleted;
// Print CPU state // Get thread name
virtual std::string get_name() const = 0;
// Get CPU state dump
virtual std::string dump() const; virtual std::string dump() const;
// Thread entry point function // Thread entry point function
@ -79,3 +106,6 @@ inline cpu_thread* get_current_cpu_thread() noexcept
return g_tls_current_cpu_thread; return g_tls_current_cpu_thread;
} }
class ppu_thread;
class spu_thread;

View File

@ -71,7 +71,7 @@ public:
bool use_ats_headers; bool use_ats_headers;
AudioDecoder(s32 type, u32 addr, u32 size, vm::ptr<CellAdecCbMsg> func, u32 arg) AudioDecoder(s32 type, u32 addr, u32 size, vm::ptr<CellAdecCbMsg> func, u32 arg)
: ppu_thread("HLE Audio Decoder") : ppu_thread({}, "", 0)
, type(type) , type(type)
, memAddr(addr) , memAddr(addr)
, memSize(size) , memSize(size)
@ -159,7 +159,7 @@ public:
} }
} }
virtual void cpu_task() override void non_task()
{ {
while (true) while (true)
{ {
@ -564,13 +564,7 @@ s32 cellAdecOpen(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResource> res, vm::
return CELL_ADEC_ERROR_ARG; return CELL_ADEC_ERROR_ARG;
} }
auto&& adec = idm::make_ptr<ppu_thread, AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg); fmt::throw_exception("cellAdec disabled, use LLE.");
*handle = adec->id;
adec->run();
return CELL_OK;
} }
s32 cellAdecOpenEx(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResourceEx> res, vm::ptr<CellAdecCb> cb, vm::ptr<u32> handle) s32 cellAdecOpenEx(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResourceEx> res, vm::ptr<CellAdecCb> cb, vm::ptr<u32> handle)
@ -582,13 +576,7 @@ s32 cellAdecOpenEx(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResourceEx> res,
return CELL_ADEC_ERROR_ARG; return CELL_ADEC_ERROR_ARG;
} }
auto&& adec = idm::make_ptr<ppu_thread, AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg); fmt::throw_exception("cellAdec disabled, use LLE.");
*handle = adec->id;
adec->run();
return CELL_OK;
} }
s32 cellAdecOpenExt(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResourceEx> res, vm::ptr<CellAdecCb> cb, vm::ptr<u32> handle) s32 cellAdecOpenExt(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResourceEx> res, vm::ptr<CellAdecCb> cb, vm::ptr<u32> handle)

View File

@ -349,7 +349,7 @@ error_code cellAudioInit()
return CELL_OK; return CELL_OK;
} }
error_code cellAudioQuit() error_code cellAudioQuit(ppu_thread& ppu)
{ {
cellAudio.warning("cellAudioQuit()"); cellAudio.warning("cellAudioQuit()");
@ -367,6 +367,11 @@ error_code cellAudioQuit()
while (true) while (true)
{ {
if (ppu.is_stopped())
{
return 0;
}
thread_ctrl::wait_for(1000); thread_ctrl::wait_for(1000);
auto g_audio = g_idm->lock<named_thread<audio_thread>>(0); auto g_audio = g_idm->lock<named_thread<audio_thread>>(0);

View File

@ -194,7 +194,7 @@ public:
atomic_t<bool> is_working; atomic_t<bool> is_working;
Demuxer(u32 addr, u32 size, vm::ptr<CellDmuxCbMsg> func, u32 arg) Demuxer(u32 addr, u32 size, vm::ptr<CellDmuxCbMsg> func, u32 arg)
: ppu_thread("HLE Demuxer") : ppu_thread({}, "", 0)
, is_finished(false) , is_finished(false)
, is_closed(false) , is_closed(false)
, is_running(false) , is_running(false)
@ -206,7 +206,7 @@ public:
{ {
} }
virtual void cpu_task() override void non_task()
{ {
DemuxerTask task; DemuxerTask task;
DemuxerStream stream = {}; DemuxerStream stream = {};
@ -987,13 +987,7 @@ s32 cellDmuxOpen(vm::cptr<CellDmuxType> type, vm::cptr<CellDmuxResource> res, vm
} }
// TODO: check demuxerResource and demuxerCb arguments // TODO: check demuxerResource and demuxerCb arguments
auto&& dmux = idm::make_ptr<ppu_thread, Demuxer>(res->memAddr, res->memSize, cb->cbMsgFunc, cb->cbArg); fmt::throw_exception("cellDmux disabled, use LLE.");
*handle = dmux->id;
dmux->run();
return CELL_OK;
} }
s32 cellDmuxOpenEx(vm::cptr<CellDmuxType> type, vm::cptr<CellDmuxResourceEx> resEx, vm::cptr<CellDmuxCb> cb, vm::ptr<u32> handle) s32 cellDmuxOpenEx(vm::cptr<CellDmuxType> type, vm::cptr<CellDmuxResourceEx> resEx, vm::cptr<CellDmuxCb> cb, vm::ptr<u32> handle)
@ -1006,13 +1000,7 @@ s32 cellDmuxOpenEx(vm::cptr<CellDmuxType> type, vm::cptr<CellDmuxResourceEx> res
} }
// TODO: check demuxerResourceEx and demuxerCb arguments // TODO: check demuxerResourceEx and demuxerCb arguments
auto&& dmux = idm::make_ptr<ppu_thread, Demuxer>(resEx->memAddr, resEx->memSize, cb->cbMsgFunc, cb->cbArg); fmt::throw_exception("cellDmux disabled, use LLE.");
*handle = dmux->id;
dmux->run();
return CELL_OK;
} }
s32 cellDmuxOpenExt(vm::cptr<CellDmuxType> type, vm::cptr<CellDmuxResourceEx> resEx, vm::cptr<CellDmuxCb> cb, vm::ptr<u32> handle) s32 cellDmuxOpenExt(vm::cptr<CellDmuxType> type, vm::cptr<CellDmuxResourceEx> resEx, vm::cptr<CellDmuxCb> cb, vm::ptr<u32> handle)
@ -1032,13 +1020,7 @@ s32 cellDmuxOpen2(vm::cptr<CellDmuxType2> type2, vm::cptr<CellDmuxResource2> res
} }
// TODO: check demuxerType2, demuxerResource2 and demuxerCb arguments // TODO: check demuxerType2, demuxerResource2 and demuxerCb arguments
auto&& dmux = idm::make_ptr<ppu_thread, Demuxer>(res2->memAddr, res2->memSize, cb->cbMsgFunc, cb->cbArg); fmt::throw_exception("cellDmux disabled, use LLE.");
*handle = dmux->id;
dmux->run();
return CELL_OK;
} }
s32 cellDmuxClose(u32 handle) s32 cellDmuxClose(u32 handle)

View File

@ -869,7 +869,7 @@ struct fs_aio_thread : ppu_thread
{ {
using ppu_thread::ppu_thread; using ppu_thread::ppu_thread;
virtual void cpu_task() override void non_task()
{ {
while (cmd64 cmd = cmd_wait()) while (cmd64 cmd = cmd_wait())
{ {
@ -920,11 +920,7 @@ s32 cellFsAioInit(vm::cptr<char> mount_point)
// TODO: create AIO thread (if not exists) for specified mount point // TODO: create AIO thread (if not exists) for specified mount point
const auto m = fxm::make<fs_aio_manager>(); const auto m = fxm::make<fs_aio_manager>();
if (m) fmt::throw_exception("cellFsAio disabled, use LLE.");
{
m->thread = idm::make_ptr<ppu_thread, fs_aio_thread>("FS AIO Thread", 500);
m->thread->run();
}
return CELL_OK; return CELL_OK;
} }
@ -961,8 +957,6 @@ s32 cellFsAioRead(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
{ aio, func }, { aio, func },
}); });
m->thread->notify();
return CELL_OK; return CELL_OK;
} }
@ -987,8 +981,6 @@ s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
{ aio, func }, { aio, func },
}); });
m->thread->notify();
return CELL_OK; return CELL_OK;
} }

View File

@ -6,6 +6,7 @@
#include "Emu/Memory/vm.h" #include "Emu/Memory/vm.h"
#include "Emu/RSX/GSRender.h" #include "Emu/RSX/GSRender.h"
#include "cellGcmSys.h" #include "cellGcmSys.h"
#include "sysPrxForUser.h"
#include <thread> #include <thread>
@ -346,7 +347,7 @@ void _cellGcmFunc15(vm::ptr<CellGcmContextData> context)
u32 g_defaultCommandBufferBegin, g_defaultCommandBufferFragmentCount; u32 g_defaultCommandBufferBegin, g_defaultCommandBufferFragmentCount;
// Called by cellGcmInit // Called by cellGcmInit
s32 _cellGcmInitBody(vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSize, u32 ioAddress) s32 _cellGcmInitBody(ppu_thread& ppu, vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSize, u32 ioAddress)
{ {
cellGcmSys.warning("_cellGcmInitBody(context=**0x%x, cmdSize=0x%x, ioSize=0x%x, ioAddress=0x%x)", context, cmdSize, ioSize, ioAddress); cellGcmSys.warning("_cellGcmInitBody(context=**0x%x, cmdSize=0x%x, ioSize=0x%x, ioAddress=0x%x)", context, cmdSize, ioSize, ioAddress);
@ -429,8 +430,10 @@ s32 _cellGcmInitBody(vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSi
ctrl.get = 0; ctrl.get = 0;
ctrl.ref = 0; // Set later to -1 at RSX initialization ctrl.ref = 0; // Set later to -1 at RSX initialization
render->intr_thread = idm::make_ptr<ppu_thread>("_gcm_intr_thread", 1, 0x4000); vm::var<u64> _tid;
render->intr_thread->run(); vm::var<char[]> _name = vm::make_str("_gcm_intr_thread");
ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, 128, 0, 1, 0x4000, 0, +_name);
render->intr_thread = idm::get<named_thread<ppu_thread>>(*_tid);
render->main_mem_addr = 0; render->main_mem_addr = 0;
render->isHLE = true; render->isHLE = true;
render->label_addr = m_config->gcm_info.label_addr; render->label_addr = m_config->gcm_info.label_addr;
@ -1380,7 +1383,11 @@ s32 cellGcmCallback(ppu_thread& ppu, vm::ptr<CellGcmContextData> context, u32 co
if (isInCommandBufferExcept(getPos, newCommandBuffer.first, newCommandBuffer.second)) if (isInCommandBufferExcept(getPos, newCommandBuffer.first, newCommandBuffer.second))
break; break;
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
busy_wait(); busy_wait();
} }

View File

@ -242,7 +242,7 @@ s32 cellMsgDialogClose(f32 delay)
{ {
if (auto dlg = manager->get<rsx::overlays::message_dialog>()) if (auto dlg = manager->get<rsx::overlays::message_dialog>())
{ {
thread_ctrl::make_shared("cellMsgDialogClose() Thread", [=] thread_ctrl::spawn("cellMsgDialogClose() Thread", [=]
{ {
while (get_system_time() < wait_until) while (get_system_time() < wait_until)
{ {
@ -256,7 +256,7 @@ s32 cellMsgDialogClose(f32 delay)
} }
dlg->close(); dlg->close();
})->detach(); });
return CELL_OK; return CELL_OK;
} }
@ -269,7 +269,7 @@ s32 cellMsgDialogClose(f32 delay)
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED; return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
} }
thread_ctrl::make_shared("cellMsgDialogClose() Thread", [=]() thread_ctrl::spawn("cellMsgDialogClose() Thread", [=]()
{ {
while (dlg->state == MsgDialogState::Open && get_system_time() < wait_until) while (dlg->state == MsgDialogState::Open && get_system_time() < wait_until)
{ {
@ -279,7 +279,7 @@ s32 cellMsgDialogClose(f32 delay)
} }
dlg->on_close(CELL_MSGDIALOG_BUTTON_NONE); dlg->on_close(CELL_MSGDIALOG_BUTTON_NONE);
})->detach(); });
return CELL_OK; return CELL_OK;
} }

View File

@ -36,7 +36,7 @@ struct cell_error_t
// Function prototypes // Function prototypes
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool spursKernelEntry(SPUThread& spu); bool spursKernelEntry(spu_thread& spu);
// SPURS Internals // SPURS Internals
namespace _spurs namespace _spurs
@ -599,18 +599,18 @@ s32 _spurs::create_handler(vm::ptr<CellSpurs> spurs, u32 ppuPriority)
{ {
using ppu_thread::ppu_thread; using ppu_thread::ppu_thread;
virtual void cpu_task() override void non_task()
{ {
BIND_FUNC(_spurs::handler_entry)(*this); BIND_FUNC(_spurs::handler_entry)(*this);
} }
}; };
auto&& eht = idm::make_ptr<ppu_thread, handler_thread>(std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr0", ppuPriority, 0x4000); // auto eht = idm::make_ptr<ppu_thread, handler_thread>(std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr0", ppuPriority, 0x4000);
spurs->ppu0 = eht->id; // spurs->ppu0 = eht->id;
eht->gpr[3] = spurs.addr(); // eht->gpr[3] = spurs.addr();
eht->run(); // eht->run();
return CELL_OK; return CELL_OK;
} }
@ -796,15 +796,15 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
{ {
using ppu_thread::ppu_thread; using ppu_thread::ppu_thread;
virtual void cpu_task() override void non_task()
{ {
BIND_FUNC(_spurs::event_helper_entry)(*this); BIND_FUNC(_spurs::event_helper_entry)(*this);
} }
}; };
auto&& eht = idm::make_ptr<ppu_thread, event_helper_thread>(std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr1", ppuPriority, 0x8000); //auto eht = idm::make_ptr<ppu_thread, event_helper_thread>(std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr1", ppuPriority, 0x8000);
if (!eht) //if (!eht)
{ {
sys_event_port_disconnect(spurs->eventPort); sys_event_port_disconnect(spurs->eventPort);
sys_event_port_destroy(spurs->eventPort); sys_event_port_destroy(spurs->eventPort);
@ -818,10 +818,10 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
return CELL_SPURS_CORE_ERROR_STAT; return CELL_SPURS_CORE_ERROR_STAT;
} }
eht->gpr[3] = spurs.addr(); // eht->gpr[3] = spurs.addr();
eht->run(); // eht->run();
spurs->ppu1 = eht->id; // spurs->ppu1 = eht->id;
return CELL_OK; return CELL_OK;
} }
@ -1118,7 +1118,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
} }
// entry point cannot be initialized immediately because SPU LS will be rewritten by sys_spu_thread_group_start() // entry point cannot be initialized immediately because SPU LS will be rewritten by sys_spu_thread_group_start()
//idm::get<SPUThread>(spurs->spus[num])->custom_task = [entry = spurs->spuImg.entry_point](SPUThread& spu) //idm::get<named_thread<spu_thread>>(spurs->spus[num])->custom_task = [entry = spurs->spuImg.entry_point](spu_thread& spu)
{ {
// Disabled // Disabled
//spu.RegisterHleFunction(entry, spursKernelEntry); //spu.RegisterHleFunction(entry, spursKernelEntry);

View File

@ -26,57 +26,57 @@ extern logs::channel cellSpurs;
// SPURS utility functions // SPURS utility functions
// //
static void cellSpursModulePutTrace(CellSpursTracePacket* packet, u32 dmaTagId); static void cellSpursModulePutTrace(CellSpursTracePacket* packet, u32 dmaTagId);
static u32 cellSpursModulePollStatus(SPUThread& spu, u32* status); static u32 cellSpursModulePollStatus(spu_thread& spu, u32* status);
static void cellSpursModuleExit(SPUThread& spu); static void cellSpursModuleExit(spu_thread& spu);
static bool spursDma(SPUThread& spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag); static bool spursDma(spu_thread& spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag);
static u32 spursDmaGetCompletionStatus(SPUThread& spu, u32 tagMask); static u32 spursDmaGetCompletionStatus(spu_thread& spu, u32 tagMask);
static u32 spursDmaWaitForCompletion(SPUThread& spu, u32 tagMask, bool waitForAll = true); static u32 spursDmaWaitForCompletion(spu_thread& spu, u32 tagMask, bool waitForAll = true);
static void spursHalt(SPUThread& spu); static void spursHalt(spu_thread& spu);
// //
// SPURS kernel functions // SPURS kernel functions
// //
static bool spursKernel1SelectWorkload(SPUThread& spu); static bool spursKernel1SelectWorkload(spu_thread& spu);
static bool spursKernel2SelectWorkload(SPUThread& spu); static bool spursKernel2SelectWorkload(spu_thread& spu);
static void spursKernelDispatchWorkload(SPUThread& spu, u64 widAndPollStatus); static void spursKernelDispatchWorkload(spu_thread& spu, u64 widAndPollStatus);
static bool spursKernelWorkloadExit(SPUThread& spu); static bool spursKernelWorkloadExit(spu_thread& spu);
bool spursKernelEntry(SPUThread& spu); bool spursKernelEntry(spu_thread& spu);
// //
// SPURS system workload functions // SPURS system workload functions
// //
static bool spursSysServiceEntry(SPUThread& spu); static bool spursSysServiceEntry(spu_thread& spu);
// TODO: Exit // TODO: Exit
static void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt); static void spursSysServiceIdleHandler(spu_thread& spu, SpursKernelContext* ctxt);
static void spursSysServiceMain(SPUThread& spu, u32 pollStatus); static void spursSysServiceMain(spu_thread& spu, u32 pollStatus);
static void spursSysServiceProcessRequests(SPUThread& spu, SpursKernelContext* ctxt); static void spursSysServiceProcessRequests(spu_thread& spu, SpursKernelContext* ctxt);
static void spursSysServiceActivateWorkload(SPUThread& spu, SpursKernelContext* ctxt); static void spursSysServiceActivateWorkload(spu_thread& spu, SpursKernelContext* ctxt);
// TODO: Deactivate workload // TODO: Deactivate workload
static void spursSysServiceUpdateShutdownCompletionEvents(SPUThread& spu, SpursKernelContext* ctxt, u32 wklShutdownBitSet); static void spursSysServiceUpdateShutdownCompletionEvents(spu_thread& spu, SpursKernelContext* ctxt, u32 wklShutdownBitSet);
static void spursSysServiceTraceSaveCount(SPUThread& spu, SpursKernelContext* ctxt); static void spursSysServiceTraceSaveCount(spu_thread& spu, SpursKernelContext* ctxt);
static void spursSysServiceTraceUpdate(SPUThread& spu, SpursKernelContext* ctxt, u32 arg2, u32 arg3, u32 forceNotify); static void spursSysServiceTraceUpdate(spu_thread& spu, SpursKernelContext* ctxt, u32 arg2, u32 arg3, u32 forceNotify);
// TODO: Deactivate trace // TODO: Deactivate trace
// TODO: System workload entry // TODO: System workload entry
static void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContext* ctxt); static void spursSysServiceCleanupAfterSystemWorkload(spu_thread& spu, SpursKernelContext* ctxt);
// //
// SPURS taskset policy module functions // SPURS taskset policy module functions
// //
static bool spursTasksetEntry(SPUThread& spu); static bool spursTasksetEntry(spu_thread& spu);
static bool spursTasksetSyscallEntry(SPUThread& spu); static bool spursTasksetSyscallEntry(spu_thread& spu);
static void spursTasksetResumeTask(SPUThread& spu); static void spursTasksetResumeTask(spu_thread& spu);
static void spursTasksetStartTask(SPUThread& spu, CellSpursTaskArgument& taskArgs); static void spursTasksetStartTask(spu_thread& spu, CellSpursTaskArgument& taskArgs);
static s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* isWaiting); static s32 spursTasksetProcessRequest(spu_thread& spu, s32 request, u32* taskId, u32* isWaiting);
static void spursTasksetProcessPollStatus(SPUThread& spu, u32 pollStatus); static void spursTasksetProcessPollStatus(spu_thread& spu, u32 pollStatus);
static bool spursTasksetPollStatus(SPUThread& spu); static bool spursTasksetPollStatus(spu_thread& spu);
static void spursTasksetExit(SPUThread& spu); static void spursTasksetExit(spu_thread& spu);
static void spursTasksetOnTaskExit(SPUThread& spu, u64 addr, u32 taskId, s32 exitCode, u64 args); static void spursTasksetOnTaskExit(spu_thread& spu, u64 addr, u32 taskId, s32 exitCode, u64 args);
static s32 spursTasketSaveTaskContext(SPUThread& spu); static s32 spursTasketSaveTaskContext(spu_thread& spu);
static void spursTasksetDispatch(SPUThread& spu); static void spursTasksetDispatch(spu_thread& spu);
static s32 spursTasksetProcessSyscall(SPUThread& spu, u32 syscallNum, u32 args); static s32 spursTasksetProcessSyscall(spu_thread& spu, u32 syscallNum, u32 args);
static void spursTasksetInit(SPUThread& spu, u32 pollStatus); static void spursTasksetInit(spu_thread& spu, u32 pollStatus);
static s32 spursTasksetLoadElf(SPUThread& spu, u32* entryPoint, u32* lowestLoadAddr, u64 elfAddr, bool skipWriteableSegments); static s32 spursTasksetLoadElf(spu_thread& spu, u32* entryPoint, u32* lowestLoadAddr, u64 elfAddr, bool skipWriteableSegments);
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// SPURS utility functions // SPURS utility functions
@ -89,7 +89,7 @@ void cellSpursModulePutTrace(CellSpursTracePacket* packet, u32 dmaTagId)
} }
// Check for execution right requests // Check for execution right requests
u32 cellSpursModulePollStatus(SPUThread& spu, u32* status) u32 cellSpursModulePollStatus(spu_thread& spu, u32* status)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
@ -114,7 +114,7 @@ u32 cellSpursModulePollStatus(SPUThread& spu, u32* status)
} }
// Exit current workload // Exit current workload
void cellSpursModuleExit(SPUThread& spu) void cellSpursModuleExit(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
spu.pc = ctxt->exitToKernelAddr; spu.pc = ctxt->exitToKernelAddr;
@ -122,7 +122,7 @@ void cellSpursModuleExit(SPUThread& spu)
} }
// Execute a DMA operation // Execute a DMA operation
bool spursDma(SPUThread& spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag) bool spursDma(spu_thread& spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag)
{ {
spu.set_ch_value(MFC_LSA, lsa); spu.set_ch_value(MFC_LSA, lsa);
spu.set_ch_value(MFC_EAH, (u32)(ea >> 32)); spu.set_ch_value(MFC_EAH, (u32)(ea >> 32));
@ -141,7 +141,7 @@ bool spursDma(SPUThread& spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag)
} }
// Get the status of DMA operations // Get the status of DMA operations
u32 spursDmaGetCompletionStatus(SPUThread& spu, u32 tagMask) u32 spursDmaGetCompletionStatus(spu_thread& spu, u32 tagMask)
{ {
spu.set_ch_value(MFC_WrTagMask, tagMask); spu.set_ch_value(MFC_WrTagMask, tagMask);
spu.set_ch_value(MFC_WrTagUpdate, MFC_TAG_UPDATE_IMMEDIATE); spu.set_ch_value(MFC_WrTagUpdate, MFC_TAG_UPDATE_IMMEDIATE);
@ -149,7 +149,7 @@ u32 spursDmaGetCompletionStatus(SPUThread& spu, u32 tagMask)
} }
// Wait for DMA operations to complete // Wait for DMA operations to complete
u32 spursDmaWaitForCompletion(SPUThread& spu, u32 tagMask, bool waitForAll) u32 spursDmaWaitForCompletion(spu_thread& spu, u32 tagMask, bool waitForAll)
{ {
spu.set_ch_value(MFC_WrTagMask, tagMask); spu.set_ch_value(MFC_WrTagMask, tagMask);
spu.set_ch_value(MFC_WrTagUpdate, waitForAll ? MFC_TAG_UPDATE_ALL : MFC_TAG_UPDATE_ANY); spu.set_ch_value(MFC_WrTagUpdate, waitForAll ? MFC_TAG_UPDATE_ALL : MFC_TAG_UPDATE_ANY);
@ -157,12 +157,12 @@ u32 spursDmaWaitForCompletion(SPUThread& spu, u32 tagMask, bool waitForAll)
} }
// Halt the SPU // Halt the SPU
void spursHalt(SPUThread& spu) void spursHalt(spu_thread& spu)
{ {
spu.halt(); spu.halt();
} }
void sys_spu_thread_exit(SPUThread& spu, s32 status) void sys_spu_thread_exit(spu_thread& spu, s32 status)
{ {
// Cancel any pending status update requests // Cancel any pending status update requests
spu.set_ch_value(MFC_WrTagUpdate, 0); spu.set_ch_value(MFC_WrTagUpdate, 0);
@ -178,7 +178,7 @@ void sys_spu_thread_exit(SPUThread& spu, s32 status)
spu.stop_and_signal(0x102); spu.stop_and_signal(0x102);
} }
void sys_spu_thread_group_exit(SPUThread& spu, s32 status) void sys_spu_thread_group_exit(spu_thread& spu, s32 status)
{ {
// Cancel any pending status update requests // Cancel any pending status update requests
spu.set_ch_value(MFC_WrTagUpdate, 0); spu.set_ch_value(MFC_WrTagUpdate, 0);
@ -194,7 +194,7 @@ void sys_spu_thread_group_exit(SPUThread& spu, s32 status)
spu.stop_and_signal(0x101); spu.stop_and_signal(0x101);
} }
s32 sys_spu_thread_send_event(SPUThread& spu, u8 spup, u32 data0, u32 data1) s32 sys_spu_thread_send_event(spu_thread& spu, u8 spup, u32 data0, u32 data1)
{ {
if (spup > 0x3F) if (spup > 0x3F)
{ {
@ -211,7 +211,7 @@ s32 sys_spu_thread_send_event(SPUThread& spu, u8 spup, u32 data0, u32 data1)
return static_cast<u32>(spu.get_ch_value(SPU_RdInMbox)); return static_cast<u32>(spu.get_ch_value(SPU_RdInMbox));
} }
s32 sys_spu_thread_switch_system_module(SPUThread& spu, u32 status) s32 sys_spu_thread_switch_system_module(spu_thread& spu, u32 status)
{ {
if (spu.get_ch_count(SPU_RdInMbox)) if (spu.get_ch_count(SPU_RdInMbox))
{ {
@ -246,7 +246,7 @@ s32 sys_spu_thread_switch_system_module(SPUThread& spu, u32 status)
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Select a workload to run // Select a workload to run
bool spursKernel1SelectWorkload(SPUThread& spu) bool spursKernel1SelectWorkload(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
@ -430,7 +430,7 @@ bool spursKernel1SelectWorkload(SPUThread& spu)
} }
// Select a workload to run // Select a workload to run
bool spursKernel2SelectWorkload(SPUThread& spu) bool spursKernel2SelectWorkload(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
@ -603,7 +603,7 @@ bool spursKernel2SelectWorkload(SPUThread& spu)
} }
// SPURS kernel dispatch workload // SPURS kernel dispatch workload
void spursKernelDispatchWorkload(SPUThread& spu, u64 widAndPollStatus) void spursKernelDispatchWorkload(spu_thread& spu, u64 widAndPollStatus)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
auto isKernel2 = ctxt->spurs->flags1 & SF1_32_WORKLOADS ? true : false; auto isKernel2 = ctxt->spurs->flags1 & SF1_32_WORKLOADS ? true : false;
@ -655,7 +655,7 @@ void spursKernelDispatchWorkload(SPUThread& spu, u64 widAndPollStatus)
} }
// SPURS kernel workload exit // SPURS kernel workload exit
bool spursKernelWorkloadExit(SPUThread& spu) bool spursKernelWorkloadExit(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
auto isKernel2 = ctxt->spurs->flags1 & SF1_32_WORKLOADS ? true : false; auto isKernel2 = ctxt->spurs->flags1 & SF1_32_WORKLOADS ? true : false;
@ -676,10 +676,8 @@ bool spursKernelWorkloadExit(SPUThread& spu)
} }
// SPURS kernel entry point // SPURS kernel entry point
bool spursKernelEntry(SPUThread& spu) bool spursKernelEntry(spu_thread& spu)
{ {
thread_ctrl::eternalize();
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
memset(ctxt, 0, sizeof(SpursKernelContext)); memset(ctxt, 0, sizeof(SpursKernelContext));
@ -728,7 +726,7 @@ bool spursKernelEntry(SPUThread& spu)
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Entry point of the system service // Entry point of the system service
bool spursSysServiceEntry(SPUThread& spu) bool spursSysServiceEntry(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + spu.gpr[3]._u32[3]); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + spu.gpr[3]._u32[3]);
auto arg = spu.gpr[4]._u64[1]; auto arg = spu.gpr[4]._u64[1];
@ -757,7 +755,7 @@ bool spursSysServiceEntry(SPUThread& spu)
} }
// Wait for an external event or exit the SPURS thread group if no workloads can be scheduled // Wait for an external event or exit the SPURS thread group if no workloads can be scheduled
void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt) void spursSysServiceIdleHandler(spu_thread& spu, SpursKernelContext* ctxt)
{ {
bool shouldExit; bool shouldExit;
@ -865,7 +863,7 @@ void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt)
} }
// Main function for the system service // Main function for the system service
void spursSysServiceMain(SPUThread& spu, u32 pollStatus) void spursSysServiceMain(spu_thread& spu, u32 pollStatus)
{ {
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
@ -970,7 +968,7 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
} }
// Process any requests // Process any requests
void spursSysServiceProcessRequests(SPUThread& spu, SpursKernelContext* ctxt) void spursSysServiceProcessRequests(spu_thread& spu, SpursKernelContext* ctxt)
{ {
bool updateTrace = false; bool updateTrace = false;
bool updateWorkload = false; bool updateWorkload = false;
@ -1023,7 +1021,7 @@ void spursSysServiceProcessRequests(SPUThread& spu, SpursKernelContext* ctxt)
} }
// Activate a workload // Activate a workload
void spursSysServiceActivateWorkload(SPUThread& spu, SpursKernelContext* ctxt) void spursSysServiceActivateWorkload(spu_thread& spu, SpursKernelContext* ctxt)
{ {
auto spurs = vm::_ptr<CellSpurs>(spu.offset + 0x100); auto spurs = vm::_ptr<CellSpurs>(spu.offset + 0x100);
std::memcpy(vm::base(spu.offset + 0x30000), ctxt->spurs->wklInfo1, 0x200); std::memcpy(vm::base(spu.offset + 0x30000), ctxt->spurs->wklInfo1, 0x200);
@ -1121,7 +1119,7 @@ void spursSysServiceActivateWorkload(SPUThread& spu, SpursKernelContext* ctxt)
} }
// Update shutdown completion events // Update shutdown completion events
void spursSysServiceUpdateShutdownCompletionEvents(SPUThread& spu, SpursKernelContext* ctxt, u32 wklShutdownBitSet) void spursSysServiceUpdateShutdownCompletionEvents(spu_thread& spu, SpursKernelContext* ctxt, u32 wklShutdownBitSet)
{ {
// Mark the workloads in wklShutdownBitSet as completed and also generate a bit set of the completed // Mark the workloads in wklShutdownBitSet as completed and also generate a bit set of the completed
// workloads that have a shutdown completion hook registered // workloads that have a shutdown completion hook registered
@ -1164,7 +1162,7 @@ void spursSysServiceUpdateShutdownCompletionEvents(SPUThread& spu, SpursKernelCo
} }
// Update the trace count for this SPU // Update the trace count for this SPU
void spursSysServiceTraceSaveCount(SPUThread& spu, SpursKernelContext* ctxt) void spursSysServiceTraceSaveCount(spu_thread& spu, SpursKernelContext* ctxt)
{ {
if (ctxt->traceBuffer) if (ctxt->traceBuffer)
{ {
@ -1174,7 +1172,7 @@ void spursSysServiceTraceSaveCount(SPUThread& spu, SpursKernelContext* ctxt)
} }
// Update trace control // Update trace control
void spursSysServiceTraceUpdate(SPUThread& spu, SpursKernelContext* ctxt, u32 arg2, u32 arg3, u32 forceNotify) void spursSysServiceTraceUpdate(spu_thread& spu, SpursKernelContext* ctxt, u32 arg2, u32 arg3, u32 forceNotify)
{ {
bool notify; bool notify;
@ -1238,7 +1236,7 @@ void spursSysServiceTraceUpdate(SPUThread& spu, SpursKernelContext* ctxt, u32 ar
} }
// Restore state after executing the system workload // Restore state after executing the system workload
void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContext* ctxt) void spursSysServiceCleanupAfterSystemWorkload(spu_thread& spu, SpursKernelContext* ctxt)
{ {
u8 wklId; u8 wklId;
@ -1314,7 +1312,7 @@ enum SpursTasksetRequest
}; };
// Taskset PM entry point // Taskset PM entry point
bool spursTasksetEntry(SPUThread& spu) bool spursTasksetEntry(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
auto kernelCtxt = vm::_ptr<SpursKernelContext>(spu.offset + spu.gpr[3]._u32[3]); auto kernelCtxt = vm::_ptr<SpursKernelContext>(spu.offset + spu.gpr[3]._u32[3]);
@ -1353,7 +1351,7 @@ bool spursTasksetEntry(SPUThread& spu)
} }
// Entry point into the Taskset PM for task syscalls // Entry point into the Taskset PM for task syscalls
bool spursTasksetSyscallEntry(SPUThread& spu) bool spursTasksetSyscallEntry(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
@ -1384,7 +1382,7 @@ bool spursTasksetSyscallEntry(SPUThread& spu)
} }
// Resume a task // Resume a task
void spursTasksetResumeTask(SPUThread& spu) void spursTasksetResumeTask(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
@ -1400,7 +1398,7 @@ void spursTasksetResumeTask(SPUThread& spu)
} }
// Start a task // Start a task
void spursTasksetStartTask(SPUThread& spu, CellSpursTaskArgument& taskArgs) void spursTasksetStartTask(spu_thread& spu, CellSpursTaskArgument& taskArgs)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
auto taskset = vm::_ptr<CellSpursTaskset>(spu.offset + 0x2700); auto taskset = vm::_ptr<CellSpursTaskset>(spu.offset + 0x2700);
@ -1418,7 +1416,7 @@ void spursTasksetStartTask(SPUThread& spu, CellSpursTaskArgument& taskArgs)
} }
// Process a request and update the state of the taskset // Process a request and update the state of the taskset
s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* isWaiting) s32 spursTasksetProcessRequest(spu_thread& spu, s32 request, u32* taskId, u32* isWaiting)
{ {
auto kernelCtxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto kernelCtxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
@ -1611,7 +1609,7 @@ s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* is
} }
// Process pollStatus received from the SPURS kernel // Process pollStatus received from the SPURS kernel
void spursTasksetProcessPollStatus(SPUThread& spu, u32 pollStatus) void spursTasksetProcessPollStatus(spu_thread& spu, u32 pollStatus)
{ {
if (pollStatus & CELL_SPURS_MODULE_POLL_STATUS_FLAG) if (pollStatus & CELL_SPURS_MODULE_POLL_STATUS_FLAG)
{ {
@ -1620,7 +1618,7 @@ void spursTasksetProcessPollStatus(SPUThread& spu, u32 pollStatus)
} }
// Check execution rights // Check execution rights
bool spursTasksetPollStatus(SPUThread& spu) bool spursTasksetPollStatus(spu_thread& spu)
{ {
u32 pollStatus; u32 pollStatus;
@ -1634,7 +1632,7 @@ bool spursTasksetPollStatus(SPUThread& spu)
} }
// Exit the Taskset PM // Exit the Taskset PM
void spursTasksetExit(SPUThread& spu) void spursTasksetExit(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
@ -1656,7 +1654,7 @@ void spursTasksetExit(SPUThread& spu)
} }
// Invoked when a task exits // Invoked when a task exits
void spursTasksetOnTaskExit(SPUThread& spu, u64 addr, u32 taskId, s32 exitCode, u64 args) void spursTasksetOnTaskExit(spu_thread& spu, u64 addr, u32 taskId, s32 exitCode, u64 args)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
@ -1670,7 +1668,7 @@ void spursTasksetOnTaskExit(SPUThread& spu, u64 addr, u32 taskId, s32 exitCode,
} }
// Save the context of a task // Save the context of a task
s32 spursTasketSaveTaskContext(SPUThread& spu) s32 spursTasketSaveTaskContext(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
auto taskInfo = vm::_ptr<CellSpursTaskset::TaskInfo>(spu.offset + 0x2780); auto taskInfo = vm::_ptr<CellSpursTaskset::TaskInfo>(spu.offset + 0x2780);
@ -1733,7 +1731,7 @@ s32 spursTasketSaveTaskContext(SPUThread& spu)
} }
// Taskset dispatcher // Taskset dispatcher
void spursTasksetDispatch(SPUThread& spu) void spursTasksetDispatch(spu_thread& spu)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
auto taskset = vm::_ptr<CellSpursTaskset>(spu.offset + 0x2700); auto taskset = vm::_ptr<CellSpursTaskset>(spu.offset + 0x2700);
@ -1864,7 +1862,7 @@ void spursTasksetDispatch(SPUThread& spu)
} }
// Process a syscall request // Process a syscall request
s32 spursTasksetProcessSyscall(SPUThread& spu, u32 syscallNum, u32 args) s32 spursTasksetProcessSyscall(spu_thread& spu, u32 syscallNum, u32 args)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
auto taskset = vm::_ptr<CellSpursTaskset>(spu.offset + 0x2700); auto taskset = vm::_ptr<CellSpursTaskset>(spu.offset + 0x2700);
@ -1974,7 +1972,7 @@ s32 spursTasksetProcessSyscall(SPUThread& spu, u32 syscallNum, u32 args)
} }
// Initialise the Taskset PM // Initialise the Taskset PM
void spursTasksetInit(SPUThread& spu, u32 pollStatus) void spursTasksetInit(spu_thread& spu, u32 pollStatus)
{ {
auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700); auto ctxt = vm::_ptr<SpursTasksetContext>(spu.offset + 0x2700);
auto kernelCtxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100); auto kernelCtxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
@ -1995,7 +1993,7 @@ void spursTasksetInit(SPUThread& spu, u32 pollStatus)
} }
// Load an ELF // Load an ELF
s32 spursTasksetLoadElf(SPUThread& spu, u32* entryPoint, u32* lowestLoadAddr, u64 elfAddr, bool skipWriteableSegments) s32 spursTasksetLoadElf(spu_thread& spu, u32* entryPoint, u32* lowestLoadAddr, u64 elfAddr, bool skipWriteableSegments)
{ {
if (elfAddr == 0 || (elfAddr & 0x0F) != 0) if (elfAddr == 0 || (elfAddr & 0x0F) != 0)
{ {

View File

@ -79,7 +79,10 @@ error_code cellSyncMutexLock(ppu_thread& ppu, vm::ptr<CellSyncMutex> mutex)
// Wait until rel value is equal to old acq value // Wait until rel value is equal to old acq value
while (mutex->ctrl.load().rel != order) while (mutex->ctrl.load().rel != order)
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
_mm_mfence(); _mm_mfence();
@ -169,7 +172,10 @@ error_code cellSyncBarrierNotify(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barri
while (!barrier->ctrl.atomic_op<&CellSyncBarrier::try_notify>()) while (!barrier->ctrl.atomic_op<&CellSyncBarrier::try_notify>())
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
return CELL_OK; return CELL_OK;
@ -217,7 +223,10 @@ error_code cellSyncBarrierWait(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barrier
while (!barrier->ctrl.atomic_op<&CellSyncBarrier::try_wait>()) while (!barrier->ctrl.atomic_op<&CellSyncBarrier::try_wait>())
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
return CELL_OK; return CELL_OK;
@ -293,7 +302,10 @@ error_code cellSyncRwmRead(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::ptr<vo
// wait until `writers` is zero, increase `readers` // wait until `writers` is zero, increase `readers`
while (!rwm->ctrl.atomic_op<&CellSyncRwm::try_read_begin>()) while (!rwm->ctrl.atomic_op<&CellSyncRwm::try_read_begin>())
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
// copy data to buffer // copy data to buffer
@ -357,13 +369,19 @@ error_code cellSyncRwmWrite(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::cptr<
// wait until `writers` is zero, set to 1 // wait until `writers` is zero, set to 1
while (!rwm->ctrl.atomic_op<&CellSyncRwm::try_write_begin>()) while (!rwm->ctrl.atomic_op<&CellSyncRwm::try_write_begin>())
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
// wait until `readers` is zero // wait until `readers` is zero
while (rwm->ctrl.load().readers != 0) while (rwm->ctrl.load().readers != 0)
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
// copy data from buffer // copy data from buffer
@ -462,7 +480,10 @@ error_code cellSyncQueuePush(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::
return CellSyncQueue::try_push_begin(ctrl, depth, &position); return CellSyncQueue::try_push_begin(ctrl, depth, &position);
})) }))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
// copy data from the buffer at the position // copy data from the buffer at the position
@ -530,7 +551,10 @@ error_code cellSyncQueuePop(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::p
return CellSyncQueue::try_pop_begin(ctrl, depth, &position); return CellSyncQueue::try_pop_begin(ctrl, depth, &position);
})) }))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
// copy data at the position to the buffer // copy data at the position to the buffer
@ -598,7 +622,10 @@ error_code cellSyncQueuePeek(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::
return CellSyncQueue::try_peek_begin(ctrl, depth, &position); return CellSyncQueue::try_peek_begin(ctrl, depth, &position);
})) }))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
// copy data at the position to the buffer // copy data at the position to the buffer
@ -680,12 +707,18 @@ error_code cellSyncQueueClear(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue)
while (!queue->ctrl.atomic_op<&CellSyncQueue::try_clear_begin_1>()) while (!queue->ctrl.atomic_op<&CellSyncQueue::try_clear_begin_1>())
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
while (!queue->ctrl.atomic_op<&CellSyncQueue::try_clear_begin_2>()) while (!queue->ctrl.atomic_op<&CellSyncQueue::try_clear_begin_2>())
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
queue->ctrl.exchange({ 0, 0 }); queue->ctrl.exchange({ 0, 0 });
@ -1120,7 +1153,10 @@ error_code _cellSyncLFQueuePushBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> qu
break; break;
} }
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
const s32 depth = queue->m_depth; const s32 depth = queue->m_depth;
@ -1415,7 +1451,10 @@ error_code _cellSyncLFQueuePopBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> que
break; break;
} }
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
const s32 depth = queue->m_depth; const s32 depth = queue->m_depth;

View File

@ -232,20 +232,23 @@ s32 cellSysutilGetSystemParamString(CellSysutilParamId id, vm::ptr<char> buf, u3
return CELL_OK; return CELL_OK;
} }
s32 cellSysutilCheckCallback(ppu_thread& ppu) error_code cellSysutilCheckCallback(ppu_thread& ppu)
{ {
cellSysutil.trace("cellSysutilCheckCallback()"); cellSysutil.trace("cellSysutilCheckCallback()");
const auto cbm = fxm::get_always<sysutil_cb_manager>(); const auto cbm = fxm::get_always<sysutil_cb_manager>();
while (auto&& func = cbm->get_cb()) while (auto func = cbm->get_cb())
{ {
if (s32 res = func(ppu)) if (s32 res = func(ppu))
{ {
return res; return not_an_error(res);
} }
thread_ctrl::test(); if (ppu.is_stopped())
{
return 0;
}
} }
return CELL_OK; return CELL_OK;

View File

@ -19,6 +19,8 @@ extern "C"
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <cmath> #include <cmath>
#include "Utilities/lockless.h"
#include <variant>
std::mutex g_mutex_avcodec_open2; std::mutex g_mutex_avcodec_open2;
@ -26,15 +28,13 @@ LOG_CHANNEL(cellVdec);
vm::gvar<s32> _cell_vdec_prx_ver; // ??? vm::gvar<s32> _cell_vdec_prx_ver; // ???
enum class vdec_cmd : u32 constexpr struct vdec_start_seq_t{} vdec_start_seq{};
{ constexpr struct vdec_close_t{} vdec_close{};
null,
start_seq, struct vdec_cmd
end_seq, {
decode, s32 mode;
set_frc, CellVdecAuInfo au;
close,
}; };
struct vdec_frame struct vdec_frame
@ -60,14 +60,19 @@ struct vdec_frame
} }
}; };
struct vdec_thread : ppu_thread struct vdec_context final
{ {
static constexpr u32 id_base = 0xf0000000;
static constexpr u32 id_step = 0x00000100;
static constexpr u32 id_count = 1024;
AVCodec* codec{}; AVCodec* codec{};
AVCodecContext* ctx{}; AVCodecContext* ctx{};
SwsContext* sws{}; SwsContext* sws{};
const s32 type; shared_mutex mutex; // Used for 'out' queue (TODO)
const u32 profile;
const u32 type;
const u32 mem_addr; const u32 mem_addr;
const u32 mem_size; const u32 mem_size;
const vm::ptr<CellVdecCbMsg> cb_func; const vm::ptr<CellVdecCbMsg> cb_func;
@ -79,16 +84,16 @@ struct vdec_thread : ppu_thread
u64 next_dts{}; u64 next_dts{};
u64 ppu_tid{}; u64 ppu_tid{};
std::mutex mutex;
std::queue<vdec_frame> out; std::queue<vdec_frame> out;
u32 max_frames = 60; atomic_t<u32> out_max = 60;
atomic_t<u32> au_count{0}; atomic_t<u32> au_count{0};
vdec_thread(s32 type, u32 profile, u32 addr, u32 size, vm::ptr<CellVdecCbMsg> func, u32 arg, u32 prio, u32 stack) notifier in_cv;
: ppu_thread("HLE Video Decoder", prio, stack) lf_queue<std::variant<vdec_start_seq_t, vdec_close_t, vdec_cmd, CellVdecFrameRate>> in_cmd;
, type(type)
, profile(profile) vdec_context(s32 type, u32 profile, u32 addr, u32 size, vm::ptr<CellVdecCbMsg> func, u32 arg)
: type(type)
, mem_addr(addr) , mem_addr(addr)
, mem_size(size) , mem_size(size)
, cb_func(func) , cb_func(func)
@ -144,55 +149,51 @@ struct vdec_thread : ppu_thread
} }
} }
virtual ~vdec_thread() override ~vdec_context()
{ {
avcodec_close(ctx); avcodec_close(ctx);
avcodec_free_context(&ctx); avcodec_free_context(&ctx);
sws_freeContext(sws); sws_freeContext(sws);
} }
virtual std::string dump() const override void exec(ppu_thread& ppu, u32 vid)
{ {
// TODO ppu_tid = ppu.id;
return ppu_thread::dump();
std::shared_lock no_lock(in_cv, std::try_to_lock);
for (auto cmds = in_cmd.pop_all(); !Emu.IsStopped(); cmds ? cmds = cmds->pop_all() : cmds = in_cmd.pop_all())
{
if (!cmds)
{
in_cv.wait(1000);
continue;
} }
virtual void cpu_task() override if (std::get_if<vdec_start_seq_t>(&cmds->get()))
{ {
while (cmd64 cmd = cmd_wait())
{
switch (vdec_cmd vcmd = cmd.arg1<vdec_cmd>())
{
case vdec_cmd::start_seq:
{
cmd_pop();
avcodec_flush_buffers(ctx); avcodec_flush_buffers(ctx);
frc_set = 0; // TODO: ??? frc_set = 0; // TODO: ???
next_pts = 0; next_pts = 0;
next_dts = 0; next_dts = 0;
cellVdec.trace("Start sequence..."); cellVdec.trace("Start sequence...");
break;
} }
else if (auto* cmd = std::get_if<vdec_cmd>(&cmds->get()))
case vdec_cmd::decode:
case vdec_cmd::end_seq:
{ {
AVPacket packet{}; AVPacket packet{};
packet.pos = -1; packet.pos = -1;
u64 au_usrd{}; u64 au_usrd{};
if (vcmd == vdec_cmd::decode) if (cmd->mode != -1)
{ {
const u32 au_mode = cmd.arg2<u32>(); // TODO const u32 au_mode = cmd->mode;
const u32 au_addr = cmd_get(1).arg1<u32>(); const u32 au_addr = cmd->au.startAddr;
const u32 au_size = cmd_get(1).arg2<u32>(); const u32 au_size = cmd->au.size;
const u64 au_pts = cmd_get(2).as<u64>(); const u64 au_pts = u64{cmd->au.pts.upper} << 32 | cmd->au.pts.lower;
const u64 au_dts = cmd_get(3).as<u64>(); const u64 au_dts = u64{cmd->au.dts.upper} << 32 | cmd->au.dts.lower;
au_usrd = cmd_get(4).as<u64>(); // TODO au_usrd = cmd->au.userData;
const u64 au_spec = cmd_get(5).as<u64>(); // Unused
cmd_pop(5);
packet.data = vm::_ptr<u8>(au_addr); packet.data = vm::_ptr<u8>(au_addr);
packet.size = au_size; packet.size = au_size;
@ -217,16 +218,14 @@ struct vdec_thread : ppu_thread
} }
else else
{ {
cmd_pop();
packet.pts = AV_NOPTS_VALUE; packet.pts = AV_NOPTS_VALUE;
packet.dts = AV_NOPTS_VALUE; packet.dts = AV_NOPTS_VALUE;
cellVdec.trace("End sequence..."); cellVdec.trace("End sequence...");
} }
while (max_frames) while (out_max)
{ {
if (vcmd == vdec_cmd::end_seq) if (cmd->mode == -1)
{ {
break; break;
} }
@ -356,59 +355,52 @@ struct vdec_thread : ppu_thread
std::lock_guard{mutex}, out.push(std::move(frame)); std::lock_guard{mutex}, out.push(std::move(frame));
cb_func(*this, id, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, cb_arg); cb_func(ppu, vid, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, cb_arg);
lv2_obj::sleep(*this); lv2_obj::sleep(ppu);
} }
if (vcmd == vdec_cmd::decode) if (cmd->mode != -1)
{ {
break; break;
} }
} }
if (max_frames) if (out_max)
{ {
cb_func(*this, id, vcmd == vdec_cmd::decode ? CELL_VDEC_MSG_TYPE_AUDONE : CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, cb_arg); cb_func(ppu, vid, cmd->mode != -1 ? CELL_VDEC_MSG_TYPE_AUDONE : CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, cb_arg);
lv2_obj::sleep(*this); lv2_obj::sleep(ppu);
} }
if (vcmd == vdec_cmd::decode) if (cmd->mode != -1)
{ {
au_count--; au_count--;
} }
while (std::lock_guard{mutex}, max_frames && out.size() > max_frames) while (!Emu.IsStopped() && out_max && (std::lock_guard{mutex}, out.size() > out_max))
{ {
thread_ctrl::wait(); in_cv.wait(1000);
} }
}
else if (auto* frc = std::get_if<CellVdecFrameRate>(&cmds->get()))
{
frc_set = *frc;
}
else
{
break; break;
} }
case vdec_cmd::set_frc:
{
cmd_pop();
frc_set = cmd.arg2<u32>();
break;
}
case vdec_cmd::close:
{
cmd_pop();
state += cpu_flag::exit;
return;
}
default:
{
fmt::throw_exception("Unknown command (0x%x)" HERE, (u32)vcmd);
}
}
} }
} }
}; };
u32 vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0 */, vm::ptr<CellVdecAttr> attr) static void vdecEntry(ppu_thread& ppu, u32 vid)
{
idm::get<vdec_context>(vid)->exec(ppu, vid);
_sys_ppu_thread_exit(ppu, 0);
}
static u32 vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0 */, vm::ptr<CellVdecAttr> attr)
{ {
switch (type) // TODO: check profile levels switch (type) // TODO: check profile levels
{ {
@ -440,51 +432,51 @@ s32 cellVdecQueryAttrEx(vm::cptr<CellVdecTypeEx> type, vm::ptr<CellVdecAttr> att
return vdecQueryAttr(type->codecType, type->profileLevel, type->codecSpecificInfo_addr, attr); return vdecQueryAttr(type->codecType, type->profileLevel, type->codecSpecificInfo_addr, attr);
} }
template <typename T, typename U>
static s32 vdecOpen(ppu_thread& ppu, T type, U res, vm::cptr<CellVdecCb> cb, vm::ptr<u32> handle)
{
// Create decoder context
const u32 vid = idm::make<vdec_context>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg);
// Run thread
vm::var<u64> _tid;
vm::var<char[]> _name = vm::make_str("HLE Video Decoder");
ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, 0, vid, +res->ppuThreadPriority, +res->ppuThreadStackSize, SYS_PPU_THREAD_CREATE_INTERRUPT, +_name);
*handle = vid;
const auto thrd = idm::get<named_thread<ppu_thread>>(*_tid);
thrd->cmd_list
({
{ ppu_cmd::set_args, 1 }, u64{vid},
{ ppu_cmd::hle_call, FIND_FUNC(vdecEntry) },
});
thrd->state -= cpu_flag::stop;
thread_ctrl::notify(*thrd);
return CELL_OK;
}
s32 cellVdecOpen(ppu_thread& ppu, vm::cptr<CellVdecType> type, vm::cptr<CellVdecResource> res, vm::cptr<CellVdecCb> cb, vm::ptr<u32> handle) s32 cellVdecOpen(ppu_thread& ppu, vm::cptr<CellVdecType> type, vm::cptr<CellVdecResource> res, vm::cptr<CellVdecCb> cb, vm::ptr<u32> handle)
{ {
cellVdec.warning("cellVdecOpen(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle); cellVdec.warning("cellVdecOpen(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle);
// Create decoder thread return vdecOpen(ppu, type, res, cb, handle);
auto&& vdec = idm::make_ptr<ppu_thread, vdec_thread>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg, res->ppuThreadPriority, res->ppuThreadStackSize);
// Hack: store thread id (normally it should be pointer)
*handle = vdec->id;
vm::var<u64> _tid;
ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, 1148, 0, 900, 0x4000, SYS_PPU_THREAD_CREATE_INTERRUPT, vm::null);
vdec->gpr[13] = idm::get<ppu_thread>(*_tid)->gpr[13];
vdec->ppu_tid = *_tid;
vdec->run();
return CELL_OK;
} }
s32 cellVdecOpenEx(ppu_thread& ppu, vm::cptr<CellVdecTypeEx> type, vm::cptr<CellVdecResourceEx> res, vm::cptr<CellVdecCb> cb, vm::ptr<u32> handle) s32 cellVdecOpenEx(ppu_thread& ppu, vm::cptr<CellVdecTypeEx> type, vm::cptr<CellVdecResourceEx> res, vm::cptr<CellVdecCb> cb, vm::ptr<u32> handle)
{ {
cellVdec.warning("cellVdecOpenEx(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle); cellVdec.warning("cellVdecOpenEx(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle);
// Create decoder thread return vdecOpen(ppu, type, res, cb, handle);
auto&& vdec = idm::make_ptr<ppu_thread, vdec_thread>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg, res->ppuThreadPriority, res->ppuThreadStackSize);
// Hack: store thread id (normally it should be pointer)
*handle = vdec->id;
vm::var<u64> _tid;
ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, 1148, 0, 900, 0x4000, SYS_PPU_THREAD_CREATE_INTERRUPT, vm::null);
vdec->gpr[13] = idm::get<ppu_thread>(*_tid)->gpr[13];
vdec->ppu_tid = *_tid;
vdec->run();
return CELL_OK;
} }
s32 cellVdecClose(ppu_thread& ppu, u32 handle) s32 cellVdecClose(ppu_thread& ppu, u32 handle)
{ {
cellVdec.warning("cellVdecClose(handle=0x%x)", handle); cellVdec.warning("cellVdecClose(handle=0x%x)", handle);
const auto vdec = idm::get<ppu_thread, vdec_thread>(handle); const auto vdec = idm::get<vdec_context>(handle);
if (!vdec) if (!vdec)
{ {
@ -492,15 +484,9 @@ s32 cellVdecClose(ppu_thread& ppu, u32 handle)
} }
lv2_obj::sleep(ppu); lv2_obj::sleep(ppu);
vdec->out_max = 0;
{ vdec->in_cmd.push(vdec_close);
std::lock_guard lock(vdec->mutex); vdec->in_cv.notify_all();
vdec->cmd_push({vdec_cmd::close, 0});
vdec->max_frames = 0;
}
vdec->notify();
vdec->join();
ppu_execute<&sys_interrupt_thread_disestablish>(ppu, vdec->ppu_tid); ppu_execute<&sys_interrupt_thread_disestablish>(ppu, vdec->ppu_tid);
return CELL_OK; return CELL_OK;
} }
@ -509,15 +495,15 @@ s32 cellVdecStartSeq(u32 handle)
{ {
cellVdec.trace("cellVdecStartSeq(handle=0x%x)", handle); cellVdec.trace("cellVdecStartSeq(handle=0x%x)", handle);
const auto vdec = idm::get<ppu_thread, vdec_thread>(handle); const auto vdec = idm::get<vdec_context>(handle);
if (!vdec) if (!vdec)
{ {
return CELL_VDEC_ERROR_ARG; return CELL_VDEC_ERROR_ARG;
} }
vdec->cmd_push({vdec_cmd::start_seq, 0}); vdec->in_cmd.push(vdec_start_seq);
vdec->notify(); vdec->in_cv.notify_all();
return CELL_OK; return CELL_OK;
} }
@ -525,15 +511,15 @@ s32 cellVdecEndSeq(u32 handle)
{ {
cellVdec.warning("cellVdecEndSeq(handle=0x%x)", handle); cellVdec.warning("cellVdecEndSeq(handle=0x%x)", handle);
const auto vdec = idm::get<ppu_thread, vdec_thread>(handle); const auto vdec = idm::get<vdec_context>(handle);
if (!vdec) if (!vdec)
{ {
return CELL_VDEC_ERROR_ARG; return CELL_VDEC_ERROR_ARG;
} }
vdec->cmd_push({vdec_cmd::end_seq, 0}); vdec->in_cmd.push(vdec_cmd{-1});
vdec->notify(); vdec->in_cv.notify_all();
return CELL_OK; return CELL_OK;
} }
@ -541,30 +527,21 @@ s32 cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVdecAuInf
{ {
cellVdec.trace("cellVdecDecodeAu(handle=0x%x, mode=%d, auInfo=*0x%x)", handle, (s32)mode, auInfo); cellVdec.trace("cellVdecDecodeAu(handle=0x%x, mode=%d, auInfo=*0x%x)", handle, (s32)mode, auInfo);
const auto vdec = idm::get<ppu_thread, vdec_thread>(handle); const auto vdec = idm::get<vdec_context>(handle);
if (mode > CELL_VDEC_DEC_MODE_PB_SKIP || !vdec) if (mode < 0 || mode > CELL_VDEC_DEC_MODE_PB_SKIP || !vdec)
{ {
return CELL_VDEC_ERROR_ARG; return CELL_VDEC_ERROR_ARG;
} }
if (vdec->au_count.fetch_op([](u32& c) { if (c < 4) c++; }) >= 4) if (!vdec->au_count.try_inc(4))
{ {
return CELL_VDEC_ERROR_BUSY; return CELL_VDEC_ERROR_BUSY;
} }
// TODO: check info // TODO: check info
vdec->cmd_list vdec->in_cmd.push(vdec_cmd{mode, *auInfo});
({ vdec->in_cv.notify_all();
{ vdec_cmd::decode, mode },
{ auInfo->startAddr, auInfo->size },
u64{auInfo->pts.upper} << 32 | auInfo->pts.lower,
u64{auInfo->dts.upper} << 32 | auInfo->dts.lower,
auInfo->userData,
auInfo->codecSpecificData,
});
vdec->notify();
return CELL_OK; return CELL_OK;
} }
@ -572,7 +549,7 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
{ {
cellVdec.trace("cellVdecGetPicture(handle=0x%x, format=*0x%x, outBuff=*0x%x)", handle, format, outBuff); cellVdec.trace("cellVdecGetPicture(handle=0x%x, format=*0x%x, outBuff=*0x%x)", handle, format, outBuff);
const auto vdec = idm::get<ppu_thread, vdec_thread>(handle); const auto vdec = idm::get<vdec_context>(handle);
if (!format || !vdec) if (!format || !vdec)
{ {
@ -580,6 +557,7 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
} }
vdec_frame frame; vdec_frame frame;
bool notify = false;
{ {
std::lock_guard lock(vdec->mutex); std::lock_guard lock(vdec->mutex);
@ -591,14 +569,12 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
frame = std::move(vdec->out.front()); frame = std::move(vdec->out.front());
vdec->out.pop(); vdec->out.pop();
if (vdec->out.size() + 1 == vdec->out_max)
if (vdec->out.size() <= vdec->max_frames) notify = true;
{
vdec->notify();
}
} }
vdec->notify(); if (notify)
vdec->in_cv.notify_all();
if (outBuff) if (outBuff)
{ {
@ -698,7 +674,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
{ {
cellVdec.trace("cellVdecGetPicItem(handle=0x%x, picItem=**0x%x)", handle, picItem); cellVdec.trace("cellVdecGetPicItem(handle=0x%x, picItem=**0x%x)", handle, picItem);
const auto vdec = idm::get<ppu_thread, vdec_thread>(handle); const auto vdec = idm::get<vdec_context>(handle);
if (!vdec) if (!vdec)
{ {
@ -893,7 +869,7 @@ s32 cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frc)
{ {
cellVdec.trace("cellVdecSetFrameRate(handle=0x%x, frc=0x%x)", handle, (s32)frc); cellVdec.trace("cellVdecSetFrameRate(handle=0x%x, frc=0x%x)", handle, (s32)frc);
const auto vdec = idm::get<ppu_thread, vdec_thread>(handle); const auto vdec = idm::get<vdec_context>(handle);
if (!vdec) if (!vdec)
{ {
@ -901,8 +877,8 @@ s32 cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frc)
} }
// TODO: check frc value // TODO: check frc value
vdec->cmd_push({vdec_cmd::set_frc, frc}); vdec->in_cmd.push(frc);
vdec->notify(); vdec->in_cv.notify_all();
return CELL_OK; return CELL_OK;
} }
@ -966,4 +942,6 @@ DECLARE(ppu_module_manager::cellVdec)("libvdec", []()
REG_FUNC(libvdec, cellVdecSetFrameRate); REG_FUNC(libvdec, cellVdecSetFrameRate);
REG_FUNC(libvdec, cellVdecSetFrameRateExt); // 0xcffc42a5 REG_FUNC(libvdec, cellVdecSetFrameRateExt); // 0xcffc42a5
REG_FUNC(libvdec, cellVdecSetPts); // 0x3ce2e4f8 REG_FUNC(libvdec, cellVdecSetPts); // 0x3ce2e4f8
REG_FUNC(libvdec, vdecEntry).flag(MFF_HIDDEN);
}); });

View File

@ -326,7 +326,7 @@ struct surmixer_thread : ppu_thread
{ {
using ppu_thread::ppu_thread; using ppu_thread::ppu_thread;
virtual void cpu_task() override void non_task()
{ {
const auto g_audio = fxm::get<audio_config>(); const auto g_audio = fxm::get<audio_config>();
@ -489,9 +489,7 @@ s32 cellSurMixerCreate(vm::cptr<CellSurMixerConfig> config)
libmixer.warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", config->chStrips1, config->chStrips2, config->chStrips6, config->chStrips8); libmixer.warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", config->chStrips1, config->chStrips2, config->chStrips6, config->chStrips8);
auto&& thread = idm::make_ptr<ppu_thread, surmixer_thread>("Surmixer Thread"); //auto thread = idm::make_ptr<ppu_thread>("Surmixer Thread");
thread->run();
return CELL_OK; return CELL_OK;
} }

View File

@ -72,7 +72,11 @@ error_code sys_lwcond_signal(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
// call the syscall // call the syscall
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 1)) if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 1))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
lwmutex->all_info--; lwmutex->all_info--;
if (res != CELL_EPERM) if (res != CELL_EPERM)
@ -103,7 +107,11 @@ error_code sys_lwcond_signal(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
// call the syscall // call the syscall
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 3)) if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, -1, 3))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
lwmutex->all_info--; lwmutex->all_info--;
// unlock the lightweight mutex // unlock the lightweight mutex
@ -145,9 +153,12 @@ error_code sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
return res; return res;
} }
ppu.test_state(); if (ppu.test_stopped())
lwmutex->all_info += +res; {
return 0;
}
lwmutex->all_info += +res;
return CELL_OK; return CELL_OK;
} }
@ -167,7 +178,10 @@ error_code sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
// if locking succeeded, call the syscall // if locking succeeded, call the syscall
error_code res = _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 1); error_code res = _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 1);
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
if (res > 0) if (res > 0)
{ {
@ -206,7 +220,11 @@ error_code sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u
// call the syscall // call the syscall
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 1)) if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 1))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
lwmutex->all_info--; lwmutex->all_info--;
return res; return res;
@ -234,7 +252,11 @@ error_code sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u
// call the syscall // call the syscall
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 3)) if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 3))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
lwmutex->all_info--; lwmutex->all_info--;
// unlock the lightweight mutex // unlock the lightweight mutex

View File

@ -16,15 +16,20 @@ void sys_spinlock_initialize(vm::ptr<atomic_be_t<u32>> lock)
} }
} }
void sys_spinlock_lock(ppu_thread& ppu, vm::ptr<atomic_be_t<u32>> lock) error_code sys_spinlock_lock(ppu_thread& ppu, vm::ptr<atomic_be_t<u32>> lock)
{ {
sysPrxForUser.trace("sys_spinlock_lock(lock=*0x%x)", lock); sysPrxForUser.trace("sys_spinlock_lock(lock=*0x%x)", lock);
// Try to exchange with 0xabadcafe, repeat until exchanged with 0 // Try to exchange with 0xabadcafe, repeat until exchanged with 0
while (*lock || lock->exchange(0xabadcafe)) while (*lock || lock->exchange(0xabadcafe))
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
} }
}
return not_an_error(ppu.gpr[3]);
} }
s32 sys_spinlock_trylock(vm::ptr<atomic_be_t<u32>> lock) s32 sys_spinlock_trylock(vm::ptr<atomic_be_t<u32>> lock)

View File

@ -983,7 +983,11 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
if (Emu.IsReady() && fxm::import<ppu_module>([&] { return prx; })) if (Emu.IsReady() && fxm::import<ppu_module>([&] { return prx; }))
{ {
// Special loading mode // Special loading mode
auto ppu = idm::make_ptr<ppu_thread>("test_thread", 0, 0x100000); ppu_thread_params p{};
p.stack_addr = vm::cast(vm::alloc(0x100000, vm::stack, 4096));
p.stack_size = 0x100000;
auto ppu = idm::make_ptr<named_thread<ppu_thread>>("PPU[0x1000000] Thread (test_thread)", p, "test_thread", 0);
ppu->cmd_push({ppu_cmd::initialize, 0}); ppu->cmd_push({ppu_cmd::initialize, 0});
} }
@ -1463,7 +1467,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
} }
// Fix primary stack size // Fix primary stack size
switch (primary_stacksize) switch (u32 sz = primary_stacksize)
{ {
case 0x10: primary_stacksize = 32 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_32K case 0x10: primary_stacksize = 32 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_32K
case 0x20: primary_stacksize = 64 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_64K case 0x20: primary_stacksize = 64 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_64K
@ -1472,10 +1476,19 @@ void ppu_load_exec(const ppu_exec_object& elf)
case 0x50: primary_stacksize = 256 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_256K case 0x50: primary_stacksize = 256 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_256K
case 0x60: primary_stacksize = 512 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_512K case 0x60: primary_stacksize = 512 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_512K
case 0x70: primary_stacksize = 1024 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_1M case 0x70: primary_stacksize = 1024 * 1024; break; // SYS_PROCESS_PRIMARY_STACK_SIZE_1M
default:
{
primary_stacksize = sz >= 4096 ? ::align(std::min<u32>(sz, 0x100000), 4096) : 0x4000;
break;
}
} }
// Initialize main thread // Initialize main thread
auto ppu = idm::make_ptr<ppu_thread>("main_thread", primary_prio, primary_stacksize); ppu_thread_params p{};
p.stack_addr = vm::cast(vm::alloc(primary_stacksize, vm::stack, 4096));
p.stack_size = primary_stacksize;
auto ppu = idm::make_ptr<named_thread<ppu_thread>>("PPU[0x1000000] Thread (main_thread)", p, "main_thread", primary_prio);
// Write initial data (exitspawn) // Write initial data (exitspawn)
if (Emu.data.size()) if (Emu.data.size())

View File

@ -77,9 +77,6 @@ class ppu_static_module final
public: public:
const std::string name; const std::string name;
task_stack on_load;
task_stack on_unload;
std::unordered_map<u32, ppu_static_function, value_hash<u32>> functions; std::unordered_map<u32, ppu_static_function, value_hash<u32>> functions;
std::unordered_map<u32, ppu_static_variable, value_hash<u32>> variables; std::unordered_map<u32, ppu_static_variable, value_hash<u32>> variables;

View File

@ -334,37 +334,6 @@ extern void ppu_breakpoint(u32 addr, bool isAdding)
} }
} }
void ppu_thread::on_spawn()
{
if (g_cfg.core.thread_scheduler_enabled)
{
// Bind to primary set
thread_ctrl::set_thread_affinity_mask(thread_ctrl::get_affinity_mask(thread_class::ppu));
}
}
void ppu_thread::on_init(const std::shared_ptr<void>& _this)
{
if (!stack_addr)
{
// Allocate stack + gap between stacks
auto new_stack_base = vm::alloc(stack_size + 4096, vm::stack, 4096);
if (!new_stack_base)
{
fmt::throw_exception("Out of stack memory (size=0x%x)" HERE, stack_size);
}
const_cast<u32&>(stack_addr) = new_stack_base + 4096;
// Make the gap inaccessible
vm::page_protect(new_stack_base, 4096, 0, 0, vm::page_readable + vm::page_writable);
gpr[1] = ::align(stack_addr + stack_size, 0x200) - 0x200;
cpu_thread::on_init(_this);
}
}
//sets breakpoint, does nothing if there is a breakpoint there already //sets breakpoint, does nothing if there is a breakpoint there already
extern void ppu_set_breakpoint(u32 addr) extern void ppu_set_breakpoint(u32 addr)
{ {
@ -427,9 +396,15 @@ extern bool ppu_patch(u32 addr, u32 value)
return true; return true;
} }
void ppu_thread::on_cleanup(named_thread<ppu_thread>* _this)
{
// Remove thread id
idm::remove<named_thread<ppu_thread>>(_this->id);
}
std::string ppu_thread::get_name() const std::string ppu_thread::get_name() const
{ {
return fmt::format("PPU[0x%x] Thread (%s)", id, m_name); return fmt::format("PPU[0x%x] Thread (%s)", id, ppu_name.get());
} }
std::string ppu_thread::dump() const std::string ppu_thread::dump() const
@ -564,6 +539,12 @@ void ppu_thread::cpu_task()
cmd_pop(), ppu_function_manager::get().at(arg)(*this); cmd_pop(), ppu_function_manager::get().at(arg)(*this);
break; break;
} }
case ppu_cmd::ptr_call:
{
const ppu_function_t func = cmd_get(1).as<ppu_function_t>();
cmd_pop(1), func(*this);
break;
}
case ppu_cmd::initialize: case ppu_cmd::initialize:
{ {
cmd_pop(), ppu_initialize(); cmd_pop(), ppu_initialize();
@ -697,20 +678,38 @@ void ppu_thread::exec_task()
ppu_thread::~ppu_thread() ppu_thread::~ppu_thread()
{ {
if (stack_addr) // Deallocate Stack Area
{ vm::dealloc_verbose_nothrow(stack_addr, vm::stack);
vm::dealloc_verbose_nothrow(stack_addr - 4096, vm::stack);
}
} }
ppu_thread::ppu_thread(const std::string& name, u32 prio, u32 stack) ppu_thread::ppu_thread(const ppu_thread_params& param, std::string_view name, u32 prio, int detached)
: cpu_thread(idm::last_id()) : cpu_thread(idm::last_id())
, prio(prio) , prio(prio)
, stack_size(stack >= 0x1000 ? ::align(std::min<u32>(stack, 0x100000), 0x1000) : 0x4000) , stack_size(param.stack_size)
, stack_addr(0) , stack_addr(param.stack_addr)
, start_time(get_system_time()) , start_time(get_system_time())
, m_name(name) , joiner(-!!detached)
, ppu_name(name)
{ {
gpr[1] = ::align(stack_addr + stack_size, 0x200) - 0x200;
gpr[13] = param.tls_addr;
if (detached >= 0 && id != id_base)
{
// Initialize thread entry point
cmd_list
({
{ppu_cmd::set_args, 2}, param.arg0, param.arg1,
{ppu_cmd::lle_call, param.entry},
});
}
else
{
// Save entry for further use (interrupt handler workaround)
gpr[2] = param.entry;
}
// Trigger the scheduler // Trigger the scheduler
state += cpu_flag::suspend; state += cpu_flag::suspend;
@ -765,7 +764,7 @@ cmd64 ppu_thread::cmd_wait()
{ {
if (UNLIKELY(state)) if (UNLIKELY(state))
{ {
if (state & (cpu_flag::stop + cpu_flag::exit)) if (is_stopped())
{ {
return cmd64{}; return cmd64{};
} }
@ -802,8 +801,7 @@ void ppu_thread::fast_call(u32 addr, u32 rtoc)
g_tls_log_prefix = [] g_tls_log_prefix = []
{ {
const auto _this = static_cast<ppu_thread*>(get_current_cpu_thread()); const auto _this = static_cast<ppu_thread*>(get_current_cpu_thread());
return fmt::format("%s [0x%08x]", thread_ctrl::get_name(), _this->cia);
return fmt::format("%s [0x%08x]", _this->get_name(), _this->cia);
}; };
auto at_ret = gsl::finally([&]() auto at_ret = gsl::finally([&]()
@ -930,7 +928,11 @@ extern void sse_cellbe_stvrx_v0(u64 addr, __m128i a);
static void ppu_check(ppu_thread& ppu, u64 addr) static void ppu_check(ppu_thread& ppu, u64 addr)
{ {
ppu.cia = ::narrow<u32>(addr); ppu.cia = ::narrow<u32>(addr);
ppu.test_state();
if (ppu.test_stopped())
{
return;
}
} }
static void ppu_trace(u64 addr) static void ppu_trace(u64 addr)

View File

@ -14,6 +14,7 @@ enum class ppu_cmd : u32
set_args, // Set general-purpose args (+arg cmd) set_args, // Set general-purpose args (+arg cmd)
lle_call, // Load addr and rtoc at *arg or *gpr[arg] and execute lle_call, // Load addr and rtoc at *arg or *gpr[arg] and execute
hle_call, // Execute function by index (arg) hle_call, // Execute function by index (arg)
ptr_call, // Execute function by pointer
initialize, // ppu_initialize() initialize, // ppu_initialize()
sleep, sleep,
reset_stack, // resets stack address reset_stack, // resets stack address
@ -24,6 +25,17 @@ enum class ppu_syscall_code : u64
{ {
}; };
// ppu_thread constructor argument
struct ppu_thread_params
{
vm::addr_t stack_addr;
u32 stack_size;
u32 tls_addr;
u32 entry;
u64 arg0;
u64 arg1;
};
class ppu_thread : public cpu_thread class ppu_thread : public cpu_thread
{ {
public: public:
@ -31,17 +43,17 @@ public:
static const u32 id_step = 1; static const u32 id_step = 1;
static const u32 id_count = 2048; static const u32 id_count = 2048;
virtual void on_spawn() override; static void on_cleanup(named_thread<ppu_thread>*);
virtual void on_init(const std::shared_ptr<void>&) override;
virtual std::string get_name() const override; virtual std::string get_name() const override;
virtual std::string dump() const override; virtual std::string dump() const override;
virtual void cpu_task() override; virtual void cpu_task() override final;
virtual void cpu_sleep() override; virtual void cpu_sleep() override;
virtual void cpu_mem() override; virtual void cpu_mem() override;
virtual void cpu_unmem() override; virtual void cpu_unmem() override;
virtual ~ppu_thread() override; virtual ~ppu_thread() override;
ppu_thread(const std::string& name, u32 prio = 0, u32 stack = 0x10000); ppu_thread(const ppu_thread_params&, std::string_view name, u32 prio, int detached = 0);
u64 gpr[32] = {}; // General-Purpose Registers u64 gpr[32] = {}; // General-Purpose Registers
f64 fpr[32] = {}; // Floating Point Registers f64 fpr[32] = {}; // Floating Point Registers
@ -153,7 +165,7 @@ public:
u64 start_time{0}; // Sleep start timepoint u64 start_time{0}; // Sleep start timepoint
const char* last_function{}; // Last function name for diagnosis, optimized for speed. const char* last_function{}; // Last function name for diagnosis, optimized for speed.
const std::string m_name; // Thread name lf_value<std::string> ppu_name; // Thread name
be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64)); be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64));
void exec_task(); void exec_task();

View File

@ -9,39 +9,7 @@
// Originally, SPU MFC registers are accessed externally in a concurrent manner (don't mix with channels, SPU MFC channels are isolated) // Originally, SPU MFC registers are accessed externally in a concurrent manner (don't mix with channels, SPU MFC channels are isolated)
thread_local spu_mfc_cmd g_tls_mfc[8] = {}; thread_local spu_mfc_cmd g_tls_mfc[8] = {};
void RawSPUThread::cpu_task() bool spu_thread::read_reg(const u32 addr, u32& value)
{
// get next PC and SPU Interrupt status
pc = npc.exchange(0);
set_interrupt_status((pc & 1) != 0);
pc &= 0x3fffc;
SPUThread::cpu_task();
// save next PC and current SPU Interrupt status
npc = pc | (interrupts_enabled);
}
void RawSPUThread::on_init(const std::shared_ptr<void>& _this)
{
if (!offset)
{
// Install correct SPU index and LS address
const_cast<u32&>(index) = id;
const_cast<u32&>(offset) = verify(HERE, vm::falloc(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index, 0x40000));
cpu_thread::on_init(_this);
}
}
RawSPUThread::RawSPUThread(const std::string& name)
: SPUThread(name, 0, nullptr)
{
}
bool RawSPUThread::read_reg(const u32 addr, u32& value)
{ {
const u32 offset = addr - RAW_SPU_BASE_ADDR - index * RAW_SPU_OFFSET - RAW_SPU_PROB_OFFSET; const u32 offset = addr - RAW_SPU_BASE_ADDR - index * RAW_SPU_OFFSET - RAW_SPU_PROB_OFFSET;
@ -101,7 +69,7 @@ bool RawSPUThread::read_reg(const u32 addr, u32& value)
return false; return false;
} }
bool RawSPUThread::write_reg(const u32 addr, const u32 value) bool spu_thread::write_reg(const u32 addr, const u32 value)
{ {
auto try_start = [this]() auto try_start = [this]()
{ {
@ -116,7 +84,8 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value)
return true; return true;
})) }))
{ {
run(); state -= cpu_flag::stop;
thread_ctrl::notify(static_cast<named_thread<spu_thread>&>(*this));
} }
}; };
@ -291,7 +260,11 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value)
void spu_load_exec(const spu_exec_object& elf) void spu_load_exec(const spu_exec_object& elf)
{ {
auto spu = idm::make_ptr<RawSPUThread>("TEST_SPU"); auto ls0 = vm::cast(vm::falloc(RAW_SPU_BASE_ADDR, 0x40000, vm::spu));
auto spu = idm::make_ptr<named_thread<spu_thread>>("TEST_SPU", ls0, nullptr, 0, "");
spu_thread::g_raw_spu_ctr++;
spu_thread::g_raw_spu_id[0] = spu->id;
for (const auto& prog : elf.progs) for (const auto& prog : elf.progs)
{ {
@ -301,6 +274,5 @@ void spu_load_exec(const spu_exec_object& elf)
} }
} }
spu->cpu_init();
spu->npc = elf.header.e_entry; spu->npc = elf.header.e_entry;
} }

View File

@ -1,20 +1,3 @@
#pragma once #pragma once
#include "SPUThread.h" #include "SPUThread.h"
class RawSPUThread final : public SPUThread
{
void cpu_task() override;
public:
static const u32 id_base = 0;
static const u32 id_step = 1;
static const u32 id_count = 5;
void on_init(const std::shared_ptr<void>&) override;
RawSPUThread(const std::string& name);
bool read_reg(const u32 addr, u32& value);
bool write_reg(const u32 addr, const u32 value);
};

View File

@ -16,11 +16,11 @@
#include "SPUASMJITRecompiler.h" #include "SPUASMJITRecompiler.h"
#define SPU_OFF_128(x, ...) asmjit::x86::oword_ptr(*cpu, offset32(&SPUThread::x, ##__VA_ARGS__)) #define SPU_OFF_128(x, ...) asmjit::x86::oword_ptr(*cpu, offset32(&spu_thread::x, ##__VA_ARGS__))
#define SPU_OFF_64(x, ...) asmjit::x86::qword_ptr(*cpu, offset32(&SPUThread::x, ##__VA_ARGS__)) #define SPU_OFF_64(x, ...) asmjit::x86::qword_ptr(*cpu, offset32(&spu_thread::x, ##__VA_ARGS__))
#define SPU_OFF_32(x, ...) asmjit::x86::dword_ptr(*cpu, offset32(&SPUThread::x, ##__VA_ARGS__)) #define SPU_OFF_32(x, ...) asmjit::x86::dword_ptr(*cpu, offset32(&spu_thread::x, ##__VA_ARGS__))
#define SPU_OFF_16(x, ...) asmjit::x86::word_ptr(*cpu, offset32(&SPUThread::x, ##__VA_ARGS__)) #define SPU_OFF_16(x, ...) asmjit::x86::word_ptr(*cpu, offset32(&spu_thread::x, ##__VA_ARGS__))
#define SPU_OFF_8(x, ...) asmjit::x86::byte_ptr(*cpu, offset32(&SPUThread::x, ##__VA_ARGS__)) #define SPU_OFF_8(x, ...) asmjit::x86::byte_ptr(*cpu, offset32(&spu_thread::x, ##__VA_ARGS__))
extern const spu_decoder<spu_interpreter_fast> g_spu_interpreter_fast; // TODO: avoid extern const spu_decoder<spu_interpreter_fast> g_spu_interpreter_fast; // TODO: avoid
const spu_decoder<spu_recompiler> s_spu_decoder; const spu_decoder<spu_recompiler> s_spu_decoder;
@ -1177,12 +1177,12 @@ inline asmjit::X86Mem spu_recompiler::XmmConst(__m128i data)
return XmmConst(v128::fromV(data)); return XmmConst(v128::fromV(data));
} }
static void check_state_ret(SPUThread& _spu, void*, u8*) static void check_state_ret(spu_thread& _spu, void*, u8*)
{ {
// MSVC workaround (TCO) // MSVC workaround (TCO)
} }
static void check_state(SPUThread* _spu, spu_function_t _ret) static void check_state(spu_thread* _spu, spu_function_t _ret)
{ {
if (_spu->state && _spu->check_state()) if (_spu->state && _spu->check_state())
{ {
@ -1209,7 +1209,7 @@ void spu_recompiler::branch_fixed(u32 target)
return; return;
} }
c->mov(x86::rax, x86::qword_ptr(*cpu, offset32(&SPUThread::jit_dispatcher) + target * 2)); c->mov(x86::rax, x86::qword_ptr(*cpu, offset32(&spu_thread::jit_dispatcher) + target * 2));
c->mov(SPU_OFF_32(pc), target); c->mov(SPU_OFF_32(pc), target);
c->cmp(SPU_OFF_32(state), 0); c->cmp(SPU_OFF_32(state), 0);
c->jnz(label_stop); c->jnz(label_stop);
@ -1251,7 +1251,7 @@ void spu_recompiler::branch_indirect(spu_opcode_t op, bool jt, bool ret)
} }
else if (op.e) else if (op.e)
{ {
auto _throw = [](SPUThread* _spu) auto _throw = [](spu_thread* _spu)
{ {
fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x)" HERE, +_spu->ch_event_mask); fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x)" HERE, +_spu->ch_event_mask);
}; };
@ -1270,7 +1270,7 @@ void spu_recompiler::branch_indirect(spu_opcode_t op, bool jt, bool ret)
c->jmp(no_intr); c->jmp(no_intr);
c->bind(fail); c->bind(fail);
c->mov(SPU_OFF_32(pc), *addr); c->mov(SPU_OFF_32(pc), *addr);
c->jmp(imm_ptr<void(*)(SPUThread*)>(_throw)); c->jmp(imm_ptr<void(*)(spu_thread*)>(_throw));
// Save addr in srr0 and disable interrupts // Save addr in srr0 and disable interrupts
c->bind(intr); c->bind(intr);
@ -1292,7 +1292,7 @@ void spu_recompiler::branch_indirect(spu_opcode_t op, bool jt, bool ret)
if (!jt && g_cfg.core.spu_block_size != spu_block_size_type::giga) if (!jt && g_cfg.core.spu_block_size != spu_block_size_type::giga)
{ {
// Simply external call (return or indirect call) // Simply external call (return or indirect call)
c->mov(x86::r10, x86::qword_ptr(*cpu, addr->r64(), 1, offset32(&SPUThread::jit_dispatcher))); c->mov(x86::r10, x86::qword_ptr(*cpu, addr->r64(), 1, offset32(&spu_thread::jit_dispatcher)));
} }
else else
{ {
@ -1311,7 +1311,7 @@ void spu_recompiler::branch_indirect(spu_opcode_t op, bool jt, bool ret)
c->lea(x86::r10, x86::qword_ptr(instr_table)); c->lea(x86::r10, x86::qword_ptr(instr_table));
c->cmp(qw1->r32(), end - start); c->cmp(qw1->r32(), end - start);
c->lea(x86::r10, x86::qword_ptr(x86::r10, *qw1, 1, 0)); c->lea(x86::r10, x86::qword_ptr(x86::r10, *qw1, 1, 0));
c->lea(*qw1, x86::qword_ptr(*cpu, addr->r64(), 1, offset32(&SPUThread::jit_dispatcher))); c->lea(*qw1, x86::qword_ptr(*cpu, addr->r64(), 1, offset32(&spu_thread::jit_dispatcher)));
c->cmovae(x86::r10, *qw1); c->cmovae(x86::r10, *qw1);
c->mov(x86::r10, x86::qword_ptr(x86::r10)); c->mov(x86::r10, x86::qword_ptr(x86::r10));
} }
@ -1321,7 +1321,7 @@ void spu_recompiler::branch_indirect(spu_opcode_t op, bool jt, bool ret)
// Get stack pointer, try to use native return address (check SPU return address) // Get stack pointer, try to use native return address (check SPU return address)
c->mov(qw1->r32(), SPU_OFF_32(gpr, 1, &v128::_u32, 3)); c->mov(qw1->r32(), SPU_OFF_32(gpr, 1, &v128::_u32, 3));
c->and_(qw1->r32(), 0x3fff0); c->and_(qw1->r32(), 0x3fff0);
c->lea(*qw1, x86::qword_ptr(*cpu, *qw1, 0, ::offset32(&SPUThread::stack_mirror))); c->lea(*qw1, x86::qword_ptr(*cpu, *qw1, 0, ::offset32(&spu_thread::stack_mirror)));
c->cmp(x86::dword_ptr(*qw1, 8), *addr); c->cmp(x86::dword_ptr(*qw1, 8), *addr);
c->cmove(x86::r10, x86::qword_ptr(*qw1)); c->cmove(x86::r10, x86::qword_ptr(*qw1));
} }
@ -1352,7 +1352,7 @@ void spu_recompiler::branch_set_link(u32 target)
// Get stack pointer, write native and SPU return addresses into the stack mirror // Get stack pointer, write native and SPU return addresses into the stack mirror
c->mov(qw1->r32(), SPU_OFF_32(gpr, 1, &v128::_u32, 3)); c->mov(qw1->r32(), SPU_OFF_32(gpr, 1, &v128::_u32, 3));
c->and_(qw1->r32(), 0x3fff0); c->and_(qw1->r32(), 0x3fff0);
c->lea(*qw1, x86::qword_ptr(*cpu, *qw1, 0, ::offset32(&SPUThread::stack_mirror))); c->lea(*qw1, x86::qword_ptr(*cpu, *qw1, 0, ::offset32(&spu_thread::stack_mirror)));
c->lea(x86::r10, x86::qword_ptr(ret)); c->lea(x86::r10, x86::qword_ptr(ret));
c->mov(x86::qword_ptr(*qw1, 0), x86::r10); c->mov(x86::qword_ptr(*qw1, 0), x86::r10);
c->mov(x86::qword_ptr(*qw1, 8), target); c->mov(x86::qword_ptr(*qw1, 8), target);
@ -1365,7 +1365,7 @@ void spu_recompiler::branch_set_link(u32 target)
c->mov(qw1->r32(), SPU_OFF_32(gpr, 1, &v128::_u32, 3)); c->mov(qw1->r32(), SPU_OFF_32(gpr, 1, &v128::_u32, 3));
c->and_(qw1->r32(), 0x3fff0); c->and_(qw1->r32(), 0x3fff0);
c->pcmpeqd(x86::xmm0, x86::xmm0); c->pcmpeqd(x86::xmm0, x86::xmm0);
c->movdqa(x86::dqword_ptr(*cpu, *qw1, 0, ::offset32(&SPUThread::stack_mirror)), x86::xmm0); c->movdqa(x86::dqword_ptr(*cpu, *qw1, 0, ::offset32(&spu_thread::stack_mirror)), x86::xmm0);
c->jmp(target); c->jmp(target);
}); });
} }
@ -1374,7 +1374,7 @@ void spu_recompiler::branch_set_link(u32 target)
void spu_recompiler::fall(spu_opcode_t op) void spu_recompiler::fall(spu_opcode_t op)
{ {
auto gate = [](SPUThread* _spu, u32 opcode, spu_inter_func_t _func, spu_function_t _ret) auto gate = [](spu_thread* _spu, u32 opcode, spu_inter_func_t _func, spu_function_t _ret)
{ {
if (!_func(*_spu, {opcode})) if (!_func(*_spu, {opcode}))
{ {
@ -1391,7 +1391,7 @@ void spu_recompiler::fall(spu_opcode_t op)
c->mov(*ls, op.opcode); c->mov(*ls, op.opcode);
c->mov(*qw0, asmjit::imm_ptr(asmjit::Internal::ptr_cast<void*>(g_spu_interpreter_fast.decode(op.opcode)))); c->mov(*qw0, asmjit::imm_ptr(asmjit::Internal::ptr_cast<void*>(g_spu_interpreter_fast.decode(op.opcode))));
c->lea(*qw1, asmjit::x86::qword_ptr(next)); c->lea(*qw1, asmjit::x86::qword_ptr(next));
c->jmp(asmjit::imm_ptr<void(*)(SPUThread*, u32, spu_inter_func_t, spu_function_t)>(gate)); c->jmp(asmjit::imm_ptr<void(*)(spu_thread*, u32, spu_inter_func_t, spu_function_t)>(gate));
c->align(asmjit::kAlignCode, 16); c->align(asmjit::kAlignCode, 16);
c->bind(next); c->bind(next);
} }
@ -1442,13 +1442,13 @@ void spu_recompiler::get_events()
if (utils::has_avx()) if (utils::has_avx())
{ {
c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&SPUThread::rdata) + 0)); c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&spu_thread::rdata) + 0));
c->vxorps(x86::ymm1, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 0)); c->vxorps(x86::ymm1, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 0));
c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&SPUThread::rdata) + 32)); c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&spu_thread::rdata) + 32));
c->vxorps(x86::ymm2, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 32)); c->vxorps(x86::ymm2, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 32));
c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&SPUThread::rdata) + 64)); c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&spu_thread::rdata) + 64));
c->vxorps(x86::ymm3, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 64)); c->vxorps(x86::ymm3, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 64));
c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&SPUThread::rdata) + 96)); c->vmovups(x86::ymm0, x86::yword_ptr(*cpu, offset32(&spu_thread::rdata) + 96));
c->vxorps(x86::ymm4, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 96)); c->vxorps(x86::ymm4, x86::ymm0, x86::yword_ptr(*qw0, *addr, 0, 96));
c->vorps(x86::ymm0, x86::ymm1, x86::ymm2); c->vorps(x86::ymm0, x86::ymm1, x86::ymm2);
c->vorps(x86::ymm1, x86::ymm3, x86::ymm4); c->vorps(x86::ymm1, x86::ymm3, x86::ymm4);
@ -1460,11 +1460,11 @@ void spu_recompiler::get_events()
else else
{ {
c->movaps(x86::xmm0, x86::dqword_ptr(*qw0, *addr)); c->movaps(x86::xmm0, x86::dqword_ptr(*qw0, *addr));
c->xorps(x86::xmm0, x86::dqword_ptr(*cpu, offset32(&SPUThread::rdata) + 0)); c->xorps(x86::xmm0, x86::dqword_ptr(*cpu, offset32(&spu_thread::rdata) + 0));
for (u32 i = 16; i < 128; i += 16) for (u32 i = 16; i < 128; i += 16)
{ {
c->movaps(x86::xmm1, x86::dqword_ptr(*qw0, *addr, 0, i)); c->movaps(x86::xmm1, x86::dqword_ptr(*qw0, *addr, 0, i));
c->xorps(x86::xmm1, x86::dqword_ptr(*cpu, offset32(&SPUThread::rdata) + i)); c->xorps(x86::xmm1, x86::dqword_ptr(*cpu, offset32(&spu_thread::rdata) + i));
c->orps(x86::xmm0, x86::xmm1); c->orps(x86::xmm0, x86::xmm1);
} }
@ -1495,7 +1495,7 @@ void spu_recompiler::get_events()
// Check decrementer event (unlikely) // Check decrementer event (unlikely)
after.emplace_back([=] after.emplace_back([=]
{ {
auto sub = [](SPUThread* _spu, spu_function_t _ret) auto sub = [](spu_thread* _spu, spu_function_t _ret)
{ {
if ((_spu->ch_dec_value - (get_timebased_time() - _spu->ch_dec_start_timestamp)) >> 31) if ((_spu->ch_dec_value - (get_timebased_time() - _spu->ch_dec_start_timestamp)) >> 31)
{ {
@ -1508,7 +1508,7 @@ void spu_recompiler::get_events()
c->bind(tcheck); c->bind(tcheck);
c->lea(*ls, x86::qword_ptr(label2)); c->lea(*ls, x86::qword_ptr(label2));
c->jmp(imm_ptr<void(*)(SPUThread*, spu_function_t)>(sub)); c->jmp(imm_ptr<void(*)(spu_thread*, spu_function_t)>(sub));
}); });
// Check whether SPU_EVENT_TM is already set // Check whether SPU_EVENT_TM is already set
@ -1527,13 +1527,13 @@ void spu_recompiler::get_events()
after.emplace_back([=] after.emplace_back([=]
{ {
auto _throw = [](SPUThread* _spu) auto _throw = [](spu_thread* _spu)
{ {
fmt::throw_exception("SPU Events not implemented (mask=0x%x)" HERE, +_spu->ch_event_mask); fmt::throw_exception("SPU Events not implemented (mask=0x%x)" HERE, +_spu->ch_event_mask);
}; };
c->bind(fail); c->bind(fail);
c->jmp(imm_ptr<void(*)(SPUThread*)>(_throw)); c->jmp(imm_ptr<void(*)(spu_thread*)>(_throw));
}); });
// Load active events into addr // Load active events into addr
@ -1547,18 +1547,18 @@ void spu_recompiler::get_events()
void spu_recompiler::UNK(spu_opcode_t op) void spu_recompiler::UNK(spu_opcode_t op)
{ {
auto gate = [](SPUThread* _spu, u32 op) auto gate = [](spu_thread* _spu, u32 op)
{ {
fmt::throw_exception("Unknown/Illegal instruction (0x%08x)" HERE, op); fmt::throw_exception("Unknown/Illegal instruction (0x%08x)" HERE, op);
}; };
c->mov(SPU_OFF_32(pc), m_pos); c->mov(SPU_OFF_32(pc), m_pos);
c->mov(*ls, op.opcode); c->mov(*ls, op.opcode);
c->jmp(asmjit::imm_ptr<void(*)(SPUThread*, u32)>(gate)); c->jmp(asmjit::imm_ptr<void(*)(spu_thread*, u32)>(gate));
m_pos = -1; m_pos = -1;
} }
void spu_stop(SPUThread* _spu, u32 code, spu_function_t _ret) void spu_stop(spu_thread* _spu, u32 code, spu_function_t _ret)
{ {
if (!_spu->stop_and_signal(code)) if (!_spu->stop_and_signal(code))
{ {
@ -1619,12 +1619,12 @@ void spu_recompiler::MFSPR(spu_opcode_t op)
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
} }
static void spu_rdch_ret(SPUThread& spu, void*, u32) static void spu_rdch_ret(spu_thread& spu, void*, u32)
{ {
// MSVC workaround (TCO) // MSVC workaround (TCO)
} }
static void spu_rdch(SPUThread* _spu, u32 ch, void(*_ret)(SPUThread&, void*, u32)) static void spu_rdch(spu_thread* _spu, u32 ch, void(*_ret)(spu_thread&, void*, u32))
{ {
const s64 result = _spu->get_ch_value(ch); const s64 result = _spu->get_ch_value(ch);
@ -1733,7 +1733,7 @@ void spu_recompiler::RDCH(spu_opcode_t op)
{ {
LOG_WARNING(SPU, "[0x%x] RDCH: RdDec", m_pos); LOG_WARNING(SPU, "[0x%x] RDCH: RdDec", m_pos);
auto sub1 = [](SPUThread* _spu, v128* _res, spu_function_t _ret) auto sub1 = [](spu_thread* _spu, v128* _res, spu_function_t _ret)
{ {
const u32 out = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp); const u32 out = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp);
@ -1744,7 +1744,7 @@ void spu_recompiler::RDCH(spu_opcode_t op)
_ret(*_spu, _spu->_ptr<u8>(0), nullptr); _ret(*_spu, _spu->_ptr<u8>(0), nullptr);
}; };
auto sub2 = [](SPUThread* _spu, v128* _res, spu_function_t _ret) auto sub2 = [](spu_thread* _spu, v128* _res, spu_function_t _ret)
{ {
const u32 out = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp); const u32 out = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp);
@ -1752,7 +1752,7 @@ void spu_recompiler::RDCH(spu_opcode_t op)
_ret(*_spu, _spu->_ptr<u8>(0), nullptr); _ret(*_spu, _spu->_ptr<u8>(0), nullptr);
}; };
using ftype = void (*)(SPUThread*, v128*, spu_function_t); using ftype = void (*)(spu_thread*, v128*, spu_function_t);
asmjit::Label next = c->newLabel(); asmjit::Label next = c->newLabel();
c->mov(SPU_OFF_32(pc), m_pos); c->mov(SPU_OFF_32(pc), m_pos);
@ -1817,7 +1817,7 @@ void spu_recompiler::RDCH(spu_opcode_t op)
c->movdqa(SPU_OFF_128(gpr, op.rt), x86::xmm0); c->movdqa(SPU_OFF_128(gpr, op.rt), x86::xmm0);
} }
static void spu_rchcnt(SPUThread* _spu, u32 ch, void(*_ret)(SPUThread&, void*, u32 res)) static void spu_rchcnt(spu_thread* _spu, u32 ch, void(*_ret)(spu_thread&, void*, u32 res))
{ {
// Put result into the third argument // Put result into the third argument
const u32 res = _spu->get_ch_count(ch); const u32 res = _spu->get_ch_count(ch);
@ -2565,12 +2565,12 @@ void spu_recompiler::MTSPR(spu_opcode_t op)
// Check SPUInterpreter for notes. // Check SPUInterpreter for notes.
} }
static void spu_wrch_ret(SPUThread& _spu, void*, u8*) static void spu_wrch_ret(spu_thread& _spu, void*, u8*)
{ {
// MSVC workaround (TCO) // MSVC workaround (TCO)
} }
static void spu_wrch(SPUThread* _spu, u32 ch, u32 value, spu_function_t _ret) static void spu_wrch(spu_thread* _spu, u32 ch, u32 value, spu_function_t _ret)
{ {
if (!_spu->set_ch_value(ch, value)) if (!_spu->set_ch_value(ch, value))
{ {
@ -2580,7 +2580,7 @@ static void spu_wrch(SPUThread* _spu, u32 ch, u32 value, spu_function_t _ret)
_ret(*_spu, _spu->_ptr<u8>(0), nullptr); _ret(*_spu, _spu->_ptr<u8>(0), nullptr);
} }
static void spu_wrch_mfc(SPUThread* _spu, spu_function_t _ret) static void spu_wrch_mfc(spu_thread* _spu, spu_function_t _ret)
{ {
if (!_spu->process_mfc_cmd(_spu->ch_mfc_cmd)) if (!_spu->process_mfc_cmd(_spu->ch_mfc_cmd))
{ {
@ -2744,7 +2744,7 @@ void spu_recompiler::WRCH(spu_opcode_t op)
} }
case MFC_WrListStallAck: case MFC_WrListStallAck:
{ {
auto sub = [](SPUThread* _spu, spu_function_t _ret) auto sub = [](spu_thread* _spu, spu_function_t _ret)
{ {
_spu->do_mfc(true); _spu->do_mfc(true);
_ret(*_spu, _spu->_ptr<u8>(0), nullptr); _ret(*_spu, _spu->_ptr<u8>(0), nullptr);
@ -2756,14 +2756,14 @@ void spu_recompiler::WRCH(spu_opcode_t op)
c->btr(SPU_OFF_32(ch_stall_mask), qw0->r32()); c->btr(SPU_OFF_32(ch_stall_mask), qw0->r32());
c->jnc(ret); c->jnc(ret);
c->lea(*ls, x86::qword_ptr(ret)); c->lea(*ls, x86::qword_ptr(ret));
c->jmp(imm_ptr<void(*)(SPUThread*, spu_function_t)>(sub)); c->jmp(imm_ptr<void(*)(spu_thread*, spu_function_t)>(sub));
c->align(kAlignCode, 16); c->align(kAlignCode, 16);
c->bind(ret); c->bind(ret);
return; return;
} }
case SPU_WrDec: case SPU_WrDec:
{ {
auto sub = [](SPUThread* _spu, spu_function_t _ret) auto sub = [](spu_thread* _spu, spu_function_t _ret)
{ {
_spu->ch_dec_start_timestamp = get_timebased_time(); _spu->ch_dec_start_timestamp = get_timebased_time();
_ret(*_spu, _spu->_ptr<u8>(0), nullptr); _ret(*_spu, _spu->_ptr<u8>(0), nullptr);
@ -2771,7 +2771,7 @@ void spu_recompiler::WRCH(spu_opcode_t op)
Label ret = c->newLabel(); Label ret = c->newLabel();
c->lea(*ls, x86::qword_ptr(ret)); c->lea(*ls, x86::qword_ptr(ret));
c->jmp(imm_ptr<void(*)(SPUThread*, spu_function_t)>(sub)); c->jmp(imm_ptr<void(*)(spu_thread*, spu_function_t)>(sub));
c->align(kAlignCode, 16); c->align(kAlignCode, 16);
c->bind(ret); c->bind(ret);
c->mov(qw0->r32(), SPU_OFF_32(gpr, op.rt, &v128::_u32, 3)); c->mov(qw0->r32(), SPU_OFF_32(gpr, op.rt, &v128::_u32, 3));
@ -3113,7 +3113,7 @@ void spu_recompiler::CBX(spu_opcode_t op)
const XmmLink& vr = XmmAlloc(); const XmmLink& vr = XmmAlloc();
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(asmjit::x86::byte_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), 0x03); c->mov(asmjit::x86::byte_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), 0x03);
} }
void spu_recompiler::CHX(spu_opcode_t op) void spu_recompiler::CHX(spu_opcode_t op)
@ -3126,7 +3126,7 @@ void spu_recompiler::CHX(spu_opcode_t op)
const XmmLink& vr = XmmAlloc(); const XmmLink& vr = XmmAlloc();
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(asmjit::x86::word_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), 0x0203); c->mov(asmjit::x86::word_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), 0x0203);
} }
void spu_recompiler::CWX(spu_opcode_t op) void spu_recompiler::CWX(spu_opcode_t op)
@ -3139,7 +3139,7 @@ void spu_recompiler::CWX(spu_opcode_t op)
const XmmLink& vr = XmmAlloc(); const XmmLink& vr = XmmAlloc();
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(asmjit::x86::dword_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), 0x00010203); c->mov(asmjit::x86::dword_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), 0x00010203);
} }
void spu_recompiler::CDX(spu_opcode_t op) void spu_recompiler::CDX(spu_opcode_t op)
@ -3153,7 +3153,7 @@ void spu_recompiler::CDX(spu_opcode_t op)
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(*qw0, asmjit::imm_u(0x0001020304050607)); c->mov(*qw0, asmjit::imm_u(0x0001020304050607));
c->mov(asmjit::x86::qword_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), *qw0); c->mov(asmjit::x86::qword_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), *qw0);
} }
void spu_recompiler::ROTQBI(spu_opcode_t op) void spu_recompiler::ROTQBI(spu_opcode_t op)
@ -3292,7 +3292,7 @@ void spu_recompiler::CBD(spu_opcode_t op)
const XmmLink& vr = XmmAlloc(); const XmmLink& vr = XmmAlloc();
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(asmjit::x86::byte_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), 0x03); c->mov(asmjit::x86::byte_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), 0x03);
} }
void spu_recompiler::CHD(spu_opcode_t op) void spu_recompiler::CHD(spu_opcode_t op)
@ -3316,7 +3316,7 @@ void spu_recompiler::CHD(spu_opcode_t op)
const XmmLink& vr = XmmAlloc(); const XmmLink& vr = XmmAlloc();
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(asmjit::x86::word_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), 0x0203); c->mov(asmjit::x86::word_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), 0x0203);
} }
void spu_recompiler::CWD(spu_opcode_t op) void spu_recompiler::CWD(spu_opcode_t op)
@ -3340,7 +3340,7 @@ void spu_recompiler::CWD(spu_opcode_t op)
const XmmLink& vr = XmmAlloc(); const XmmLink& vr = XmmAlloc();
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(asmjit::x86::dword_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), 0x00010203); c->mov(asmjit::x86::dword_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), 0x00010203);
} }
void spu_recompiler::CDD(spu_opcode_t op) void spu_recompiler::CDD(spu_opcode_t op)
@ -3365,7 +3365,7 @@ void spu_recompiler::CDD(spu_opcode_t op)
c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f))); c->movdqa(vr, XmmConst(_mm_set_epi32(0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f)));
c->movdqa(SPU_OFF_128(gpr, op.rt), vr); c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
c->mov(*qw0, asmjit::imm_u(0x0001020304050607)); c->mov(*qw0, asmjit::imm_u(0x0001020304050607));
c->mov(asmjit::x86::qword_ptr(*cpu, addr->r64(), 0, offset32(&SPUThread::gpr, op.rt)), *qw0); c->mov(asmjit::x86::qword_ptr(*cpu, addr->r64(), 0, offset32(&spu_thread::gpr, op.rt)), *qw0);
} }
void spu_recompiler::ROTQBII(spu_opcode_t op) void spu_recompiler::ROTQBII(spu_opcode_t op)

File diff suppressed because it is too large Load Diff

View File

@ -2,248 +2,248 @@
#include "SPUOpcodes.h" #include "SPUOpcodes.h"
class SPUThread; class spu_thread;
using spu_inter_func_t = bool(*)(SPUThread& spu, spu_opcode_t op); using spu_inter_func_t = bool(*)(spu_thread& spu, spu_opcode_t op);
struct spu_interpreter struct spu_interpreter
{ {
static bool UNK(SPUThread&, spu_opcode_t); static bool UNK(spu_thread&, spu_opcode_t);
static void set_interrupt_status(SPUThread&, spu_opcode_t); static void set_interrupt_status(spu_thread&, spu_opcode_t);
static bool STOP(SPUThread&, spu_opcode_t); static bool STOP(spu_thread&, spu_opcode_t);
static bool LNOP(SPUThread&, spu_opcode_t); static bool LNOP(spu_thread&, spu_opcode_t);
static bool SYNC(SPUThread&, spu_opcode_t); static bool SYNC(spu_thread&, spu_opcode_t);
static bool DSYNC(SPUThread&, spu_opcode_t); static bool DSYNC(spu_thread&, spu_opcode_t);
static bool MFSPR(SPUThread&, spu_opcode_t); static bool MFSPR(spu_thread&, spu_opcode_t);
static bool RDCH(SPUThread&, spu_opcode_t); static bool RDCH(spu_thread&, spu_opcode_t);
static bool RCHCNT(SPUThread&, spu_opcode_t); static bool RCHCNT(spu_thread&, spu_opcode_t);
static bool SF(SPUThread&, spu_opcode_t); static bool SF(spu_thread&, spu_opcode_t);
static bool OR(SPUThread&, spu_opcode_t); static bool OR(spu_thread&, spu_opcode_t);
static bool BG(SPUThread&, spu_opcode_t); static bool BG(spu_thread&, spu_opcode_t);
static bool SFH(SPUThread&, spu_opcode_t); static bool SFH(spu_thread&, spu_opcode_t);
static bool NOR(SPUThread&, spu_opcode_t); static bool NOR(spu_thread&, spu_opcode_t);
static bool ABSDB(SPUThread&, spu_opcode_t); static bool ABSDB(spu_thread&, spu_opcode_t);
static bool ROT(SPUThread&, spu_opcode_t); static bool ROT(spu_thread&, spu_opcode_t);
static bool ROTM(SPUThread&, spu_opcode_t); static bool ROTM(spu_thread&, spu_opcode_t);
static bool ROTMA(SPUThread&, spu_opcode_t); static bool ROTMA(spu_thread&, spu_opcode_t);
static bool SHL(SPUThread&, spu_opcode_t); static bool SHL(spu_thread&, spu_opcode_t);
static bool ROTH(SPUThread&, spu_opcode_t); static bool ROTH(spu_thread&, spu_opcode_t);
static bool ROTHM(SPUThread&, spu_opcode_t); static bool ROTHM(spu_thread&, spu_opcode_t);
static bool ROTMAH(SPUThread&, spu_opcode_t); static bool ROTMAH(spu_thread&, spu_opcode_t);
static bool SHLH(SPUThread&, spu_opcode_t); static bool SHLH(spu_thread&, spu_opcode_t);
static bool ROTI(SPUThread&, spu_opcode_t); static bool ROTI(spu_thread&, spu_opcode_t);
static bool ROTMI(SPUThread&, spu_opcode_t); static bool ROTMI(spu_thread&, spu_opcode_t);
static bool ROTMAI(SPUThread&, spu_opcode_t); static bool ROTMAI(spu_thread&, spu_opcode_t);
static bool SHLI(SPUThread&, spu_opcode_t); static bool SHLI(spu_thread&, spu_opcode_t);
static bool ROTHI(SPUThread&, spu_opcode_t); static bool ROTHI(spu_thread&, spu_opcode_t);
static bool ROTHMI(SPUThread&, spu_opcode_t); static bool ROTHMI(spu_thread&, spu_opcode_t);
static bool ROTMAHI(SPUThread&, spu_opcode_t); static bool ROTMAHI(spu_thread&, spu_opcode_t);
static bool SHLHI(SPUThread&, spu_opcode_t); static bool SHLHI(spu_thread&, spu_opcode_t);
static bool A(SPUThread&, spu_opcode_t); static bool A(spu_thread&, spu_opcode_t);
static bool AND(SPUThread&, spu_opcode_t); static bool AND(spu_thread&, spu_opcode_t);
static bool CG(SPUThread&, spu_opcode_t); static bool CG(spu_thread&, spu_opcode_t);
static bool AH(SPUThread&, spu_opcode_t); static bool AH(spu_thread&, spu_opcode_t);
static bool NAND(SPUThread&, spu_opcode_t); static bool NAND(spu_thread&, spu_opcode_t);
static bool AVGB(SPUThread&, spu_opcode_t); static bool AVGB(spu_thread&, spu_opcode_t);
static bool MTSPR(SPUThread&, spu_opcode_t); static bool MTSPR(spu_thread&, spu_opcode_t);
static bool WRCH(SPUThread&, spu_opcode_t); static bool WRCH(spu_thread&, spu_opcode_t);
static bool BIZ(SPUThread&, spu_opcode_t); static bool BIZ(spu_thread&, spu_opcode_t);
static bool BINZ(SPUThread&, spu_opcode_t); static bool BINZ(spu_thread&, spu_opcode_t);
static bool BIHZ(SPUThread&, spu_opcode_t); static bool BIHZ(spu_thread&, spu_opcode_t);
static bool BIHNZ(SPUThread&, spu_opcode_t); static bool BIHNZ(spu_thread&, spu_opcode_t);
static bool STOPD(SPUThread&, spu_opcode_t); static bool STOPD(spu_thread&, spu_opcode_t);
static bool STQX(SPUThread&, spu_opcode_t); static bool STQX(spu_thread&, spu_opcode_t);
static bool BI(SPUThread&, spu_opcode_t); static bool BI(spu_thread&, spu_opcode_t);
static bool BISL(SPUThread&, spu_opcode_t); static bool BISL(spu_thread&, spu_opcode_t);
static bool IRET(SPUThread&, spu_opcode_t); static bool IRET(spu_thread&, spu_opcode_t);
static bool BISLED(SPUThread&, spu_opcode_t); static bool BISLED(spu_thread&, spu_opcode_t);
static bool HBR(SPUThread&, spu_opcode_t); static bool HBR(spu_thread&, spu_opcode_t);
static bool GB(SPUThread&, spu_opcode_t); static bool GB(spu_thread&, spu_opcode_t);
static bool GBH(SPUThread&, spu_opcode_t); static bool GBH(spu_thread&, spu_opcode_t);
static bool GBB(SPUThread&, spu_opcode_t); static bool GBB(spu_thread&, spu_opcode_t);
static bool FSM(SPUThread&, spu_opcode_t); static bool FSM(spu_thread&, spu_opcode_t);
static bool FSMH(SPUThread&, spu_opcode_t); static bool FSMH(spu_thread&, spu_opcode_t);
static bool FSMB(SPUThread&, spu_opcode_t); static bool FSMB(spu_thread&, spu_opcode_t);
static bool LQX(SPUThread&, spu_opcode_t); static bool LQX(spu_thread&, spu_opcode_t);
static bool ROTQBYBI(SPUThread&, spu_opcode_t); static bool ROTQBYBI(spu_thread&, spu_opcode_t);
static bool ROTQMBYBI(SPUThread&, spu_opcode_t); static bool ROTQMBYBI(spu_thread&, spu_opcode_t);
static bool SHLQBYBI(SPUThread&, spu_opcode_t); static bool SHLQBYBI(spu_thread&, spu_opcode_t);
static bool CBX(SPUThread&, spu_opcode_t); static bool CBX(spu_thread&, spu_opcode_t);
static bool CHX(SPUThread&, spu_opcode_t); static bool CHX(spu_thread&, spu_opcode_t);
static bool CWX(SPUThread&, spu_opcode_t); static bool CWX(spu_thread&, spu_opcode_t);
static bool CDX(SPUThread&, spu_opcode_t); static bool CDX(spu_thread&, spu_opcode_t);
static bool ROTQBI(SPUThread&, spu_opcode_t); static bool ROTQBI(spu_thread&, spu_opcode_t);
static bool ROTQMBI(SPUThread&, spu_opcode_t); static bool ROTQMBI(spu_thread&, spu_opcode_t);
static bool SHLQBI(SPUThread&, spu_opcode_t); static bool SHLQBI(spu_thread&, spu_opcode_t);
static bool ROTQBY(SPUThread&, spu_opcode_t); static bool ROTQBY(spu_thread&, spu_opcode_t);
static bool ROTQMBY(SPUThread&, spu_opcode_t); static bool ROTQMBY(spu_thread&, spu_opcode_t);
static bool SHLQBY(SPUThread&, spu_opcode_t); static bool SHLQBY(spu_thread&, spu_opcode_t);
static bool ORX(SPUThread&, spu_opcode_t); static bool ORX(spu_thread&, spu_opcode_t);
static bool CBD(SPUThread&, spu_opcode_t); static bool CBD(spu_thread&, spu_opcode_t);
static bool CHD(SPUThread&, spu_opcode_t); static bool CHD(spu_thread&, spu_opcode_t);
static bool CWD(SPUThread&, spu_opcode_t); static bool CWD(spu_thread&, spu_opcode_t);
static bool CDD(SPUThread&, spu_opcode_t); static bool CDD(spu_thread&, spu_opcode_t);
static bool ROTQBII(SPUThread&, spu_opcode_t); static bool ROTQBII(spu_thread&, spu_opcode_t);
static bool ROTQMBII(SPUThread&, spu_opcode_t); static bool ROTQMBII(spu_thread&, spu_opcode_t);
static bool SHLQBII(SPUThread&, spu_opcode_t); static bool SHLQBII(spu_thread&, spu_opcode_t);
static bool ROTQBYI(SPUThread&, spu_opcode_t); static bool ROTQBYI(spu_thread&, spu_opcode_t);
static bool ROTQMBYI(SPUThread&, spu_opcode_t); static bool ROTQMBYI(spu_thread&, spu_opcode_t);
static bool SHLQBYI(SPUThread&, spu_opcode_t); static bool SHLQBYI(spu_thread&, spu_opcode_t);
static bool NOP(SPUThread&, spu_opcode_t); static bool NOP(spu_thread&, spu_opcode_t);
static bool CGT(SPUThread&, spu_opcode_t); static bool CGT(spu_thread&, spu_opcode_t);
static bool XOR(SPUThread&, spu_opcode_t); static bool XOR(spu_thread&, spu_opcode_t);
static bool CGTH(SPUThread&, spu_opcode_t); static bool CGTH(spu_thread&, spu_opcode_t);
static bool EQV(SPUThread&, spu_opcode_t); static bool EQV(spu_thread&, spu_opcode_t);
static bool CGTB(SPUThread&, spu_opcode_t); static bool CGTB(spu_thread&, spu_opcode_t);
static bool SUMB(SPUThread&, spu_opcode_t); static bool SUMB(spu_thread&, spu_opcode_t);
static bool HGT(SPUThread&, spu_opcode_t); static bool HGT(spu_thread&, spu_opcode_t);
static bool CLZ(SPUThread&, spu_opcode_t); static bool CLZ(spu_thread&, spu_opcode_t);
static bool XSWD(SPUThread&, spu_opcode_t); static bool XSWD(spu_thread&, spu_opcode_t);
static bool XSHW(SPUThread&, spu_opcode_t); static bool XSHW(spu_thread&, spu_opcode_t);
static bool CNTB(SPUThread&, spu_opcode_t); static bool CNTB(spu_thread&, spu_opcode_t);
static bool XSBH(SPUThread&, spu_opcode_t); static bool XSBH(spu_thread&, spu_opcode_t);
static bool CLGT(SPUThread&, spu_opcode_t); static bool CLGT(spu_thread&, spu_opcode_t);
static bool ANDC(SPUThread&, spu_opcode_t); static bool ANDC(spu_thread&, spu_opcode_t);
static bool CLGTH(SPUThread&, spu_opcode_t); static bool CLGTH(spu_thread&, spu_opcode_t);
static bool ORC(SPUThread&, spu_opcode_t); static bool ORC(spu_thread&, spu_opcode_t);
static bool CLGTB(SPUThread&, spu_opcode_t); static bool CLGTB(spu_thread&, spu_opcode_t);
static bool HLGT(SPUThread&, spu_opcode_t); static bool HLGT(spu_thread&, spu_opcode_t);
static bool CEQ(SPUThread&, spu_opcode_t); static bool CEQ(spu_thread&, spu_opcode_t);
static bool MPYHHU(SPUThread&, spu_opcode_t); static bool MPYHHU(spu_thread&, spu_opcode_t);
static bool ADDX(SPUThread&, spu_opcode_t); static bool ADDX(spu_thread&, spu_opcode_t);
static bool SFX(SPUThread&, spu_opcode_t); static bool SFX(spu_thread&, spu_opcode_t);
static bool CGX(SPUThread&, spu_opcode_t); static bool CGX(spu_thread&, spu_opcode_t);
static bool BGX(SPUThread&, spu_opcode_t); static bool BGX(spu_thread&, spu_opcode_t);
static bool MPYHHA(SPUThread&, spu_opcode_t); static bool MPYHHA(spu_thread&, spu_opcode_t);
static bool MPYHHAU(SPUThread&, spu_opcode_t); static bool MPYHHAU(spu_thread&, spu_opcode_t);
static bool MPY(SPUThread&, spu_opcode_t); static bool MPY(spu_thread&, spu_opcode_t);
static bool MPYH(SPUThread&, spu_opcode_t); static bool MPYH(spu_thread&, spu_opcode_t);
static bool MPYHH(SPUThread&, spu_opcode_t); static bool MPYHH(spu_thread&, spu_opcode_t);
static bool MPYS(SPUThread&, spu_opcode_t); static bool MPYS(spu_thread&, spu_opcode_t);
static bool CEQH(SPUThread&, spu_opcode_t); static bool CEQH(spu_thread&, spu_opcode_t);
static bool MPYU(SPUThread&, spu_opcode_t); static bool MPYU(spu_thread&, spu_opcode_t);
static bool CEQB(SPUThread&, spu_opcode_t); static bool CEQB(spu_thread&, spu_opcode_t);
static bool HEQ(SPUThread&, spu_opcode_t); static bool HEQ(spu_thread&, spu_opcode_t);
static bool BRZ(SPUThread&, spu_opcode_t); static bool BRZ(spu_thread&, spu_opcode_t);
static bool STQA(SPUThread&, spu_opcode_t); static bool STQA(spu_thread&, spu_opcode_t);
static bool BRNZ(SPUThread&, spu_opcode_t); static bool BRNZ(spu_thread&, spu_opcode_t);
static bool BRHZ(SPUThread&, spu_opcode_t); static bool BRHZ(spu_thread&, spu_opcode_t);
static bool BRHNZ(SPUThread&, spu_opcode_t); static bool BRHNZ(spu_thread&, spu_opcode_t);
static bool STQR(SPUThread&, spu_opcode_t); static bool STQR(spu_thread&, spu_opcode_t);
static bool BRA(SPUThread&, spu_opcode_t); static bool BRA(spu_thread&, spu_opcode_t);
static bool LQA(SPUThread&, spu_opcode_t); static bool LQA(spu_thread&, spu_opcode_t);
static bool BRASL(SPUThread&, spu_opcode_t); static bool BRASL(spu_thread&, spu_opcode_t);
static bool BR(SPUThread&, spu_opcode_t); static bool BR(spu_thread&, spu_opcode_t);
static bool FSMBI(SPUThread&, spu_opcode_t); static bool FSMBI(spu_thread&, spu_opcode_t);
static bool BRSL(SPUThread&, spu_opcode_t); static bool BRSL(spu_thread&, spu_opcode_t);
static bool LQR(SPUThread&, spu_opcode_t); static bool LQR(spu_thread&, spu_opcode_t);
static bool IL(SPUThread&, spu_opcode_t); static bool IL(spu_thread&, spu_opcode_t);
static bool ILHU(SPUThread&, spu_opcode_t); static bool ILHU(spu_thread&, spu_opcode_t);
static bool ILH(SPUThread&, spu_opcode_t); static bool ILH(spu_thread&, spu_opcode_t);
static bool IOHL(SPUThread&, spu_opcode_t); static bool IOHL(spu_thread&, spu_opcode_t);
static bool ORI(SPUThread&, spu_opcode_t); static bool ORI(spu_thread&, spu_opcode_t);
static bool ORHI(SPUThread&, spu_opcode_t); static bool ORHI(spu_thread&, spu_opcode_t);
static bool ORBI(SPUThread&, spu_opcode_t); static bool ORBI(spu_thread&, spu_opcode_t);
static bool SFI(SPUThread&, spu_opcode_t); static bool SFI(spu_thread&, spu_opcode_t);
static bool SFHI(SPUThread&, spu_opcode_t); static bool SFHI(spu_thread&, spu_opcode_t);
static bool ANDI(SPUThread&, spu_opcode_t); static bool ANDI(spu_thread&, spu_opcode_t);
static bool ANDHI(SPUThread&, spu_opcode_t); static bool ANDHI(spu_thread&, spu_opcode_t);
static bool ANDBI(SPUThread&, spu_opcode_t); static bool ANDBI(spu_thread&, spu_opcode_t);
static bool AI(SPUThread&, spu_opcode_t); static bool AI(spu_thread&, spu_opcode_t);
static bool AHI(SPUThread&, spu_opcode_t); static bool AHI(spu_thread&, spu_opcode_t);
static bool STQD(SPUThread&, spu_opcode_t); static bool STQD(spu_thread&, spu_opcode_t);
static bool LQD(SPUThread&, spu_opcode_t); static bool LQD(spu_thread&, spu_opcode_t);
static bool XORI(SPUThread&, spu_opcode_t); static bool XORI(spu_thread&, spu_opcode_t);
static bool XORHI(SPUThread&, spu_opcode_t); static bool XORHI(spu_thread&, spu_opcode_t);
static bool XORBI(SPUThread&, spu_opcode_t); static bool XORBI(spu_thread&, spu_opcode_t);
static bool CGTI(SPUThread&, spu_opcode_t); static bool CGTI(spu_thread&, spu_opcode_t);
static bool CGTHI(SPUThread&, spu_opcode_t); static bool CGTHI(spu_thread&, spu_opcode_t);
static bool CGTBI(SPUThread&, spu_opcode_t); static bool CGTBI(spu_thread&, spu_opcode_t);
static bool HGTI(SPUThread&, spu_opcode_t); static bool HGTI(spu_thread&, spu_opcode_t);
static bool CLGTI(SPUThread&, spu_opcode_t); static bool CLGTI(spu_thread&, spu_opcode_t);
static bool CLGTHI(SPUThread&, spu_opcode_t); static bool CLGTHI(spu_thread&, spu_opcode_t);
static bool CLGTBI(SPUThread&, spu_opcode_t); static bool CLGTBI(spu_thread&, spu_opcode_t);
static bool HLGTI(SPUThread&, spu_opcode_t); static bool HLGTI(spu_thread&, spu_opcode_t);
static bool MPYI(SPUThread&, spu_opcode_t); static bool MPYI(spu_thread&, spu_opcode_t);
static bool MPYUI(SPUThread&, spu_opcode_t); static bool MPYUI(spu_thread&, spu_opcode_t);
static bool CEQI(SPUThread&, spu_opcode_t); static bool CEQI(spu_thread&, spu_opcode_t);
static bool CEQHI(SPUThread&, spu_opcode_t); static bool CEQHI(spu_thread&, spu_opcode_t);
static bool CEQBI(SPUThread&, spu_opcode_t); static bool CEQBI(spu_thread&, spu_opcode_t);
static bool HEQI(SPUThread&, spu_opcode_t); static bool HEQI(spu_thread&, spu_opcode_t);
static bool HBRA(SPUThread&, spu_opcode_t); static bool HBRA(spu_thread&, spu_opcode_t);
static bool HBRR(SPUThread&, spu_opcode_t); static bool HBRR(spu_thread&, spu_opcode_t);
static bool ILA(SPUThread&, spu_opcode_t); static bool ILA(spu_thread&, spu_opcode_t);
static bool SELB(SPUThread&, spu_opcode_t); static bool SELB(spu_thread&, spu_opcode_t);
static const spu_inter_func_t SHUFB; static const spu_inter_func_t SHUFB;
static bool MPYA(SPUThread&, spu_opcode_t); static bool MPYA(spu_thread&, spu_opcode_t);
static bool DFCGT(SPUThread&, spu_opcode_t); static bool DFCGT(spu_thread&, spu_opcode_t);
static bool DFCMGT(SPUThread&, spu_opcode_t); static bool DFCMGT(spu_thread&, spu_opcode_t);
static bool DFTSV(SPUThread&, spu_opcode_t); static bool DFTSV(spu_thread&, spu_opcode_t);
static bool DFCEQ(SPUThread&, spu_opcode_t); static bool DFCEQ(spu_thread&, spu_opcode_t);
static bool DFCMEQ(SPUThread&, spu_opcode_t); static bool DFCMEQ(spu_thread&, spu_opcode_t);
}; };
struct spu_interpreter_fast final : spu_interpreter struct spu_interpreter_fast final : spu_interpreter
{ {
static bool FREST(SPUThread&, spu_opcode_t); static bool FREST(spu_thread&, spu_opcode_t);
static bool FRSQEST(SPUThread&, spu_opcode_t); static bool FRSQEST(spu_thread&, spu_opcode_t);
static bool FCGT(SPUThread&, spu_opcode_t); static bool FCGT(spu_thread&, spu_opcode_t);
static bool FA(SPUThread&, spu_opcode_t); static bool FA(spu_thread&, spu_opcode_t);
static bool FS(SPUThread&, spu_opcode_t); static bool FS(spu_thread&, spu_opcode_t);
static bool FM(SPUThread&, spu_opcode_t); static bool FM(spu_thread&, spu_opcode_t);
static bool FCMGT(SPUThread&, spu_opcode_t); static bool FCMGT(spu_thread&, spu_opcode_t);
static bool DFA(SPUThread&, spu_opcode_t); static bool DFA(spu_thread&, spu_opcode_t);
static bool DFS(SPUThread&, spu_opcode_t); static bool DFS(spu_thread&, spu_opcode_t);
static bool DFM(SPUThread&, spu_opcode_t); static bool DFM(spu_thread&, spu_opcode_t);
static bool DFMA(SPUThread&, spu_opcode_t); static bool DFMA(spu_thread&, spu_opcode_t);
static bool DFMS(SPUThread&, spu_opcode_t); static bool DFMS(spu_thread&, spu_opcode_t);
static bool DFNMS(SPUThread&, spu_opcode_t); static bool DFNMS(spu_thread&, spu_opcode_t);
static bool DFNMA(SPUThread&, spu_opcode_t); static bool DFNMA(spu_thread&, spu_opcode_t);
static bool FSCRRD(SPUThread&, spu_opcode_t); static bool FSCRRD(spu_thread&, spu_opcode_t);
static bool FESD(SPUThread&, spu_opcode_t); static bool FESD(spu_thread&, spu_opcode_t);
static bool FRDS(SPUThread&, spu_opcode_t); static bool FRDS(spu_thread&, spu_opcode_t);
static bool FSCRWR(SPUThread&, spu_opcode_t); static bool FSCRWR(spu_thread&, spu_opcode_t);
static bool FCEQ(SPUThread&, spu_opcode_t); static bool FCEQ(spu_thread&, spu_opcode_t);
static bool FCMEQ(SPUThread&, spu_opcode_t); static bool FCMEQ(spu_thread&, spu_opcode_t);
static bool FI(SPUThread&, spu_opcode_t); static bool FI(spu_thread&, spu_opcode_t);
static bool CFLTS(SPUThread&, spu_opcode_t); static bool CFLTS(spu_thread&, spu_opcode_t);
static bool CFLTU(SPUThread&, spu_opcode_t); static bool CFLTU(spu_thread&, spu_opcode_t);
static bool CSFLT(SPUThread&, spu_opcode_t); static bool CSFLT(spu_thread&, spu_opcode_t);
static bool CUFLT(SPUThread&, spu_opcode_t); static bool CUFLT(spu_thread&, spu_opcode_t);
static bool FNMS(SPUThread&, spu_opcode_t); static bool FNMS(spu_thread&, spu_opcode_t);
static bool FMA(SPUThread&, spu_opcode_t); static bool FMA(spu_thread&, spu_opcode_t);
static bool FMS(SPUThread&, spu_opcode_t); static bool FMS(spu_thread&, spu_opcode_t);
}; };
struct spu_interpreter_precise final : spu_interpreter struct spu_interpreter_precise final : spu_interpreter
{ {
static bool FREST(SPUThread&, spu_opcode_t); static bool FREST(spu_thread&, spu_opcode_t);
static bool FRSQEST(SPUThread&, spu_opcode_t); static bool FRSQEST(spu_thread&, spu_opcode_t);
static bool FCGT(SPUThread&, spu_opcode_t); static bool FCGT(spu_thread&, spu_opcode_t);
static bool FA(SPUThread&, spu_opcode_t); static bool FA(spu_thread&, spu_opcode_t);
static bool FS(SPUThread&, spu_opcode_t); static bool FS(spu_thread&, spu_opcode_t);
static bool FM(SPUThread&, spu_opcode_t); static bool FM(spu_thread&, spu_opcode_t);
static bool FCMGT(SPUThread&, spu_opcode_t); static bool FCMGT(spu_thread&, spu_opcode_t);
static bool DFA(SPUThread&, spu_opcode_t); static bool DFA(spu_thread&, spu_opcode_t);
static bool DFS(SPUThread&, spu_opcode_t); static bool DFS(spu_thread&, spu_opcode_t);
static bool DFM(SPUThread&, spu_opcode_t); static bool DFM(spu_thread&, spu_opcode_t);
static bool DFMA(SPUThread&, spu_opcode_t); static bool DFMA(spu_thread&, spu_opcode_t);
static bool DFMS(SPUThread&, spu_opcode_t); static bool DFMS(spu_thread&, spu_opcode_t);
static bool DFNMS(SPUThread&, spu_opcode_t); static bool DFNMS(spu_thread&, spu_opcode_t);
static bool DFNMA(SPUThread&, spu_opcode_t); static bool DFNMA(spu_thread&, spu_opcode_t);
static bool FSCRRD(SPUThread&, spu_opcode_t); static bool FSCRRD(spu_thread&, spu_opcode_t);
static bool FESD(SPUThread&, spu_opcode_t); static bool FESD(spu_thread&, spu_opcode_t);
static bool FRDS(SPUThread&, spu_opcode_t); static bool FRDS(spu_thread&, spu_opcode_t);
static bool FSCRWR(SPUThread&, spu_opcode_t); static bool FSCRWR(spu_thread&, spu_opcode_t);
static bool FCEQ(SPUThread&, spu_opcode_t); static bool FCEQ(spu_thread&, spu_opcode_t);
static bool FCMEQ(SPUThread&, spu_opcode_t); static bool FCMEQ(spu_thread&, spu_opcode_t);
static bool FI(SPUThread&, spu_opcode_t); static bool FI(spu_thread&, spu_opcode_t);
static bool CFLTS(SPUThread&, spu_opcode_t); static bool CFLTS(spu_thread&, spu_opcode_t);
static bool CFLTU(SPUThread&, spu_opcode_t); static bool CFLTU(spu_thread&, spu_opcode_t);
static bool CSFLT(SPUThread&, spu_opcode_t); static bool CSFLT(spu_thread&, spu_opcode_t);
static bool CUFLT(SPUThread&, spu_opcode_t); static bool CUFLT(spu_thread&, spu_opcode_t);
static bool FNMS(SPUThread&, spu_opcode_t); static bool FNMS(spu_thread&, spu_opcode_t);
static bool FMA(SPUThread&, spu_opcode_t); static bool FMA(spu_thread&, spu_opcode_t);
static bool FMS(SPUThread&, spu_opcode_t); static bool FMS(spu_thread&, spu_opcode_t);
}; };

View File

@ -211,7 +211,7 @@ spu_recompiler_base::~spu_recompiler_base()
{ {
} }
void spu_recompiler_base::dispatch(SPUThread& spu, void*, u8* rip) void spu_recompiler_base::dispatch(spu_thread& spu, void*, u8* rip)
{ {
// If code verification failed from a patched patchpoint, clear it with a single NOP // If code verification failed from a patched patchpoint, clear it with a single NOP
if (rip) if (rip)
@ -255,7 +255,7 @@ void spu_recompiler_base::dispatch(SPUThread& spu, void*, u8* rip)
} }
} }
void spu_recompiler_base::branch(SPUThread& spu, void*, u8* rip) void spu_recompiler_base::branch(spu_thread& spu, void*, u8* rip)
{ {
// Compile (TODO: optimize search of the existing functions) // Compile (TODO: optimize search of the existing functions)
const auto func = verify(HERE, spu.jit->compile(spu.jit->block(spu._ptr<u32>(0), spu.pc))); const auto func = verify(HERE, spu.jit->compile(spu.jit->block(spu._ptr<u32>(0), spu.pc)));
@ -1692,7 +1692,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
const auto cblock = m_ir->GetInsertBlock(); const auto cblock = m_ir->GetInsertBlock();
const auto result = llvm::BasicBlock::Create(m_context, "", m_function); const auto result = llvm::BasicBlock::Create(m_context, "", m_function);
m_ir->SetInsertPoint(result); m_ir->SetInsertPoint(result);
m_ir->CreateStore(m_ir->getInt32(target), spu_ptr<u32>(&SPUThread::pc)); m_ir->CreateStore(m_ir->getInt32(target), spu_ptr<u32>(&spu_thread::pc));
tail(add_function(target)); tail(add_function(target));
m_ir->SetInsertPoint(cblock); m_ir->SetInsertPoint(cblock);
return result; return result;
@ -1708,8 +1708,8 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
const auto cblock = m_ir->GetInsertBlock(); const auto cblock = m_ir->GetInsertBlock();
const auto result = llvm::BasicBlock::Create(m_context, "", m_function); const auto result = llvm::BasicBlock::Create(m_context, "", m_function);
m_ir->SetInsertPoint(result); m_ir->SetInsertPoint(result);
m_ir->CreateStore(m_ir->getInt32(target), spu_ptr<u32>(&SPUThread::pc)); m_ir->CreateStore(m_ir->getInt32(target), spu_ptr<u32>(&spu_thread::pc));
const auto addr = m_ir->CreateGEP(m_thread, m_ir->getInt64(::offset32(&SPUThread::jit_dispatcher) + target * 2)); const auto addr = m_ir->CreateGEP(m_thread, m_ir->getInt64(::offset32(&spu_thread::jit_dispatcher) + target * 2));
const auto type = llvm::FunctionType::get(get_type<void>(), {get_type<u8*>(), get_type<u8*>(), get_type<u32>()}, false)->getPointerTo()->getPointerTo(); const auto type = llvm::FunctionType::get(get_type<void>(), {get_type<u8*>(), get_type<u8*>(), get_type<u32>()}, false)->getPointerTo()->getPointerTo();
tail(m_ir->CreateLoad(m_ir->CreateBitCast(addr, type))); tail(m_ir->CreateLoad(m_ir->CreateBitCast(addr, type)));
m_ir->SetInsertPoint(cblock); m_ir->SetInsertPoint(cblock);
@ -1789,15 +1789,15 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
{ {
if (index < 128) if (index < 128)
{ {
return ::offset32(&SPUThread::gpr, index); return ::offset32(&spu_thread::gpr, index);
} }
switch (index) switch (index)
{ {
case s_reg_mfc_eal: return ::offset32(&SPUThread::ch_mfc_cmd, &spu_mfc_cmd::eal); case s_reg_mfc_eal: return ::offset32(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::eal);
case s_reg_mfc_lsa: return ::offset32(&SPUThread::ch_mfc_cmd, &spu_mfc_cmd::lsa); case s_reg_mfc_lsa: return ::offset32(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::lsa);
case s_reg_mfc_tag: return ::offset32(&SPUThread::ch_mfc_cmd, &spu_mfc_cmd::tag); case s_reg_mfc_tag: return ::offset32(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::tag);
case s_reg_mfc_size: return ::offset32(&SPUThread::ch_mfc_cmd, &spu_mfc_cmd::size); case s_reg_mfc_size: return ::offset32(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::size);
default: default:
fmt::throw_exception("get_reg_offset(%u): invalid register index" HERE, index); fmt::throw_exception("get_reg_offset(%u): invalid register index" HERE, index);
} }
@ -2183,19 +2183,19 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
void update_pc() void update_pc()
{ {
m_ir->CreateStore(m_ir->getInt32(m_pos), spu_ptr<u32>(&SPUThread::pc))->setVolatile(true); m_ir->CreateStore(m_ir->getInt32(m_pos), spu_ptr<u32>(&spu_thread::pc))->setVolatile(true);
} }
// Call cpu_thread::check_state if necessary and return or continue (full check) // Call cpu_thread::check_state if necessary and return or continue (full check)
void check_state(u32 addr) void check_state(u32 addr)
{ {
const auto pstate = spu_ptr<u32>(&SPUThread::state); const auto pstate = spu_ptr<u32>(&spu_thread::state);
const auto _body = llvm::BasicBlock::Create(m_context, "", m_function); const auto _body = llvm::BasicBlock::Create(m_context, "", m_function);
const auto check = llvm::BasicBlock::Create(m_context, "", m_function); const auto check = llvm::BasicBlock::Create(m_context, "", m_function);
const auto stop = llvm::BasicBlock::Create(m_context, "", m_function); const auto stop = llvm::BasicBlock::Create(m_context, "", m_function);
m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(pstate), m_ir->getInt32(0)), _body, check); m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(pstate), m_ir->getInt32(0)), _body, check);
m_ir->SetInsertPoint(check); m_ir->SetInsertPoint(check);
m_ir->CreateStore(m_ir->getInt32(addr), spu_ptr<u32>(&SPUThread::pc)); m_ir->CreateStore(m_ir->getInt32(addr), spu_ptr<u32>(&spu_thread::pc));
m_ir->CreateCondBr(call(&exec_check_state, m_thread), stop, _body); m_ir->CreateCondBr(call(&exec_check_state, m_thread), stop, _body);
m_ir->SetInsertPoint(stop); m_ir->SetInsertPoint(stop);
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
@ -2386,7 +2386,7 @@ public:
const auto label_stop = BasicBlock::Create(m_context, "", m_function); const auto label_stop = BasicBlock::Create(m_context, "", m_function);
// Emit state check // Emit state check
const auto pstate = spu_ptr<u32>(&SPUThread::state); const auto pstate = spu_ptr<u32>(&spu_thread::state);
m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(pstate, true), m_ir->getInt32(0)), label_stop, label_test); m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(pstate, true), m_ir->getInt32(0)), label_stop, label_test);
// Emit code check // Emit code check
@ -2482,7 +2482,7 @@ public:
// Increase block counter with statistics // Increase block counter with statistics
m_ir->SetInsertPoint(label_body); m_ir->SetInsertPoint(label_body);
const auto pbcount = spu_ptr<u64>(&SPUThread::block_counter); const auto pbcount = spu_ptr<u64>(&spu_thread::block_counter);
m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(pbcount), m_ir->getInt64(check_iterations)), pbcount); m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(pbcount), m_ir->getInt64(check_iterations)), pbcount);
// Call the entry function chunk // Call the entry function chunk
@ -2497,7 +2497,7 @@ public:
if (g_cfg.core.spu_verification) if (g_cfg.core.spu_verification)
{ {
const auto pbfail = spu_ptr<u64>(&SPUThread::block_failure); const auto pbfail = spu_ptr<u64>(&spu_thread::block_failure);
m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(pbfail), m_ir->getInt64(1)), pbfail); m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(pbfail), m_ir->getInt64(1)), pbfail);
tail(&spu_recompiler_base::dispatch, m_thread, m_ir->getInt32(0), m_ir->getInt32(0)); tail(&spu_recompiler_base::dispatch, m_thread, m_ir->getInt32(0), m_ir->getInt32(0));
} }
@ -3001,13 +3001,13 @@ public:
return fn; return fn;
} }
static bool exec_check_state(SPUThread* _spu) static bool exec_check_state(spu_thread* _spu)
{ {
return _spu->check_state(); return _spu->check_state();
} }
template <spu_inter_func_t F> template <spu_inter_func_t F>
static void exec_fall(SPUThread* _spu, spu_opcode_t op) static void exec_fall(spu_thread* _spu, spu_opcode_t op)
{ {
if (F(*_spu, op)) if (F(*_spu, op))
{ {
@ -3022,7 +3022,7 @@ public:
call(&exec_fall<F>, m_thread, m_ir->getInt32(op.opcode)); call(&exec_fall<F>, m_thread, m_ir->getInt32(op.opcode));
} }
static void exec_unk(SPUThread* _spu, u32 op) static void exec_unk(spu_thread* _spu, u32 op)
{ {
fmt::throw_exception("Unknown/Illegal instruction (0x%08x)" HERE, op); fmt::throw_exception("Unknown/Illegal instruction (0x%08x)" HERE, op);
} }
@ -3034,7 +3034,7 @@ public:
tail(&exec_unk, m_thread, m_ir->getInt32(op_unk.opcode)); tail(&exec_unk, m_thread, m_ir->getInt32(op_unk.opcode));
} }
static bool exec_stop(SPUThread* _spu, u32 code) static bool exec_stop(spu_thread* _spu, u32 code)
{ {
return _spu->stop_and_signal(code); return _spu->stop_and_signal(code);
} }
@ -3053,7 +3053,7 @@ public:
if (g_cfg.core.spu_block_size == spu_block_size_type::safe) if (g_cfg.core.spu_block_size == spu_block_size_type::safe)
{ {
m_block->block_end = m_ir->GetInsertBlock(); m_block->block_end = m_ir->GetInsertBlock();
m_ir->CreateStore(m_ir->getInt32(m_pos + 4), spu_ptr<u32>(&SPUThread::pc)); m_ir->CreateStore(m_ir->getInt32(m_pos + 4), spu_ptr<u32>(&spu_thread::pc));
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
} }
} }
@ -3063,18 +3063,18 @@ public:
STOP(spu_opcode_t{0x3fff}); STOP(spu_opcode_t{0x3fff});
} }
static s64 exec_rdch(SPUThread* _spu, u32 ch) static s64 exec_rdch(spu_thread* _spu, u32 ch)
{ {
return _spu->get_ch_value(ch); return _spu->get_ch_value(ch);
} }
static s64 exec_read_in_mbox(SPUThread* _spu) static s64 exec_read_in_mbox(spu_thread* _spu)
{ {
// TODO // TODO
return _spu->get_ch_value(SPU_RdInMbox); return _spu->get_ch_value(SPU_RdInMbox);
} }
static u32 exec_read_dec(SPUThread* _spu) static u32 exec_read_dec(spu_thread* _spu)
{ {
const u32 res = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp); const u32 res = _spu->ch_dec_value - static_cast<u32>(get_timebased_time() - _spu->ch_dec_start_timestamp);
@ -3086,7 +3086,7 @@ public:
return res; return res;
} }
static s64 exec_read_events(SPUThread* _spu) static s64 exec_read_events(spu_thread* _spu)
{ {
if (const u32 events = _spu->get_events()) if (const u32 events = _spu->get_events())
{ {
@ -3139,7 +3139,7 @@ public:
{ {
case SPU_RdSRR0: case SPU_RdSRR0:
{ {
res.value = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::srr0)); res.value = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::srr0));
break; break;
} }
case SPU_RdInMbox: case SPU_RdInMbox:
@ -3157,32 +3157,32 @@ public:
} }
case MFC_RdTagStat: case MFC_RdTagStat:
{ {
res.value = get_rdch(op, ::offset32(&SPUThread::ch_tag_stat), false); res.value = get_rdch(op, ::offset32(&spu_thread::ch_tag_stat), false);
break; break;
} }
case MFC_RdTagMask: case MFC_RdTagMask:
{ {
res.value = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::ch_tag_mask)); res.value = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::ch_tag_mask));
break; break;
} }
case SPU_RdSigNotify1: case SPU_RdSigNotify1:
{ {
res.value = get_rdch(op, ::offset32(&SPUThread::ch_snr1), true); res.value = get_rdch(op, ::offset32(&spu_thread::ch_snr1), true);
break; break;
} }
case SPU_RdSigNotify2: case SPU_RdSigNotify2:
{ {
res.value = get_rdch(op, ::offset32(&SPUThread::ch_snr2), true); res.value = get_rdch(op, ::offset32(&spu_thread::ch_snr2), true);
break; break;
} }
case MFC_RdAtomicStat: case MFC_RdAtomicStat:
{ {
res.value = get_rdch(op, ::offset32(&SPUThread::ch_atomic_stat), false); res.value = get_rdch(op, ::offset32(&spu_thread::ch_atomic_stat), false);
break; break;
} }
case MFC_RdListStallStat: case MFC_RdListStallStat:
{ {
res.value = get_rdch(op, ::offset32(&SPUThread::ch_stall_stat), false); res.value = get_rdch(op, ::offset32(&spu_thread::ch_stall_stat), false);
break; break;
} }
case SPU_RdDec: case SPU_RdDec:
@ -3192,7 +3192,7 @@ public:
} }
case SPU_RdEventMask: case SPU_RdEventMask:
{ {
res.value = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::ch_event_mask)); res.value = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::ch_event_mask));
break; break;
} }
case SPU_RdEventStat: case SPU_RdEventStat:
@ -3210,7 +3210,7 @@ public:
} }
case SPU_RdMachStat: case SPU_RdMachStat:
{ {
res.value = m_ir->CreateZExt(m_ir->CreateLoad(spu_ptr<u8>(&SPUThread::interrupts_enabled)), get_type<u32>()); res.value = m_ir->CreateZExt(m_ir->CreateLoad(spu_ptr<u8>(&spu_thread::interrupts_enabled)), get_type<u32>());
break; break;
} }
@ -3232,12 +3232,12 @@ public:
set_vr(op.rt, insert(splat<u32[4]>(0), 3, res)); set_vr(op.rt, insert(splat<u32[4]>(0), 3, res));
} }
static u32 exec_rchcnt(SPUThread* _spu, u32 ch) static u32 exec_rchcnt(spu_thread* _spu, u32 ch)
{ {
return _spu->get_ch_count(ch); return _spu->get_ch_count(ch);
} }
static u32 exec_get_events(SPUThread* _spu) static u32 exec_get_events(spu_thread* _spu)
{ {
return _spu->get_events(); return _spu->get_events();
} }
@ -3257,55 +3257,55 @@ public:
{ {
case SPU_WrOutMbox: case SPU_WrOutMbox:
{ {
res.value = get_rchcnt(::offset32(&SPUThread::ch_out_mbox), true); res.value = get_rchcnt(::offset32(&spu_thread::ch_out_mbox), true);
break; break;
} }
case SPU_WrOutIntrMbox: case SPU_WrOutIntrMbox:
{ {
res.value = get_rchcnt(::offset32(&SPUThread::ch_out_intr_mbox), true); res.value = get_rchcnt(::offset32(&spu_thread::ch_out_intr_mbox), true);
break; break;
} }
case MFC_RdTagStat: case MFC_RdTagStat:
{ {
res.value = get_rchcnt(::offset32(&SPUThread::ch_tag_stat)); res.value = get_rchcnt(::offset32(&spu_thread::ch_tag_stat));
break; break;
} }
case MFC_RdListStallStat: case MFC_RdListStallStat:
{ {
res.value = get_rchcnt(::offset32(&SPUThread::ch_stall_stat)); res.value = get_rchcnt(::offset32(&spu_thread::ch_stall_stat));
break; break;
} }
case SPU_RdSigNotify1: case SPU_RdSigNotify1:
{ {
res.value = get_rchcnt(::offset32(&SPUThread::ch_snr1)); res.value = get_rchcnt(::offset32(&spu_thread::ch_snr1));
break; break;
} }
case SPU_RdSigNotify2: case SPU_RdSigNotify2:
{ {
res.value = get_rchcnt(::offset32(&SPUThread::ch_snr2)); res.value = get_rchcnt(::offset32(&spu_thread::ch_snr2));
break; break;
} }
case MFC_RdAtomicStat: case MFC_RdAtomicStat:
{ {
res.value = get_rchcnt(::offset32(&SPUThread::ch_atomic_stat)); res.value = get_rchcnt(::offset32(&spu_thread::ch_atomic_stat));
break; break;
} }
case MFC_WrTagUpdate: case MFC_WrTagUpdate:
{ {
res.value = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::ch_tag_upd), true); res.value = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::ch_tag_upd), true);
res.value = m_ir->CreateICmpEQ(res.value, m_ir->getInt32(0)); res.value = m_ir->CreateICmpEQ(res.value, m_ir->getInt32(0));
res.value = m_ir->CreateZExt(res.value, get_type<u32>()); res.value = m_ir->CreateZExt(res.value, get_type<u32>());
break; break;
} }
case MFC_Cmd: case MFC_Cmd:
{ {
res.value = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::mfc_size), true); res.value = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::mfc_size), true);
res.value = m_ir->CreateSub(m_ir->getInt32(16), res.value); res.value = m_ir->CreateSub(m_ir->getInt32(16), res.value);
break; break;
} }
case SPU_RdInMbox: case SPU_RdInMbox:
{ {
res.value = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::ch_in_mbox), true); res.value = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::ch_in_mbox), true);
res.value = m_ir->CreateLShr(res.value, 8); res.value = m_ir->CreateLShr(res.value, 8);
res.value = m_ir->CreateAnd(res.value, 7); res.value = m_ir->CreateAnd(res.value, 7);
break; break;
@ -3328,17 +3328,17 @@ public:
set_vr(op.rt, insert(splat<u32[4]>(0), 3, res)); set_vr(op.rt, insert(splat<u32[4]>(0), 3, res));
} }
static bool exec_wrch(SPUThread* _spu, u32 ch, u32 value) static bool exec_wrch(spu_thread* _spu, u32 ch, u32 value)
{ {
return _spu->set_ch_value(ch, value); return _spu->set_ch_value(ch, value);
} }
static void exec_mfc(SPUThread* _spu) static void exec_mfc(spu_thread* _spu)
{ {
return _spu->do_mfc(); return _spu->do_mfc();
} }
static bool exec_mfc_cmd(SPUThread* _spu) static bool exec_mfc_cmd(spu_thread* _spu)
{ {
return _spu->process_mfc_cmd(_spu->ch_mfc_cmd); return _spu->process_mfc_cmd(_spu->ch_mfc_cmd);
} }
@ -3351,7 +3351,7 @@ public:
{ {
case SPU_WrSRR0: case SPU_WrSRR0:
{ {
m_ir->CreateStore(val.value, spu_ptr<u32>(&SPUThread::srr0)); m_ir->CreateStore(val.value, spu_ptr<u32>(&spu_thread::srr0));
return; return;
} }
case SPU_WrOutIntrMbox: case SPU_WrOutIntrMbox:
@ -3367,7 +3367,7 @@ public:
case MFC_WrTagMask: case MFC_WrTagMask:
{ {
// TODO // TODO
m_ir->CreateStore(val.value, spu_ptr<u32>(&SPUThread::ch_tag_mask)); m_ir->CreateStore(val.value, spu_ptr<u32>(&spu_thread::ch_tag_mask));
return; return;
} }
case MFC_WrTagUpdate: case MFC_WrTagUpdate:
@ -3376,11 +3376,11 @@ public:
{ {
const u64 upd = ci->getZExtValue(); const u64 upd = ci->getZExtValue();
const auto tag_mask = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::ch_tag_mask)); const auto tag_mask = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::ch_tag_mask));
const auto mfc_fence = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::mfc_fence)); const auto mfc_fence = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::mfc_fence));
const auto completed = m_ir->CreateAnd(tag_mask, m_ir->CreateNot(mfc_fence)); const auto completed = m_ir->CreateAnd(tag_mask, m_ir->CreateNot(mfc_fence));
const auto upd_ptr = spu_ptr<u32>(&SPUThread::ch_tag_upd); const auto upd_ptr = spu_ptr<u32>(&spu_thread::ch_tag_upd);
const auto stat_ptr = spu_ptr<u64>(&SPUThread::ch_tag_stat); const auto stat_ptr = spu_ptr<u64>(&spu_thread::ch_tag_stat);
const auto stat_val = m_ir->CreateOr(m_ir->CreateZExt(completed, get_type<u64>()), INT64_MIN); const auto stat_val = m_ir->CreateOr(m_ir->CreateZExt(completed, get_type<u64>()), INT64_MIN);
if (upd == 0) if (upd == 0)
@ -3424,7 +3424,7 @@ public:
} }
LOG_WARNING(SPU, "[0x%x] MFC_EAH: $%u is not a zero constant", m_pos, +op.rt); LOG_WARNING(SPU, "[0x%x] MFC_EAH: $%u is not a zero constant", m_pos, +op.rt);
//m_ir->CreateStore(val.value, spu_ptr<u32>(&SPUThread::ch_mfc_cmd, &spu_mfc_cmd::eah)); //m_ir->CreateStore(val.value, spu_ptr<u32>(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::eah));
return; return;
} }
case MFC_EAL: case MFC_EAL:
@ -3468,8 +3468,8 @@ public:
const auto fail = llvm::BasicBlock::Create(m_context, "", m_function); const auto fail = llvm::BasicBlock::Create(m_context, "", m_function);
const auto next = llvm::BasicBlock::Create(m_context, "", m_function); const auto next = llvm::BasicBlock::Create(m_context, "", m_function);
const auto pf = spu_ptr<u32>(&SPUThread::mfc_fence); const auto pf = spu_ptr<u32>(&spu_thread::mfc_fence);
const auto pb = spu_ptr<u32>(&SPUThread::mfc_barrier); const auto pb = spu_ptr<u32>(&spu_thread::mfc_barrier);
switch (u64 cmd = ci->getZExtValue()) switch (u64 cmd = ci->getZExtValue())
{ {
@ -3494,7 +3494,7 @@ public:
m_ir->SetInsertPoint(fail); m_ir->SetInsertPoint(fail);
m_ir->CreateUnreachable(); m_ir->CreateUnreachable();
m_ir->SetInsertPoint(next); m_ir->SetInsertPoint(next);
m_ir->CreateStore(ci, spu_ptr<u8>(&SPUThread::ch_mfc_cmd, &spu_mfc_cmd::cmd)); m_ir->CreateStore(ci, spu_ptr<u8>(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd));
call(&exec_mfc_cmd, m_thread); call(&exec_mfc_cmd, m_thread);
return; return;
} }
@ -3609,7 +3609,7 @@ public:
case MFC_EIEIO_CMD: case MFC_EIEIO_CMD:
case MFC_SYNC_CMD: case MFC_SYNC_CMD:
{ {
const auto cond = m_ir->CreateIsNull(m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::mfc_size))); const auto cond = m_ir->CreateIsNull(m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::mfc_size)));
m_ir->CreateCondBr(cond, exec, fail); m_ir->CreateCondBr(cond, exec, fail);
m_ir->SetInsertPoint(exec); m_ir->SetInsertPoint(exec);
m_ir->CreateFence(llvm::AtomicOrdering::SequentiallyConsistent); m_ir->CreateFence(llvm::AtomicOrdering::SequentiallyConsistent);
@ -3631,8 +3631,8 @@ public:
m_ir->SetInsertPoint(fail); m_ir->SetInsertPoint(fail);
// Get MFC slot, redirect to invalid memory address // Get MFC slot, redirect to invalid memory address
const auto slot = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::mfc_size)); const auto slot = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::mfc_size));
const auto off0 = m_ir->CreateAdd(m_ir->CreateMul(slot, m_ir->getInt32(sizeof(spu_mfc_cmd))), m_ir->getInt32(::offset32(&SPUThread::mfc_queue))); const auto off0 = m_ir->CreateAdd(m_ir->CreateMul(slot, m_ir->getInt32(sizeof(spu_mfc_cmd))), m_ir->getInt32(::offset32(&spu_thread::mfc_queue)));
const auto ptr0 = m_ir->CreateGEP(m_thread, m_ir->CreateZExt(off0, get_type<u64>())); const auto ptr0 = m_ir->CreateGEP(m_thread, m_ir->CreateZExt(off0, get_type<u64>()));
const auto ptr1 = m_ir->CreateGEP(m_memptr, m_ir->getInt64(0xffdeadf0)); const auto ptr1 = m_ir->CreateGEP(m_memptr, m_ir->getInt64(0xffdeadf0));
const auto pmfc = m_ir->CreateSelect(m_ir->CreateICmpULT(slot, m_ir->getInt32(16)), ptr0, ptr1); const auto pmfc = m_ir->CreateSelect(m_ir->CreateICmpULT(slot, m_ir->getInt32(16)), ptr0, ptr1);
@ -3695,7 +3695,7 @@ public:
} }
} }
m_ir->CreateStore(m_ir->CreateAdd(slot, m_ir->getInt32(1)), spu_ptr<u32>(&SPUThread::mfc_size)); m_ir->CreateStore(m_ir->CreateAdd(slot, m_ir->getInt32(1)), spu_ptr<u32>(&spu_thread::mfc_size));
m_ir->CreateBr(next); m_ir->CreateBr(next);
m_ir->SetInsertPoint(next); m_ir->SetInsertPoint(next);
return; return;
@ -3708,7 +3708,7 @@ public:
case MFC_WrListStallAck: case MFC_WrListStallAck:
{ {
const auto mask = eval(splat<u32>(1) << (val & 0x1f)); const auto mask = eval(splat<u32>(1) << (val & 0x1f));
const auto _ptr = spu_ptr<u32>(&SPUThread::ch_stall_mask); const auto _ptr = spu_ptr<u32>(&spu_thread::ch_stall_mask);
const auto _old = m_ir->CreateLoad(_ptr); const auto _old = m_ir->CreateLoad(_ptr);
const auto _new = m_ir->CreateAnd(_old, m_ir->CreateNot(mask.value)); const auto _new = m_ir->CreateAnd(_old, m_ir->CreateNot(mask.value));
m_ir->CreateStore(_new, _ptr); m_ir->CreateStore(_new, _ptr);
@ -3723,18 +3723,18 @@ public:
} }
case SPU_WrDec: case SPU_WrDec:
{ {
m_ir->CreateStore(call(&get_timebased_time), spu_ptr<u64>(&SPUThread::ch_dec_start_timestamp)); m_ir->CreateStore(call(&get_timebased_time), spu_ptr<u64>(&spu_thread::ch_dec_start_timestamp));
m_ir->CreateStore(val.value, spu_ptr<u32>(&SPUThread::ch_dec_value)); m_ir->CreateStore(val.value, spu_ptr<u32>(&spu_thread::ch_dec_value));
return; return;
} }
case SPU_WrEventMask: case SPU_WrEventMask:
{ {
m_ir->CreateStore(val.value, spu_ptr<u32>(&SPUThread::ch_event_mask))->setVolatile(true); m_ir->CreateStore(val.value, spu_ptr<u32>(&spu_thread::ch_event_mask))->setVolatile(true);
return; return;
} }
case SPU_WrEventAck: case SPU_WrEventAck:
{ {
m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::And, spu_ptr<u32>(&SPUThread::ch_event_stat), eval(~val).value, llvm::AtomicOrdering::Release); m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::And, spu_ptr<u32>(&spu_thread::ch_event_stat), eval(~val).value, llvm::AtomicOrdering::Release);
return; return;
} }
case 69: case 69:
@ -3769,7 +3769,7 @@ public:
if (g_cfg.core.spu_block_size == spu_block_size_type::safe) if (g_cfg.core.spu_block_size == spu_block_size_type::safe)
{ {
m_block->block_end = m_ir->GetInsertBlock(); m_block->block_end = m_ir->GetInsertBlock();
m_ir->CreateStore(m_ir->getInt32(m_pos + 4), spu_ptr<u32>(&SPUThread::pc)); m_ir->CreateStore(m_ir->getInt32(m_pos + 4), spu_ptr<u32>(&spu_thread::pc));
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
} }
} }
@ -5330,7 +5330,7 @@ public:
const auto halt = llvm::BasicBlock::Create(m_context, "", m_function); const auto halt = llvm::BasicBlock::Create(m_context, "", m_function);
m_ir->CreateCondBr(cond.value, halt, next); m_ir->CreateCondBr(cond.value, halt, next);
m_ir->SetInsertPoint(halt); m_ir->SetInsertPoint(halt);
const auto pstatus = spu_ptr<u32>(&SPUThread::status); const auto pstatus = spu_ptr<u32>(&spu_thread::status);
const auto chalt = m_ir->getInt32(SPU_STATUS_STOPPED_BY_HALT); const auto chalt = m_ir->getInt32(SPU_STATUS_STOPPED_BY_HALT);
m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::Or, pstatus, chalt, llvm::AtomicOrdering::Release)->setVolatile(true); m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::Or, pstatus, chalt, llvm::AtomicOrdering::Release)->setVolatile(true);
const auto ptr = _ptr<u32>(m_memptr, 0xffdead00); const auto ptr = _ptr<u32>(m_memptr, 0xffdead00);
@ -5391,7 +5391,7 @@ public:
} }
// TODO // TODO
static u32 exec_check_interrupts(SPUThread* _spu, u32 addr) static u32 exec_check_interrupts(spu_thread* _spu, u32 addr)
{ {
_spu->set_interrupt_status(true); _spu->set_interrupt_status(true);
@ -5464,18 +5464,18 @@ public:
if (op.d) if (op.d)
{ {
m_ir->CreateStore(m_ir->getFalse(), spu_ptr<bool>(&SPUThread::interrupts_enabled))->setVolatile(true); m_ir->CreateStore(m_ir->getFalse(), spu_ptr<bool>(&spu_thread::interrupts_enabled))->setVolatile(true);
} }
m_ir->CreateStore(addr.value, spu_ptr<u32>(&SPUThread::pc)); m_ir->CreateStore(addr.value, spu_ptr<u32>(&spu_thread::pc));
const auto type = llvm::FunctionType::get(get_type<void>(), {get_type<u8*>(), get_type<u8*>(), get_type<u32>()}, false)->getPointerTo()->getPointerTo(); const auto type = llvm::FunctionType::get(get_type<void>(), {get_type<u8*>(), get_type<u8*>(), get_type<u32>()}, false)->getPointerTo()->getPointerTo();
const auto disp = m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, m_ir->getInt64(::offset32(&SPUThread::jit_dispatcher))), type); const auto disp = m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, m_ir->getInt64(::offset32(&spu_thread::jit_dispatcher))), type);
const auto ad64 = m_ir->CreateZExt(addr.value, get_type<u64>()); const auto ad64 = m_ir->CreateZExt(addr.value, get_type<u64>());
if (ret && g_cfg.core.spu_block_size != spu_block_size_type::safe) if (ret && g_cfg.core.spu_block_size != spu_block_size_type::safe)
{ {
// Compare address stored in stack mirror with addr // Compare address stored in stack mirror with addr
const auto stack0 = eval(zext<u64>(sp) + ::offset32(&SPUThread::stack_mirror)); const auto stack0 = eval(zext<u64>(sp) + ::offset32(&spu_thread::stack_mirror));
const auto stack1 = eval(stack0 + 8); const auto stack1 = eval(stack0 + 8);
const auto _ret = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack0.value), type)); const auto _ret = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack0.value), type));
const auto link = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack1.value), get_type<u64*>())); const auto link = m_ir->CreateLoad(m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack1.value), get_type<u64*>()));
@ -5595,7 +5595,7 @@ public:
// Exit function on unexpected target // Exit function on unexpected target
m_ir->SetInsertPoint(sw->getDefaultDest()); m_ir->SetInsertPoint(sw->getDefaultDest());
m_ir->CreateStore(addr.value, spu_ptr<u32>(&SPUThread::pc)); m_ir->CreateStore(addr.value, spu_ptr<u32>(&spu_thread::pc));
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
} }
else else
@ -5617,7 +5617,7 @@ public:
{ {
m_block->block_end = m_ir->GetInsertBlock(); m_block->block_end = m_ir->GetInsertBlock();
value_t<u32> srr0; value_t<u32> srr0;
srr0.value = m_ir->CreateLoad(spu_ptr<u32>(&SPUThread::srr0)); srr0.value = m_ir->CreateLoad(spu_ptr<u32>(&spu_thread::srr0));
m_ir->CreateBr(add_block_indirect(op, srr0)); m_ir->CreateBr(add_block_indirect(op, srr0));
} }
@ -5716,7 +5716,7 @@ public:
{ {
// Store the return function chunk address at the stack mirror // Store the return function chunk address at the stack mirror
const auto func = add_function(m_pos + 4); const auto func = add_function(m_pos + 4);
const auto stack0 = eval(zext<u64>(extract(get_vr(1), 3) & 0x3fff0) + ::offset32(&SPUThread::stack_mirror)); const auto stack0 = eval(zext<u64>(extract(get_vr(1), 3) & 0x3fff0) + ::offset32(&spu_thread::stack_mirror));
const auto stack1 = eval(stack0 + 8); const auto stack1 = eval(stack0 + 8);
m_ir->CreateStore(func, m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack0.value), func->getType()->getPointerTo())); m_ir->CreateStore(func, m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack0.value), func->getType()->getPointerTo()));
m_ir->CreateStore(m_ir->getInt64(m_pos + 4), m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack1.value), get_type<u64*>())); m_ir->CreateStore(m_ir->getInt64(m_pos + 4), m_ir->CreateBitCast(m_ir->CreateGEP(m_thread, stack1.value), get_type<u64*>()));

View File

@ -76,10 +76,10 @@ public:
virtual spu_function_t compile(std::vector<u32>&&) = 0; virtual spu_function_t compile(std::vector<u32>&&) = 0;
// Default dispatch function fallback (second arg is unused) // Default dispatch function fallback (second arg is unused)
static void dispatch(SPUThread&, void*, u8* rip); static void dispatch(spu_thread&, void*, u8* rip);
// Target for the unresolved patch point (second arg is unused) // Target for the unresolved patch point (second arg is unused)
static void branch(SPUThread&, void*, u8* rip); static void branch(spu_thread&, void*, u8* rip);
// Get the block at specified address // Get the block at specified address
std::vector<u32> block(const be_t<u32>* ls, u32 lsa); std::vector<u32> block(const be_t<u32>* ls, u32 lsa);

View File

@ -140,7 +140,7 @@ namespace spu
u32 pc = 0; u32 pc = 0;
bool active = false; bool active = false;
concurrent_execution_watchdog(SPUThread& spu) concurrent_execution_watchdog(spu_thread& spu)
:pc(spu.pc) :pc(spu.pc)
{ {
if (g_cfg.core.preferred_spu_threads > 0) if (g_cfg.core.preferred_spu_threads > 0)
@ -391,35 +391,12 @@ spu_imm_table_t::spu_imm_table_t()
} }
} }
void SPUThread::on_spawn() std::string spu_thread::get_name() const
{ {
if (g_cfg.core.thread_scheduler_enabled) return fmt::format("%sSPU[0x%x] Thread (%s)", offset >= RAW_SPU_BASE_ADDR ? "Raw" : "", id, spu_name.get());
{
thread_ctrl::set_thread_affinity_mask(thread_ctrl::get_affinity_mask(thread_class::spu));
}
if (g_cfg.core.lower_spu_priority)
{
thread_ctrl::set_native_priority(-1);
}
} }
void SPUThread::on_init(const std::shared_ptr<void>& _this) std::string spu_thread::dump() const
{
if (!offset)
{
const_cast<u32&>(offset) = verify("SPU LS" HERE, vm::alloc(0x40000, vm::main));
cpu_thread::on_init(_this);
}
}
std::string SPUThread::get_name() const
{
return fmt::format("%sSPU[0x%x] Thread (%s)", offset >= RAW_SPU_BASE_ADDR ? "Raw" : "", id, m_name);
}
std::string SPUThread::dump() const
{ {
std::string ret = cpu_thread::dump(); std::string ret = cpu_thread::dump();
@ -451,7 +428,7 @@ std::string SPUThread::dump() const
return ret; return ret;
} }
void SPUThread::cpu_init() void spu_thread::cpu_init()
{ {
gpr = {}; gpr = {};
fpscr.Reset(); fpscr.Reset();
@ -501,8 +478,15 @@ void SPUThread::cpu_init()
extern thread_local std::string(*g_tls_log_prefix)(); extern thread_local std::string(*g_tls_log_prefix)();
void SPUThread::cpu_task() void spu_thread::cpu_task()
{ {
// Get next PC and SPU Interrupt status
pc = npc.exchange(0);
set_interrupt_status((pc & 1) != 0);
pc &= 0x3fffc;
std::fesetround(FE_TOWARDZERO); std::fesetround(FE_TOWARDZERO);
if (g_cfg.core.set_daz_and_ftz && g_cfg.core.spu_decoder != spu_decoder_type::precise) if (g_cfg.core.set_daz_and_ftz && g_cfg.core.spu_decoder != spu_decoder_type::precise)
@ -513,9 +497,8 @@ void SPUThread::cpu_task()
g_tls_log_prefix = [] g_tls_log_prefix = []
{ {
const auto cpu = static_cast<SPUThread*>(get_current_cpu_thread()); const auto cpu = static_cast<spu_thread*>(get_current_cpu_thread());
return fmt::format("%s [0x%05x]", thread_ctrl::get_name(), cpu->pc);
return fmt::format("%s [0x%05x]", cpu->get_name(), cpu->pc);
}; };
if (jit) if (jit)
@ -525,6 +508,9 @@ void SPUThread::cpu_task()
jit_dispatcher[pc / 4](*this, vm::_ptr<u8>(offset), nullptr); jit_dispatcher[pc / 4](*this, vm::_ptr<u8>(offset), nullptr);
} }
// save next PC and current SPU Interrupt status
npc = pc | (interrupts_enabled);
// Print some stats // Print some stats
LOG_NOTICE(SPU, "Stats: Block Weight: %u (Retreats: %u);", block_counter, block_failure); LOG_NOTICE(SPU, "Stats: Block Weight: %u (Retreats: %u);", block_counter, block_failure);
return; return;
@ -548,7 +534,8 @@ void SPUThread::cpu_task()
{ {
if (UNLIKELY(state)) if (UNLIKELY(state))
{ {
if (check_state()) return; if (check_state())
break;
// Decode single instruction (may be step) // Decode single instruction (may be step)
const u32 op = *reinterpret_cast<const be_t<u32>*>(base + pc); const u32 op = *reinterpret_cast<const be_t<u32>*>(base + pc);
@ -606,29 +593,39 @@ void SPUThread::cpu_task()
break; break;
} }
} }
// save next PC and current SPU Interrupt status
npc = pc | (interrupts_enabled);
} }
void SPUThread::cpu_mem() void spu_thread::cpu_mem()
{ {
//vm::passive_lock(*this); //vm::passive_lock(*this);
} }
void SPUThread::cpu_unmem() void spu_thread::cpu_unmem()
{ {
//state.test_and_set(cpu_flag::memory); //state.test_and_set(cpu_flag::memory);
} }
SPUThread::~SPUThread() spu_thread::~spu_thread()
{ {
// Deallocate Local Storage // Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset); vm::dealloc_verbose_nothrow(offset);
// Deallocate RawSPU ID
if (!group && offset >= RAW_SPU_BASE_ADDR)
{
g_raw_spu_id[index] = 0;
g_raw_spu_ctr--;
}
} }
SPUThread::SPUThread(const std::string& name, u32 index, lv2_spu_group* group) spu_thread::spu_thread(vm::addr_t ls, lv2_spu_group* group, u32 index, std::string_view name)
: cpu_thread(idm::last_id()) : cpu_thread(idm::last_id())
, m_name(name) , spu_name(name)
, index(index) , index(index)
, offset(0) , offset(ls)
, group(group) , group(group)
{ {
if (g_cfg.core.spu_decoder == spu_decoder_type::asmjit) if (g_cfg.core.spu_decoder == spu_decoder_type::asmjit)
@ -652,9 +649,14 @@ SPUThread::SPUThread(const std::string& name, u32 index, lv2_spu_group* group)
std::memset(stack_mirror.data(), 0xff, sizeof(stack_mirror)); std::memset(stack_mirror.data(), 0xff, sizeof(stack_mirror));
} }
} }
if (!group && offset >= RAW_SPU_BASE_ADDR)
{
cpu_init();
}
} }
void SPUThread::push_snr(u32 number, u32 value) void spu_thread::push_snr(u32 number, u32 value)
{ {
// Get channel // Get channel
const auto channel = number & 1 ? &ch_snr2 : &ch_snr1; const auto channel = number & 1 ? &ch_snr2 : &ch_snr1;
@ -670,7 +672,7 @@ void SPUThread::push_snr(u32 number, u32 value)
} }
} }
void SPUThread::do_dma_transfer(const spu_mfc_cmd& args) void spu_thread::do_dma_transfer(const spu_mfc_cmd& args)
{ {
const bool is_get = (args.cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK | MFC_START_MASK)) == MFC_GET_CMD; const bool is_get = (args.cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK | MFC_START_MASK)) == MFC_GET_CMD;
@ -686,7 +688,7 @@ void SPUThread::do_dma_transfer(const spu_mfc_cmd& args)
if (eal < SYS_SPU_THREAD_BASE_LOW) if (eal < SYS_SPU_THREAD_BASE_LOW)
{ {
// RawSPU MMIO // RawSPU MMIO
auto thread = idm::get<RawSPUThread>((eal - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET); auto thread = idm::get<named_thread<spu_thread>>(find_raw_spu((eal - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET));
if (!thread) if (!thread)
{ {
@ -717,7 +719,7 @@ void SPUThread::do_dma_transfer(const spu_mfc_cmd& args)
} }
else if (group && group->threads[index]) else if (group && group->threads[index])
{ {
auto& spu = static_cast<SPUThread&>(*group->threads[index]); auto& spu = static_cast<spu_thread&>(*group->threads[index]);
if (offset + args.size - 1 < 0x40000) // LS access if (offset + args.size - 1 < 0x40000) // LS access
{ {
@ -890,7 +892,7 @@ void SPUThread::do_dma_transfer(const spu_mfc_cmd& args)
} }
} }
bool SPUThread::do_dma_check(const spu_mfc_cmd& args) bool spu_thread::do_dma_check(const spu_mfc_cmd& args)
{ {
const u32 mask = 1u << args.tag; const u32 mask = 1u << args.tag;
@ -940,7 +942,7 @@ bool SPUThread::do_dma_check(const spu_mfc_cmd& args)
return true; return true;
} }
bool SPUThread::do_list_transfer(spu_mfc_cmd& args) bool spu_thread::do_list_transfer(spu_mfc_cmd& args)
{ {
struct list_element struct list_element
{ {
@ -994,7 +996,7 @@ bool SPUThread::do_list_transfer(spu_mfc_cmd& args)
return true; return true;
} }
void SPUThread::do_putlluc(const spu_mfc_cmd& args) void spu_thread::do_putlluc(const spu_mfc_cmd& args)
{ {
const u32 addr = args.eal & -128u; const u32 addr = args.eal & -128u;
@ -1047,7 +1049,7 @@ void SPUThread::do_putlluc(const spu_mfc_cmd& args)
vm::reservation_notifier(addr, 128).notify_all(); vm::reservation_notifier(addr, 128).notify_all();
} }
void SPUThread::do_mfc(bool wait) void spu_thread::do_mfc(bool wait)
{ {
u32 removed = 0; u32 removed = 0;
u32 barrier = 0; u32 barrier = 0;
@ -1149,17 +1151,17 @@ void SPUThread::do_mfc(bool wait)
} }
} }
u32 SPUThread::get_mfc_completed() u32 spu_thread::get_mfc_completed()
{ {
return ch_tag_mask & ~mfc_fence; return ch_tag_mask & ~mfc_fence;
} }
bool SPUThread::process_mfc_cmd(spu_mfc_cmd args) bool spu_thread::process_mfc_cmd(spu_mfc_cmd args)
{ {
// Stall infinitely if MFC queue is full // Stall infinitely if MFC queue is full
while (UNLIKELY(mfc_size >= 16)) while (UNLIKELY(mfc_size >= 16))
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return false; return false;
} }
@ -1192,7 +1194,7 @@ bool SPUThread::process_mfc_cmd(spu_mfc_cmd args)
while (rdata == data && vm::reservation_acquire(raddr, 128) == rtime) while (rdata == data && vm::reservation_acquire(raddr, 128) == rtime)
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
break; break;
} }
@ -1446,7 +1448,7 @@ bool SPUThread::process_mfc_cmd(spu_mfc_cmd args)
args.cmd, args.lsa, args.eal, args.tag, args.size); args.cmd, args.lsa, args.eal, args.tag, args.size);
} }
u32 SPUThread::get_events(bool waiting) u32 spu_thread::get_events(bool waiting)
{ {
const u32 mask1 = ch_event_mask; const u32 mask1 = ch_event_mask;
@ -1485,7 +1487,7 @@ u32 SPUThread::get_events(bool waiting)
}); });
} }
void SPUThread::set_events(u32 mask) void spu_thread::set_events(u32 mask)
{ {
if (mask & ~SPU_EVENT_IMPLEMENTED) if (mask & ~SPU_EVENT_IMPLEMENTED)
{ {
@ -1502,7 +1504,7 @@ void SPUThread::set_events(u32 mask)
} }
} }
void SPUThread::set_interrupt_status(bool enable) void spu_thread::set_interrupt_status(bool enable)
{ {
if (enable) if (enable)
{ {
@ -1520,7 +1522,7 @@ void SPUThread::set_interrupt_status(bool enable)
} }
} }
u32 SPUThread::get_ch_count(u32 ch) u32 spu_thread::get_ch_count(u32 ch)
{ {
LOG_TRACE(SPU, "get_ch_count(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???"); LOG_TRACE(SPU, "get_ch_count(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???");
@ -1542,7 +1544,7 @@ u32 SPUThread::get_ch_count(u32 ch)
fmt::throw_exception("Unknown/illegal channel (ch=%d [%s])" HERE, ch, ch < 128 ? spu_ch_name[ch] : "???"); fmt::throw_exception("Unknown/illegal channel (ch=%d [%s])" HERE, ch, ch < 128 ? spu_ch_name[ch] : "???");
} }
s64 SPUThread::get_ch_value(u32 ch) s64 spu_thread::get_ch_value(u32 ch)
{ {
LOG_TRACE(SPU, "get_ch_value(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???"); LOG_TRACE(SPU, "get_ch_value(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???");
@ -1557,7 +1559,7 @@ s64 SPUThread::get_ch_value(u32 ch)
while (!channel.try_pop(out)) while (!channel.try_pop(out))
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return -1; return -1;
} }
@ -1595,7 +1597,7 @@ s64 SPUThread::get_ch_value(u32 ch)
return out; return out;
} }
if (state & cpu_flag::stop) if (is_stopped())
{ {
return -1; return -1;
} }
@ -1699,7 +1701,7 @@ s64 SPUThread::get_ch_value(u32 ch)
while (res = get_events(), !res) while (res = get_events(), !res)
{ {
if (state & (cpu_flag::stop + cpu_flag::dbg_global_stop)) if (is_stopped())
{ {
return -1; return -1;
} }
@ -1712,7 +1714,7 @@ s64 SPUThread::get_ch_value(u32 ch)
while (res = get_events(true), !res) while (res = get_events(true), !res)
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return -1; return -1;
} }
@ -1734,7 +1736,7 @@ s64 SPUThread::get_ch_value(u32 ch)
fmt::throw_exception("Unknown/illegal channel (ch=%d [%s])" HERE, ch, ch < 128 ? spu_ch_name[ch] : "???"); fmt::throw_exception("Unknown/illegal channel (ch=%d [%s])" HERE, ch, ch < 128 ? spu_ch_name[ch] : "???");
} }
bool SPUThread::set_ch_value(u32 ch, u32 value) bool spu_thread::set_ch_value(u32 ch, u32 value)
{ {
LOG_TRACE(SPU, "set_ch_value(ch=%d [%s], value=0x%x)", ch, ch < 128 ? spu_ch_name[ch] : "???", value); LOG_TRACE(SPU, "set_ch_value(ch=%d [%s], value=0x%x)", ch, ch < 128 ? spu_ch_name[ch] : "???", value);
@ -1752,7 +1754,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{ {
while (!ch_out_intr_mbox.try_push(value)) while (!ch_out_intr_mbox.try_push(value))
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return false; return false;
} }
@ -1898,7 +1900,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{ {
while (!ch_out_mbox.try_push(value)) while (!ch_out_mbox.try_push(value))
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return false; return false;
} }
@ -2043,7 +2045,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
fmt::throw_exception("Unknown/illegal channel (ch=%d [%s], value=0x%x)" HERE, ch, ch < 128 ? spu_ch_name[ch] : "???", value); fmt::throw_exception("Unknown/illegal channel (ch=%d [%s], value=0x%x)" HERE, ch, ch < 128 ? spu_ch_name[ch] : "???", value);
} }
bool SPUThread::stop_and_signal(u32 code) bool spu_thread::stop_and_signal(u32 code)
{ {
LOG_TRACE(SPU, "stop_and_signal(code=0x%x)", code); LOG_TRACE(SPU, "stop_and_signal(code=0x%x)", code);
@ -2087,7 +2089,7 @@ bool SPUThread::stop_and_signal(u32 code)
// HACK: wait for executable code // HACK: wait for executable code
while (!_ref<u32>(pc)) while (!_ref<u32>(pc))
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return false; return false;
} }
@ -2143,7 +2145,7 @@ bool SPUThread::stop_and_signal(u32 code)
// Check group status, wait if necessary // Check group status, wait if necessary
while (group->run_state >= SPU_THREAD_GROUP_STATUS_WAITING && group->run_state <= SPU_THREAD_GROUP_STATUS_SUSPENDED) while (group->run_state >= SPU_THREAD_GROUP_STATUS_WAITING && group->run_state <= SPU_THREAD_GROUP_STATUS_SUSPENDED)
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return false; return false;
} }
@ -2212,7 +2214,7 @@ bool SPUThread::stop_and_signal(u32 code)
while (true) while (true)
{ {
if (state & cpu_flag::stop) if (is_stopped())
{ {
return false; return false;
} }
@ -2246,7 +2248,7 @@ bool SPUThread::stop_and_signal(u32 code)
if (thread.get() != this) if (thread.get() != this)
{ {
thread->notify(); thread_ctrl::notify(*thread);
} }
} }
} }
@ -2285,7 +2287,7 @@ bool SPUThread::stop_and_signal(u32 code)
if (thread && thread.get() != this) if (thread && thread.get() != this)
{ {
thread->state += cpu_flag::stop; thread->state += cpu_flag::stop;
thread->notify(); thread_ctrl::notify(*thread);
} }
} }
@ -2329,7 +2331,7 @@ bool SPUThread::stop_and_signal(u32 code)
} }
} }
void SPUThread::halt() void spu_thread::halt()
{ {
LOG_TRACE(SPU, "halt()"); LOG_TRACE(SPU, "halt()");
@ -2350,7 +2352,7 @@ void SPUThread::halt()
fmt::throw_exception("Halt" HERE); fmt::throw_exception("Halt" HERE);
} }
void SPUThread::fast_call(u32 ls_addr) void spu_thread::fast_call(u32 ls_addr)
{ {
// LS:0x0: this is originally the entry point of the interrupt handler, but interrupts are not implemented // LS:0x0: this is originally the entry point of the interrupt handler, but interrupts are not implemented
_ref<u32>(0) = 0x00000002; // STOP 2 _ref<u32>(0) = 0x00000002; // STOP 2
@ -2378,3 +2380,6 @@ void SPUThread::fast_call(u32 ls_addr)
gpr[0]._u32[3] = old_lr; gpr[0]._u32[3] = old_lr;
gpr[1]._u32[3] = old_stack; gpr[1]._u32[3] = old_stack;
} }
DECLARE(spu_thread::g_raw_spu_ctr){};
DECLARE(spu_thread::g_raw_spu_id){};

View File

@ -11,10 +11,8 @@ struct lv2_event_queue;
struct lv2_spu_group; struct lv2_spu_group;
struct lv2_int_tag; struct lv2_int_tag;
class SPUThread;
// JIT Block // JIT Block
using spu_function_t = void(*)(SPUThread&, void*, u8*); using spu_function_t = void(*)(spu_thread&, void*, u8*);
// SPU Channels // SPU Channels
enum : u32 enum : u32
@ -500,24 +498,22 @@ public:
} }
}; };
class SPUThread : public cpu_thread class spu_thread : public cpu_thread
{ {
public: public:
virtual void on_spawn() override;
virtual void on_init(const std::shared_ptr<void>&) override;
virtual std::string get_name() const override; virtual std::string get_name() const override;
virtual std::string dump() const override; virtual std::string dump() const override;
virtual void cpu_task() override; virtual void cpu_task() override final;
virtual void cpu_mem() override; virtual void cpu_mem() override;
virtual void cpu_unmem() override; virtual void cpu_unmem() override;
virtual ~SPUThread() override; virtual ~spu_thread() override;
void cpu_init(); void cpu_init();
static const u32 id_base = 0x02000000; // TODO (used to determine thread type) static const u32 id_base = 0x02000000; // TODO (used to determine thread type)
static const u32 id_step = 1; static const u32 id_step = 1;
static const u32 id_count = 2048; static const u32 id_count = 2048;
SPUThread(const std::string& name, u32 index, lv2_spu_group* group); spu_thread(vm::addr_t ls, lv2_spu_group* group, u32 index, std::string_view name);
u32 pc = 0; u32 pc = 0;
@ -578,7 +574,7 @@ public:
const u32 offset; // SPU LS offset const u32 offset; // SPU LS offset
lv2_spu_group* const group; // SPU Thread Group lv2_spu_group* const group; // SPU Thread Group
const std::string m_name; // Thread name lf_value<std::string> spu_name; // Thread name
std::unique_ptr<class spu_recompiler_base> jit; // Recompiler instance std::unique_ptr<class spu_recompiler_base> jit; // Recompiler instance
@ -623,4 +619,20 @@ public:
{ {
return *_ptr<T>(lsa); return *_ptr<T>(lsa);
} }
bool read_reg(const u32 addr, u32& value);
bool write_reg(const u32 addr, const u32 value);
static atomic_t<u32> g_raw_spu_ctr;
static atomic_t<u32> g_raw_spu_id[5];
static u32 find_raw_spu(u32 id)
{
if (LIKELY(id < std::size(g_raw_spu_id)))
{
return g_raw_spu_id[id];
}
return -1;
}
}; };

View File

@ -1002,13 +1002,13 @@ DECLARE(lv2_obj::g_ppu);
DECLARE(lv2_obj::g_pending); DECLARE(lv2_obj::g_pending);
DECLARE(lv2_obj::g_waiting); DECLARE(lv2_obj::g_waiting);
void lv2_obj::sleep_timeout(old_thread& thread, u64 timeout) void lv2_obj::sleep_timeout(cpu_thread& thread, u64 timeout)
{ {
std::lock_guard lock(g_mutex); std::lock_guard lock(g_mutex);
const u64 start_time = get_system_time(); const u64 start_time = get_system_time();
if (auto ppu = dynamic_cast<ppu_thread*>(&thread)) if (auto ppu = static_cast<ppu_thread*>(thread.id_type() == 1 ? &thread : nullptr))
{ {
LOG_TRACE(PPU, "sleep() - waiting (%zu)", g_pending.size()); LOG_TRACE(PPU, "sleep() - waiting (%zu)", g_pending.size());
@ -1123,7 +1123,7 @@ void lv2_obj::awake(cpu_thread& cpu, u32 prio)
} }
// Remove pending if necessary // Remove pending if necessary
if (!g_pending.empty() && cpu.get() == thread_ctrl::get_current()) if (!g_pending.empty() && &cpu == get_current_cpu_thread())
{ {
unqueue(g_pending, &cpu); unqueue(g_pending, &cpu);
} }
@ -1165,7 +1165,7 @@ void lv2_obj::schedule_all()
target->state ^= (cpu_flag::signal + cpu_flag::suspend); target->state ^= (cpu_flag::signal + cpu_flag::suspend);
target->start_time = 0; target->start_time = 0;
if (target->get() != thread_ctrl::get_current()) if (target != get_current_cpu_thread())
{ {
target->notify(); target->notify();
} }

View File

@ -241,6 +241,11 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;

View File

@ -57,7 +57,7 @@ bool lv2_event_queue::send(lv2_event event)
else else
{ {
// Store event in In_MBox // Store event in In_MBox
auto& spu = static_cast<SPUThread&>(*sq.front()); auto& spu = static_cast<spu_thread&>(*sq.front());
// TODO: use protocol? // TODO: use protocol?
sq.pop_front(); sq.pop_front();
@ -180,7 +180,7 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
} }
else else
{ {
static_cast<SPUThread&>(*cpu).ch_in_mbox.set_values(1, CELL_ECANCELED); static_cast<spu_thread&>(*cpu).ch_in_mbox.set_values(1, CELL_ECANCELED);
cpu->state += cpu_flag::signal; cpu->state += cpu_flag::signal;
cpu->notify(); cpu->notify();
} }
@ -271,6 +271,11 @@ error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_e
// If cancelled, gpr[3] will be non-zero. Other registers must contain event data. // If cancelled, gpr[3] will be non-zero. Other registers must contain event data.
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;

View File

@ -165,6 +165,11 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;
@ -193,7 +198,11 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
} }
} }
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
if (result) *result = ppu.gpr[6]; if (result) *result = ppu.gpr[6];
return not_an_error(ppu.gpr[3]); return not_an_error(ppu.gpr[3]);
} }
@ -366,7 +375,11 @@ error_code sys_event_flag_cancel(ppu_thread& ppu, u32 id, vm::ptr<u32> num)
} }
} }
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
if (num) *num = value; if (num) *num = value;
return CELL_OK; return CELL_OK;
} }

View File

@ -8,8 +8,6 @@
#include "Emu/Cell/PPUOpcodes.h" #include "Emu/Cell/PPUOpcodes.h"
#include "sys_interrupt.h" #include "sys_interrupt.h"
LOG_CHANNEL(sys_interrupt); LOG_CHANNEL(sys_interrupt);
void lv2_int_serv::exec() void lv2_int_serv::exec()
@ -22,7 +20,7 @@ void lv2_int_serv::exec()
{ ppu_cmd::sleep, 0 } { ppu_cmd::sleep, 0 }
}); });
thread->notify(); thread_ctrl::notify(*thread);
} }
void lv2_int_serv::join() void lv2_int_serv::join()
@ -35,8 +33,8 @@ void lv2_int_serv::join()
{ ppu_cmd::opcode, ppu_instructions::SC(0) }, { ppu_cmd::opcode, ppu_instructions::SC(0) },
}); });
thread->notify(); thread_ctrl::notify(*thread);
thread->join(); (*thread)();
} }
error_code sys_interrupt_tag_destroy(u32 intrtag) error_code sys_interrupt_tag_destroy(u32 intrtag)
@ -86,7 +84,7 @@ error_code _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 int
} }
// Get interrupt thread // Get interrupt thread
const auto it = idm::get_unlocked<ppu_thread>(intrthread); const auto it = idm::get_unlocked<named_thread<ppu_thread>>(intrthread);
if (!it) if (!it)
{ {
@ -110,7 +108,8 @@ error_code _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 int
result = std::make_shared<lv2_int_serv>(it, arg1, arg2); result = std::make_shared<lv2_int_serv>(it, arg1, arg2);
tag->handler = result; tag->handler = result;
it->run(); it->state -= cpu_flag::stop;
thread_ctrl::notify(*it);
return result; return result;
}); });
@ -131,7 +130,7 @@ error_code _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u
if (!handler) if (!handler)
{ {
if (const auto thread = idm::withdraw<ppu_thread>(ih)) if (const auto thread = idm::withdraw<named_thread<ppu_thread>>(ih))
{ {
*r13 = thread->gpr[13]; *r13 = thread->gpr[13];
return CELL_OK; return CELL_OK;

View File

@ -15,11 +15,11 @@ struct lv2_int_serv final : lv2_obj
{ {
static const u32 id_base = 0x0b000000; static const u32 id_base = 0x0b000000;
const std::shared_ptr<ppu_thread> thread; const std::shared_ptr<named_thread<ppu_thread>> thread;
const u64 arg1; const u64 arg1;
const u64 arg2; const u64 arg2;
lv2_int_serv(const std::shared_ptr<ppu_thread>& thread, u64 arg1, u64 arg2) lv2_int_serv(const std::shared_ptr<named_thread<ppu_thread>>& thread, u64 arg1, u64 arg2)
: thread(thread) : thread(thread)
, arg1(arg1) , arg1(arg1)
, arg2(arg2) , arg2(arg2)

View File

@ -274,6 +274,11 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;
@ -290,7 +295,7 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
cond->waiters--; cond->waiters--;
if (mutex->signaled.fetch_dec_sat()) if (mutex->signaled.try_dec())
{ {
ppu.gpr[3] = CELL_EDEADLK; ppu.gpr[3] = CELL_EDEADLK;
break; break;

View File

@ -112,6 +112,11 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout)
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;

View File

@ -152,6 +152,11 @@ error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout)
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;

View File

@ -112,7 +112,7 @@ static void network_clear_queue(ppu_thread& ppu)
extern void network_thread_init() extern void network_thread_init()
{ {
thread_ctrl::make_shared("Network Thread", []() thread_ctrl::spawn("Network Thread", []()
{ {
std::vector<std::shared_ptr<lv2_socket>> socklist; std::vector<std::shared_ptr<lv2_socket>> socklist;
socklist.reserve(lv2_socket::id_count); socklist.reserve(lv2_socket::id_count);
@ -241,7 +241,7 @@ extern void network_thread_init()
CloseHandle(_eventh); CloseHandle(_eventh);
WSACleanup(); WSACleanup();
#endif #endif
})->detach(); });
} }
lv2_socket::lv2_socket(lv2_socket::socket_type s) lv2_socket::lv2_socket(lv2_socket::socket_type s)
@ -338,6 +338,11 @@ s32 sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr> addr,
{ {
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
thread_ctrl::wait(); thread_ctrl::wait();
} }
@ -546,6 +551,11 @@ s32 sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr> addr,
{ {
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
thread_ctrl::wait(); thread_ctrl::wait();
} }
@ -946,6 +956,11 @@ s32 sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32 len, s3
{ {
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
thread_ctrl::wait(); thread_ctrl::wait();
} }
@ -1099,6 +1114,11 @@ s32 sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 len, s32
{ {
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
thread_ctrl::wait(); thread_ctrl::wait();
} }
@ -1546,6 +1566,11 @@ s32 sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 nfds, s3
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;
@ -1740,6 +1765,11 @@ s32 sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set> readf
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;

View File

@ -9,8 +9,6 @@
#include "sys_event.h" #include "sys_event.h"
#include "sys_mmapper.h" #include "sys_mmapper.h"
LOG_CHANNEL(sys_ppu_thread); LOG_CHANNEL(sys_ppu_thread);
void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode) void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode)
@ -40,15 +38,15 @@ void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode)
if (jid == -1) if (jid == -1)
{ {
// Delete detached thread and unqueue // Detach detached thread, id will be removed on cleanup
idm::remove<ppu_thread>(ppu.id); static_cast<named_thread<ppu_thread>&>(ppu) = thread_state::detached;
} }
else if (jid != 0) else if (jid != 0)
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
// Schedule joiner and unqueue // Schedule joiner and unqueue
lv2_obj::awake(*idm::check_unlocked<ppu_thread>(jid), -2); lv2_obj::awake(*idm::check_unlocked<named_thread<ppu_thread>>(jid), -2);
} }
// Unqueue // Unqueue
@ -71,7 +69,7 @@ error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr<u64> vptr
sys_ppu_thread.trace("sys_ppu_thread_join(thread_id=0x%x, vptr=*0x%x)", thread_id, vptr); sys_ppu_thread.trace("sys_ppu_thread_join(thread_id=0x%x, vptr=*0x%x)", thread_id, vptr);
const auto thread = idm::get<ppu_thread>(thread_id, [&](ppu_thread& thread) -> CellError const auto thread = idm::get<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread) -> CellError
{ {
CellError result = thread.joiner.atomic_op([&](u32& value) -> CellError CellError result = thread.joiner.atomic_op([&](u32& value) -> CellError
{ {
@ -120,17 +118,21 @@ error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr<u64> vptr
} }
// Wait for cleanup // Wait for cleanup
thread->join(); (*thread.ptr)();
// Get the exit status from the register // Get the exit status from the register
if (vptr) if (vptr)
{ {
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
*vptr = thread->gpr[3]; *vptr = thread->gpr[3];
} }
// Cleanup // Cleanup
idm::remove<ppu_thread>(thread->id); idm::remove<named_thread<ppu_thread>>(thread->id);
return CELL_OK; return CELL_OK;
} }
@ -138,7 +140,7 @@ error_code sys_ppu_thread_detach(u32 thread_id)
{ {
sys_ppu_thread.trace("sys_ppu_thread_detach(thread_id=0x%x)", thread_id); sys_ppu_thread.trace("sys_ppu_thread_detach(thread_id=0x%x)", thread_id);
const auto thread = idm::check<ppu_thread>(thread_id, [&](ppu_thread& thread) -> CellError const auto thread = idm::check<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread) -> CellError
{ {
return thread.joiner.atomic_op([&](u32& value) -> CellError return thread.joiner.atomic_op([&](u32& value) -> CellError
{ {
@ -180,7 +182,7 @@ error_code sys_ppu_thread_detach(u32 thread_id)
if (thread.ret == CELL_EAGAIN) if (thread.ret == CELL_EAGAIN)
{ {
idm::remove<ppu_thread>(thread_id); idm::remove<named_thread<ppu_thread>>(thread_id);
} }
return CELL_OK; return CELL_OK;
@ -202,7 +204,7 @@ error_code sys_ppu_thread_set_priority(ppu_thread& ppu, u32 thread_id, s32 prio)
return CELL_EINVAL; return CELL_EINVAL;
} }
const auto thread = idm::check<ppu_thread>(thread_id, [&](ppu_thread& thread) const auto thread = idm::check<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread)
{ {
if (thread.prio != prio && thread.prio.exchange(prio) != prio) if (thread.prio != prio && thread.prio.exchange(prio) != prio)
{ {
@ -222,7 +224,7 @@ error_code sys_ppu_thread_get_priority(u32 thread_id, vm::ptr<s32> priop)
{ {
sys_ppu_thread.trace("sys_ppu_thread_get_priority(thread_id=0x%x, priop=*0x%x)", thread_id, priop); sys_ppu_thread.trace("sys_ppu_thread_get_priority(thread_id=0x%x, priop=*0x%x)", thread_id, priop);
const auto thread = idm::check<ppu_thread>(thread_id, [&](ppu_thread& thread) const auto thread = idm::check<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread)
{ {
*priop = thread.prio; *priop = thread.prio;
}); });
@ -249,7 +251,7 @@ error_code sys_ppu_thread_stop(u32 thread_id)
{ {
sys_ppu_thread.todo("sys_ppu_thread_stop(thread_id=0x%x)", thread_id); sys_ppu_thread.todo("sys_ppu_thread_stop(thread_id=0x%x)", thread_id);
const auto thread = idm::get<ppu_thread>(thread_id); const auto thread = idm::get<named_thread<ppu_thread>>(thread_id);
if (!thread) if (!thread)
{ {
@ -263,7 +265,7 @@ error_code sys_ppu_thread_restart(u32 thread_id)
{ {
sys_ppu_thread.todo("sys_ppu_thread_restart(thread_id=0x%x)", thread_id); sys_ppu_thread.todo("sys_ppu_thread_restart(thread_id=0x%x)", thread_id);
const auto thread = idm::get<ppu_thread>(thread_id); const auto thread = idm::get<named_thread<ppu_thread>>(thread_id);
if (!thread) if (!thread)
{ {
@ -273,10 +275,10 @@ error_code sys_ppu_thread_restart(u32 thread_id)
return CELL_OK; return CELL_OK;
} }
error_code _sys_ppu_thread_create(vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> param, u64 arg, u64 unk, s32 prio, u32 stacksize, u64 flags, vm::cptr<char> threadname) error_code _sys_ppu_thread_create(vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> param, u64 arg, u64 unk, s32 prio, u32 _stacksz, u64 flags, vm::cptr<char> threadname)
{ {
sys_ppu_thread.warning("_sys_ppu_thread_create(thread_id=*0x%x, param=*0x%x, arg=0x%llx, unk=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname=%s)", sys_ppu_thread.warning("_sys_ppu_thread_create(thread_id=*0x%x, param=*0x%x, arg=0x%llx, unk=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname=%s)",
thread_id, param, arg, unk, prio, stacksize, flags, threadname); thread_id, param, arg, unk, prio, _stacksz, flags, threadname);
if (prio < 0 || prio > 3071) if (prio < 0 || prio > 3071)
{ {
@ -288,33 +290,38 @@ error_code _sys_ppu_thread_create(vm::ptr<u64> thread_id, vm::ptr<ppu_thread_par
return CELL_EPERM; return CELL_EPERM;
} }
const u32 tid = idm::import<ppu_thread>([&]() // Compute actual stack size and allocate
{ const u32 stack_size = _stacksz >= 4096 ? ::align(std::min<u32>(_stacksz, 0x100000), 4096) : 0x4000;
auto ppu = std::make_shared<ppu_thread>(threadname ? threadname.get_ptr() : "", prio, stacksize);
if ((flags & SYS_PPU_THREAD_CREATE_JOINABLE) != 0) const vm::addr_t stack_base{vm::alloc(_stacksz, vm::stack, 4096)};
if (!stack_base)
{ {
ppu->joiner = 0; return CELL_ENOMEM;
} }
ppu->gpr[13] = param->tls.value(); const u32 tid = idm::import<named_thread<ppu_thread>>([&]()
{
const u32 tid = idm::last_id();
if ((flags & SYS_PPU_THREAD_CREATE_INTERRUPT) == 0) std::string ppu_name;
std::string full_name = fmt::format("PPU[0x%x] Thread", tid);
if (threadname)
{ {
// Initialize thread entry point ppu_name = threadname.get_ptr();
ppu->cmd_list fmt::append(full_name, " (%s)", ppu_name);
({
{ ppu_cmd::set_args, 2 }, arg, unk, // Actually unknown
{ ppu_cmd::lle_call, param->entry.value() },
});
}
else
{
// Save entry for further use (workaround)
ppu->gpr[2] = param->entry.value();
} }
return ppu; ppu_thread_params p;
p.stack_addr = stack_base;
p.stack_size = stack_size;
p.tls_addr = param->tls;
p.entry = param->entry;
p.arg0 = arg;
p.arg1 = unk;
return std::make_shared<named_thread<ppu_thread>>(full_name, p, ppu_name, prio, 1 - static_cast<int>(flags & 3));
}); });
if (!tid) if (!tid)
@ -330,7 +337,7 @@ error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id)
{ {
sys_ppu_thread.trace("sys_ppu_thread_start(thread_id=0x%x)", thread_id); sys_ppu_thread.trace("sys_ppu_thread_start(thread_id=0x%x)", thread_id);
const auto thread = idm::get<ppu_thread>(thread_id, [&](ppu_thread& thread) const auto thread = idm::get<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread)
{ {
lv2_obj::awake(thread, -2); lv2_obj::awake(thread, -2);
}); });
@ -347,10 +354,10 @@ error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id)
} }
else else
{ {
thread->notify(); thread_ctrl::notify(*thread);
// Dirty hack for sound: confirm the creation of _mxr000 event queue // Dirty hack for sound: confirm the creation of _mxr000 event queue
if (thread->m_name == "_cellsurMixerMain") if (thread->ppu_name.get() == "_cellsurMixerMain"sv)
{ {
lv2_obj::sleep(ppu); lv2_obj::sleep(ppu);
@ -360,10 +367,18 @@ error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id)
return (eq.name == "_mxr000\0"_u64) || (eq.key == 0x8000cafe02460300); return (eq.name == "_mxr000\0"_u64) || (eq.key == 0x8000cafe02460300);
})) }))
{ {
if (ppu.is_stopped())
{
return 0;
}
thread_ctrl::wait_for(50000); thread_ctrl::wait_for(50000);
} }
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
} }
} }
@ -372,22 +387,26 @@ error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id)
error_code sys_ppu_thread_rename(u32 thread_id, vm::cptr<char> name) error_code sys_ppu_thread_rename(u32 thread_id, vm::cptr<char> name)
{ {
sys_ppu_thread.todo("sys_ppu_thread_rename(thread_id=0x%x, name=%s)", thread_id, name); sys_ppu_thread.warning("sys_ppu_thread_rename(thread_id=0x%x, name=%s)", thread_id, name);
const auto thread = idm::get<ppu_thread>(thread_id); const auto thread = idm::get<named_thread<ppu_thread>>(thread_id);
if (!thread) if (!thread)
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
// thread_ctrl name is not changed (TODO)
thread->ppu_name.assign(name.get_ptr());
return CELL_OK; return CELL_OK;
} }
error_code sys_ppu_thread_recover_page_fault(u32 thread_id) error_code sys_ppu_thread_recover_page_fault(u32 thread_id)
{ {
sys_ppu_thread.warning("sys_ppu_thread_recover_page_fault(thread_id=0x%x)", thread_id); sys_ppu_thread.warning("sys_ppu_thread_recover_page_fault(thread_id=0x%x)", thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
const auto thread = idm::get<named_thread<ppu_thread>>(thread_id);
if (!thread) if (!thread)
{ {
return CELL_ESRCH; return CELL_ESRCH;
@ -421,7 +440,8 @@ error_code sys_ppu_thread_get_page_fault_context(u32 thread_id, vm::ptr<sys_ppu_
{ {
sys_ppu_thread.todo("sys_ppu_thread_get_page_fault_context(thread_id=0x%x, ctxt=*0x%x)", thread_id, ctxt); sys_ppu_thread.todo("sys_ppu_thread_get_page_fault_context(thread_id=0x%x, ctxt=*0x%x)", thread_id, ctxt);
const auto thread = idm::get<ppu_thread>(thread_id); const auto thread = idm::get<named_thread<ppu_thread>>(thread_id);
if (!thread) if (!thread)
{ {
return CELL_ESRCH; return CELL_ESRCH;

View File

@ -246,7 +246,7 @@ void _sys_process_exit(ppu_thread& ppu, s32 status, u32 arg2, u32 arg3)
Emu.Stop(); Emu.Stop();
}); });
thread_ctrl::eternalize(); ppu.state += cpu_flag::dbg_global_stop;
} }
void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> arg, u32 arg_size, u32 arg4) void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> arg, u32 arg_size, u32 arg4)
@ -314,5 +314,5 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
Emu.BootGame(path, true); Emu.BootGame(path, true);
}); });
thread_ctrl::eternalize(); ppu.state += cpu_flag::dbg_global_stop;
} }

View File

@ -129,6 +129,11 @@ error_code sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;
@ -318,6 +323,11 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;

View File

@ -123,6 +123,11 @@ error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout)
while (!ppu.state.test_and_reset(cpu_flag::signal)) while (!ppu.state.test_and_reset(cpu_flag::signal))
{ {
if (ppu.is_stopped())
{
return 0;
}
if (timeout) if (timeout)
{ {
const u64 passed = get_system_time() - ppu.start_time; const u64 passed = get_system_time() - ppu.start_time;

View File

@ -15,8 +15,6 @@
#include "sys_event.h" #include "sys_event.h"
#include "sys_spu.h" #include "sys_spu.h"
LOG_CHANNEL(sys_spu); LOG_CHANNEL(sys_spu);
void sys_spu_image::load(const fs::file& stream) void sys_spu_image::load(const fs::file& stream)
@ -233,11 +231,25 @@ error_code sys_spu_thread_initialize(vm::ptr<u32> thread, u32 group_id, u32 spu_
sys_spu.todo("Unimplemented SPU Thread options (0x%x)", option); sys_spu.todo("Unimplemented SPU Thread options (0x%x)", option);
} }
auto spu = idm::make_ptr<SPUThread>(thread_name, spu_num, group.get()); const vm::addr_t ls_addr{verify("SPU LS" HERE, vm::alloc(0x40000, vm::main))};
*thread = spu->id; const u32 tid = idm::import<named_thread<spu_thread>>([&]()
{
const u32 tid = idm::last_id();
std::string full_name = fmt::format("SPU[0x%x] Thread", tid);
if (!thread_name.empty())
{
fmt::append(full_name, " (%s)", thread_name);
}
group->threads[spu_num] = std::make_shared<named_thread<spu_thread>>(full_name, ls_addr, group.get(), spu_num, thread_name);
return group->threads[spu_num];
});
*thread = tid;
group->threads[spu_num] = std::move(spu);
group->args[spu_num] = {arg->arg1, arg->arg2, arg->arg3, arg->arg4}; group->args[spu_num] = {arg->arg1, arg->arg2, arg->arg3, arg->arg4};
group->imgs[spu_num] = std::make_pair(*img, std::vector<sys_spu_segment>()); group->imgs[spu_num] = std::make_pair(*img, std::vector<sys_spu_segment>());
group->imgs[spu_num].second.assign(img->segs.get_ptr(), img->segs.get_ptr() + img->nsegs); group->imgs[spu_num].second.assign(img->segs.get_ptr(), img->segs.get_ptr() + img->nsegs);
@ -254,9 +266,9 @@ error_code sys_spu_thread_set_argument(u32 id, vm::ptr<sys_spu_thread_argument>
{ {
sys_spu.warning("sys_spu_thread_set_argument(id=0x%x, arg=*0x%x)", id, arg); sys_spu.warning("sys_spu_thread_set_argument(id=0x%x, arg=*0x%x)", id, arg);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -274,9 +286,9 @@ error_code sys_spu_thread_get_exit_status(u32 id, vm::ptr<u32> status)
{ {
sys_spu.warning("sys_spu_thread_get_exit_status(id=0x%x, status=*0x%x)", id, status); sys_spu.warning("sys_spu_thread_get_exit_status(id=0x%x, status=*0x%x)", id, status);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (UNLIKELY(!thread)) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -342,7 +354,7 @@ error_code sys_spu_thread_group_destroy(u32 id)
{ {
if (auto thread = std::move(ptr)) if (auto thread = std::move(ptr))
{ {
idm::remove<SPUThread>(thread->id); idm::remove<named_thread<spu_thread>>(thread->id);
} }
} }
@ -384,8 +396,8 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id)
sys_spu_image::deploy(thread->offset, img.second.data(), img.first.nsegs); sys_spu_image::deploy(thread->offset, img.second.data(), img.first.nsegs);
thread->pc = img.first.entry_point;
thread->cpu_init(); thread->cpu_init();
thread->npc = img.first.entry_point;
thread->gpr[3] = v128::from64(0, args[0]); thread->gpr[3] = v128::from64(0, args[0]);
thread->gpr[4] = v128::from64(0, args[1]); thread->gpr[4] = v128::from64(0, args[1]);
thread->gpr[5] = v128::from64(0, args[2]); thread->gpr[5] = v128::from64(0, args[2]);
@ -403,7 +415,8 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id)
{ {
if (thread) if (thread)
{ {
thread->run(); thread->state -= cpu_flag::stop;
thread_ctrl::notify(*thread);
} }
} }
@ -501,7 +514,7 @@ error_code sys_spu_thread_group_resume(u32 id)
if (thread) if (thread)
{ {
thread->state -= cpu_flag::suspend; thread->state -= cpu_flag::suspend;
thread->notify(); thread_ctrl::notify(*thread);
} }
} }
@ -539,11 +552,11 @@ error_code sys_spu_thread_group_terminate(u32 id, s32 value)
sys_spu.warning("sys_spu_thread_group_terminate(id=0x%x, value=0x%x)", id, value); sys_spu.warning("sys_spu_thread_group_terminate(id=0x%x, value=0x%x)", id, value);
// The id can be either SPU Thread Group or SPU Thread // The id can be either SPU Thread Group or SPU Thread
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
const auto _group = idm::get<lv2_spu_group>(id); const auto _group = idm::get<lv2_spu_group>(id);
const auto group = thread ? thread->group : _group.get(); const auto group = thread ? thread->group : _group.get();
if (!group && !thread) if (!group && (!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -581,7 +594,7 @@ error_code sys_spu_thread_group_terminate(u32 id, s32 value)
if (thread) if (thread)
{ {
thread->state += cpu_flag::stop; thread->state += cpu_flag::stop;
thread->notify(); thread_ctrl::notify(*thread);
} }
} }
@ -627,6 +640,11 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr<u32> cause
while ((group->join_state & ~SPU_TGJSF_IS_JOINING) == 0) while ((group->join_state & ~SPU_TGJSF_IS_JOINING) == 0)
{ {
if (ppu.is_stopped())
{
return 0;
}
bool stopped = true; bool stopped = true;
for (auto& t : group->threads) for (auto& t : group->threads)
@ -648,7 +666,6 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr<u32> cause
// TODO // TODO
group->cv.wait(group->mutex, 1000); group->cv.wait(group->mutex, 1000);
thread_ctrl::test();
} }
join_state = group->join_state; join_state = group->join_state;
@ -657,7 +674,10 @@ error_code sys_spu_thread_group_join(ppu_thread& ppu, u32 id, vm::ptr<u32> cause
group->run_state = SPU_THREAD_GROUP_STATUS_INITIALIZED; // hack group->run_state = SPU_THREAD_GROUP_STATUS_INITIALIZED; // hack
} }
ppu.test_state(); if (ppu.test_stopped())
{
return 0;
}
switch (join_state & ~SPU_TGJSF_IS_JOINING) switch (join_state & ~SPU_TGJSF_IS_JOINING)
{ {
@ -743,9 +763,9 @@ error_code sys_spu_thread_write_ls(u32 id, u32 lsa, u64 value, u32 type)
{ {
sys_spu.trace("sys_spu_thread_write_ls(id=0x%x, lsa=0x%05x, value=0x%llx, type=%d)", id, lsa, value, type); sys_spu.trace("sys_spu_thread_write_ls(id=0x%x, lsa=0x%05x, value=0x%llx, type=%d)", id, lsa, value, type);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -780,9 +800,9 @@ error_code sys_spu_thread_read_ls(u32 id, u32 lsa, vm::ptr<u64> value, u32 type)
{ {
sys_spu.trace("sys_spu_thread_read_ls(id=0x%x, lsa=0x%05x, value=*0x%x, type=%d)", id, lsa, value, type); sys_spu.trace("sys_spu_thread_read_ls(id=0x%x, lsa=0x%05x, value=*0x%x, type=%d)", id, lsa, value, type);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -817,9 +837,9 @@ error_code sys_spu_thread_write_spu_mb(u32 id, u32 value)
{ {
sys_spu.warning("sys_spu_thread_write_spu_mb(id=0x%x, value=0x%x)", id, value); sys_spu.warning("sys_spu_thread_write_spu_mb(id=0x%x, value=0x%x)", id, value);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -842,9 +862,9 @@ error_code sys_spu_thread_set_spu_cfg(u32 id, u64 value)
{ {
sys_spu.warning("sys_spu_thread_set_spu_cfg(id=0x%x, value=0x%x)", id, value); sys_spu.warning("sys_spu_thread_set_spu_cfg(id=0x%x, value=0x%x)", id, value);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -863,9 +883,9 @@ error_code sys_spu_thread_get_spu_cfg(u32 id, vm::ptr<u64> value)
{ {
sys_spu.warning("sys_spu_thread_get_spu_cfg(id=0x%x, value=*0x%x)", id, value); sys_spu.warning("sys_spu_thread_get_spu_cfg(id=0x%x, value=*0x%x)", id, value);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -879,9 +899,9 @@ error_code sys_spu_thread_write_snr(u32 id, u32 number, u32 value)
{ {
sys_spu.trace("sys_spu_thread_write_snr(id=0x%x, number=%d, value=0x%x)", id, number, value); sys_spu.trace("sys_spu_thread_write_snr(id=0x%x, number=%d, value=0x%x)", id, number, value);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1016,10 +1036,10 @@ error_code sys_spu_thread_connect_event(u32 id, u32 eq, u32 et, u8 spup)
{ {
sys_spu.warning("sys_spu_thread_connect_event(id=0x%x, eq=0x%x, et=%d, spup=%d)", id, eq, et, spup); sys_spu.warning("sys_spu_thread_connect_event(id=0x%x, eq=0x%x, et=%d, spup=%d)", id, eq, et, spup);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
const auto queue = idm::get<lv2_obj, lv2_event_queue>(eq); const auto queue = idm::get<lv2_obj, lv2_event_queue>(eq);
if (!thread || !queue) if (UNLIKELY(!queue || !thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1048,9 +1068,9 @@ error_code sys_spu_thread_disconnect_event(u32 id, u32 et, u8 spup)
{ {
sys_spu.warning("sys_spu_thread_disconnect_event(id=0x%x, et=%d, spup=%d)", id, et, spup); sys_spu.warning("sys_spu_thread_disconnect_event(id=0x%x, et=%d, spup=%d)", id, et, spup);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1079,10 +1099,10 @@ error_code sys_spu_thread_bind_queue(u32 id, u32 spuq, u32 spuq_num)
{ {
sys_spu.warning("sys_spu_thread_bind_queue(id=0x%x, spuq=0x%x, spuq_num=0x%x)", id, spuq, spuq_num); sys_spu.warning("sys_spu_thread_bind_queue(id=0x%x, spuq=0x%x, spuq_num=0x%x)", id, spuq, spuq_num);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
const auto queue = idm::get<lv2_obj, lv2_event_queue>(spuq); const auto queue = idm::get<lv2_obj, lv2_event_queue>(spuq);
if (!thread || !queue) if (UNLIKELY(!queue || !thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1123,9 +1143,9 @@ error_code sys_spu_thread_unbind_queue(u32 id, u32 spuq_num)
{ {
sys_spu.warning("sys_spu_thread_unbind_queue(id=0x%x, spuq_num=0x%x)", id, spuq_num); sys_spu.warning("sys_spu_thread_unbind_queue(id=0x%x, spuq_num=0x%x)", id, spuq_num);
const auto thread = idm::get<SPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(id);
if (!thread) if (UNLIKELY(!thread || !thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1251,16 +1271,27 @@ error_code sys_raw_spu_create(vm::ptr<u32> id, vm::ptr<void> attr)
// TODO: check number set by sys_spu_initialize() // TODO: check number set by sys_spu_initialize()
const auto thread = idm::make_ptr<RawSPUThread>(""); if (!spu_thread::g_raw_spu_ctr.try_inc(5))
if (!thread)
{ {
return CELL_EAGAIN; return CELL_EAGAIN;
} }
thread->cpu_init(); u32 index = 0;
*id = thread->index; // Find free RawSPU ID
while (!spu_thread::g_raw_spu_id[index].try_inc(1))
{
if (++index == 5)
index = 0;
}
const vm::addr_t ls_addr{verify(HERE, vm::falloc(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index, 0x40000, vm::spu))};
const u32 tid = idm::make<named_thread<spu_thread>>(fmt::format("RawSPU[0x%x] Thread", index), ls_addr, nullptr, index, "");
spu_thread::g_raw_spu_id[index] = verify("RawSPU ID" HERE, tid);
*id = index;
return CELL_OK; return CELL_OK;
} }
@ -1269,9 +1300,9 @@ error_code sys_raw_spu_destroy(ppu_thread& ppu, u32 id)
{ {
sys_spu.warning("sys_raw_spu_destroy(id=%d)", id); sys_spu.warning("sys_raw_spu_destroy(id=%d)", id);
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1320,8 +1351,7 @@ error_code sys_raw_spu_destroy(ppu_thread& ppu, u32 id)
idm::remove<lv2_obj, lv2_int_serv>(pair.second); idm::remove<lv2_obj, lv2_int_serv>(pair.second);
} }
idm::remove<RawSPUThread>(thread->id); idm::remove<named_thread<spu_thread>>(thread->id);
return CELL_OK; return CELL_OK;
} }
@ -1340,9 +1370,9 @@ error_code sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread,
{ {
std::shared_ptr<lv2_int_tag> result; std::shared_ptr<lv2_int_tag> result;
auto thread = idm::check_unlocked<RawSPUThread>(id); auto thread = idm::check_unlocked<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (!thread || thread->group)
{ {
error = CELL_ESRCH; error = CELL_ESRCH;
return result; return result;
@ -1379,9 +1409,9 @@ error_code sys_raw_spu_set_int_mask(u32 id, u32 class_id, u64 mask)
return CELL_EINVAL; return CELL_EINVAL;
} }
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1400,9 +1430,9 @@ error_code sys_raw_spu_get_int_mask(u32 id, u32 class_id, vm::ptr<u64> mask)
return CELL_EINVAL; return CELL_EINVAL;
} }
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1421,9 +1451,9 @@ error_code sys_raw_spu_set_int_stat(u32 id, u32 class_id, u64 stat)
return CELL_EINVAL; return CELL_EINVAL;
} }
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1442,9 +1472,9 @@ error_code sys_raw_spu_get_int_stat(u32 id, u32 class_id, vm::ptr<u64> stat)
return CELL_EINVAL; return CELL_EINVAL;
} }
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1458,9 +1488,9 @@ error_code sys_raw_spu_read_puint_mb(u32 id, vm::ptr<u32> value)
{ {
sys_spu.trace("sys_raw_spu_read_puint_mb(id=%d, value=*0x%x)", id, value); sys_spu.trace("sys_raw_spu_read_puint_mb(id=%d, value=*0x%x)", id, value);
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1479,9 +1509,9 @@ error_code sys_raw_spu_set_spu_cfg(u32 id, u32 value)
fmt::throw_exception("Unexpected value (0x%x)" HERE, value); fmt::throw_exception("Unexpected value (0x%x)" HERE, value);
} }
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -1495,9 +1525,9 @@ error_code sys_raw_spu_get_spu_cfg(u32 id, vm::ptr<u32> value)
{ {
sys_spu.trace("sys_raw_spu_get_spu_afg(id=%d, value=*0x%x)", id, value); sys_spu.trace("sys_raw_spu_get_spu_afg(id=%d, value=*0x%x)", id, value);
const auto thread = idm::get<RawSPUThread>(id); const auto thread = idm::get<named_thread<spu_thread>>(spu_thread::find_raw_spu(id));
if (!thread) if (UNLIKELY(!thread || thread->group))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "sys_event.h" #include "sys_event.h"
#include "Emu/Cell/SPUThread.h"
enum : s32 enum : s32
{ {
@ -214,8 +215,6 @@ enum : u32
SPU_TGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit SPU_TGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit
}; };
class SPUThread;
struct lv2_spu_group struct lv2_spu_group
{ {
static const u32 id_base = 1; // Wrong? static const u32 id_base = 1; // Wrong?
@ -236,7 +235,7 @@ struct lv2_spu_group
atomic_t<u32> join_state; // flags used to detect exit cause atomic_t<u32> join_state; // flags used to detect exit cause
cond_variable cv; // used to signal waiting PPU thread cond_variable cv; // used to signal waiting PPU thread
std::array<std::shared_ptr<SPUThread>, 256> threads; // SPU Threads std::array<std::shared_ptr<named_thread<spu_thread>>, 256> threads; // SPU Threads
std::array<std::pair<sys_spu_image, std::vector<sys_spu_segment>>, 256> imgs; // SPU Images std::array<std::pair<sys_spu_image, std::vector<sys_spu_segment>>, 256> imgs; // SPU Images
std::array<std::array<u64, 4>, 256> args; // SPU Thread Arguments std::array<std::array<u64, 4>, 256> args; // SPU Thread Arguments

View File

@ -114,7 +114,7 @@ struct lv2_obj
} }
// Remove the current thread from the scheduling queue, register timeout // Remove the current thread from the scheduling queue, register timeout
static void sleep_timeout(old_thread&, u64 timeout); static void sleep_timeout(cpu_thread&, u64 timeout);
static void sleep(cpu_thread& thread, u64 timeout = 0) static void sleep(cpu_thread& thread, u64 timeout = 0)
{ {
@ -224,7 +224,7 @@ private:
static std::deque<class cpu_thread*> g_pending; static std::deque<class cpu_thread*> g_pending;
// Scheduler queue for timeouts (wait until -> thread) // Scheduler queue for timeouts (wait until -> thread)
static std::deque<std::pair<u64, old_thread*>> g_waiting; static std::deque<std::pair<u64, class cpu_thread*>> g_waiting;
static void schedule_all(); static void schedule_all();
}; };

View File

@ -11,13 +11,11 @@
#include <thread> #include <thread>
LOG_CHANNEL(sys_timer); LOG_CHANNEL(sys_timer);
extern u64 get_system_time(); extern u64 get_system_time();
void lv2_timer::on_task() void lv2_timer_context::operator()()
{ {
while (!Emu.IsStopped()) while (!Emu.IsStopped())
{ {
@ -50,7 +48,6 @@ void lv2_timer::on_task()
} }
// TODO: use single global dedicated thread for busy waiting, no timer threads // TODO: use single global dedicated thread for busy waiting, no timer threads
lv2_obj::sleep_timeout(*this, next - _now);
thread_ctrl::wait_for(next - _now); thread_ctrl::wait_for(next - _now);
} }
else if (_state == SYS_TIMER_STATE_STOP) else if (_state == SYS_TIMER_STATE_STOP)
@ -64,19 +61,17 @@ void lv2_timer::on_task()
} }
} }
void lv2_timer::on_stop() void lv2_timer_context::on_abort()
{ {
// Signal thread using invalid state // Signal thread using invalid state
state = -1; state = -1;
notify();
join();
} }
error_code sys_timer_create(vm::ptr<u32> timer_id) error_code sys_timer_create(vm::ptr<u32> timer_id)
{ {
sys_timer.warning("sys_timer_create(timer_id=*0x%x)", timer_id); sys_timer.warning("sys_timer_create(timer_id=*0x%x)", timer_id);
if (const u32 id = idm::make<lv2_obj, lv2_timer>()) if (const u32 id = idm::make<lv2_obj, lv2_timer>("Timer Thread"))
{ {
*timer_id = id; *timer_id = id;
return CELL_OK; return CELL_OK;
@ -155,7 +150,7 @@ error_code _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
{ {
std::lock_guard lock(timer.mutex); std::unique_lock lock(timer.mutex);
if (timer.state != SYS_TIMER_STATE_STOP) if (timer.state != SYS_TIMER_STATE_STOP)
{ {
@ -171,7 +166,9 @@ error_code _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
timer.expire = base_time ? base_time : start_time + period; timer.expire = base_time ? base_time : start_time + period;
timer.period = period; timer.period = period;
timer.state = SYS_TIMER_STATE_RUN; timer.state = SYS_TIMER_STATE_RUN;
timer.notify();
lock.unlock();
thread_ctrl::notify(timer);
return {}; return {};
}); });
@ -311,6 +308,11 @@ error_code sys_timer_usleep(ppu_thread& ppu, u64 sleep_time)
while (sleep_time >= passed) while (sleep_time >= passed)
{ {
if (ppu.is_stopped())
{
return 0;
}
remaining = sleep_time - passed; remaining = sleep_time - passed;
if (remaining > host_min_quantum) if (remaining > host_min_quantum)

View File

@ -17,12 +17,12 @@ struct sys_timer_information_t
be_t<u32> pad; be_t<u32> pad;
}; };
struct lv2_timer final : public lv2_obj, public old_thread struct lv2_timer_context : lv2_obj
{ {
static const u32 id_base = 0x11000000; static const u32 id_base = 0x11000000;
void on_task() override; void operator()();
void on_stop() override; void on_abort();
semaphore<> mutex; semaphore<> mutex;
atomic_t<u32> state{SYS_TIMER_STATE_STOP}; atomic_t<u32> state{SYS_TIMER_STATE_STOP};
@ -36,6 +36,8 @@ struct lv2_timer final : public lv2_obj, public old_thread
atomic_t<u64> period{0}; // Period (oneshot if 0) atomic_t<u64> period{0}; // Period (oneshot if 0)
}; };
using lv2_timer = named_thread<lv2_timer_context>;
class ppu_thread; class ppu_thread;
// Syscalls // Syscalls

View File

@ -1,5 +1,6 @@
#include "stdafx.h" #include "stdafx.h"
#include "IdManager.h" #include "IdManager.h"
#include "Utilities/Thread.h"
shared_mutex id_manager::g_mutex; shared_mutex id_manager::g_mutex;
@ -23,7 +24,7 @@ id_manager::id_map::pointer idm::allocate_id(const id_manager::id_key& info, u32
if (_next >= base && _next < base + step * count) if (_next >= base && _next < base + step * count)
{ {
g_id = _next; g_id = _next;
vec.emplace_back(id_manager::id_key(_next, info.type(), info.on_stop()), nullptr); vec.emplace_back(id_manager::id_key(_next, info.type()), nullptr);
return &vec.back(); return &vec.back();
} }
} }
@ -37,7 +38,7 @@ id_manager::id_map::pointer idm::allocate_id(const id_manager::id_key& info, u32
if (!ptr->second) if (!ptr->second)
{ {
g_id = next; g_id = next;
ptr->first = id_manager::id_key(next, info.type(), info.on_stop()); ptr->first = id_manager::id_key(next, info.type());
return ptr; return ptr;
} }
} }
@ -60,13 +61,9 @@ void idm::clear()
{ {
for (auto& pair : map) for (auto& pair : map)
{ {
if (auto ptr = pair.second.get())
{
pair.first.on_stop()(ptr);
pair.second.reset(); pair.second.reset();
pair.first = {}; pair.first = {};
} }
}
map.clear(); map.clear();
} }
@ -82,13 +79,8 @@ void fxm::init()
void fxm::clear() void fxm::clear()
{ {
// Call recorded finalization functions for all IDs // Call recorded finalization functions for all IDs
for (auto& pair : g_vec) for (auto& val : g_vec)
{ {
if (auto ptr = pair.second.get()) val.reset();
{
pair.first(ptr);
pair.second.reset();
pair.first = nullptr;
}
} }
} }

View File

@ -35,46 +35,6 @@ namespace id_manager
static_assert(u64{step} * count + base < UINT32_MAX, "ID traits: invalid object range"); static_assert(u64{step} * count + base < UINT32_MAX, "ID traits: invalid object range");
}; };
// Optional object initialization function (called after ID registration)
template <typename T, typename = void>
struct on_init
{
static inline void func(T*, const std::shared_ptr<void>&)
{
// Forbid forward declarations
static constexpr auto size = sizeof(std::conditional_t<std::is_void<T>::value, void*, T>);
}
};
template <typename T>
struct on_init<T, decltype(std::declval<T>().on_init(std::declval<const std::shared_ptr<void>&>()))>
{
static inline void func(T* ptr, const std::shared_ptr<void>& _ptr)
{
if (ptr) ptr->on_init(_ptr);
}
};
// Optional object finalization function (called after ID removal)
template <typename T, typename = void>
struct on_stop
{
static inline void func(T*)
{
// Forbid forward declarations
static constexpr auto size = sizeof(std::conditional_t<std::is_void<T>::value, void*, T>);
}
};
template <typename T>
struct on_stop<T, decltype(std::declval<T>().on_stop())>
{
static inline void func(T* ptr)
{
if (ptr) ptr->on_stop();
}
};
// Correct usage testing // Correct usage testing
template <typename T, typename T2, typename = void> template <typename T, typename T2, typename = void>
struct id_verify : std::integral_constant<bool, std::is_base_of<T, T2>::value> struct id_verify : std::integral_constant<bool, std::is_base_of<T, T2>::value>
@ -118,16 +78,6 @@ namespace id_manager
{ {
return add_type(0); return add_type(0);
} }
// Get type finalizer
template <typename T>
static inline auto get_stop()
{
return [](void* ptr) -> void
{
return id_manager::on_stop<T>::func(static_cast<T*>(ptr));
};
}
}; };
template <typename T> template <typename T>
@ -138,15 +88,13 @@ namespace id_manager
{ {
u32 m_value; // ID value u32 m_value; // ID value
u32 m_type; // True object type u32 m_type; // True object type
void (*m_stop)(void*); // Finalizer
public: public:
id_key() = default; id_key() = default;
id_key(u32 value, u32 type, void (*stop)(void*)) id_key(u32 value, u32 type)
: m_value(value) : m_value(value)
, m_type(type) , m_type(type)
, m_stop(stop)
{ {
} }
@ -160,11 +108,6 @@ namespace id_manager
return m_type; return m_type;
} }
auto on_stop() const
{
return m_stop;
}
operator u32() const operator u32() const
{ {
return m_value; return m_value;
@ -301,7 +244,7 @@ class idm
static_assert(id_manager::id_verify<T, Type>::value, "Invalid ID type combination"); static_assert(id_manager::id_verify<T, Type>::value, "Invalid ID type combination");
// ID info // ID info
const id_manager::id_key info{get_type<T>(), get_type<Type>(), id_manager::typeinfo::get_stop<Type>()}; const id_manager::id_key info{get_type<T>(), get_type<Type>()};
// ID traits // ID traits
using traits = id_manager::id_traits<Type>; using traits = id_manager::id_traits<Type>;
@ -342,7 +285,6 @@ public:
{ {
if (auto pair = create_id<T, Make>([&] { return std::make_shared<Make>(std::forward<Args>(args)...); })) if (auto pair = create_id<T, Make>([&] { return std::make_shared<Make>(std::forward<Args>(args)...); }))
{ {
id_manager::on_init<Make>::func(static_cast<Make*>(pair->second.get()), pair->second);
return {pair->second, static_cast<Make*>(pair->second.get())}; return {pair->second, static_cast<Make*>(pair->second.get())};
} }
@ -355,7 +297,6 @@ public:
{ {
if (auto pair = create_id<T, Make>([&] { return std::make_shared<Make>(std::forward<Args>(args)...); })) if (auto pair = create_id<T, Make>([&] { return std::make_shared<Make>(std::forward<Args>(args)...); }))
{ {
id_manager::on_init<Make>::func(static_cast<Make*>(pair->second.get()), pair->second);
return pair->first; return pair->first;
} }
@ -368,7 +309,6 @@ public:
{ {
if (auto pair = create_id<T, Made>([&] { return ptr; })) if (auto pair = create_id<T, Made>([&] { return ptr; }))
{ {
id_manager::on_init<Made>::func(static_cast<Made*>(pair->second.get()), pair->second);
return pair->first; return pair->first;
} }
@ -381,7 +321,6 @@ public:
{ {
if (auto pair = create_id<T, Made>(std::forward<F>(provider))) if (auto pair = create_id<T, Made>(std::forward<F>(provider)))
{ {
id_manager::on_init<Made>::func(static_cast<Made*>(pair->second.get()), pair->second);
return pair->first; return pair->first;
} }
@ -572,7 +511,6 @@ public:
} }
} }
id_manager::on_stop<Get>::func(static_cast<Get*>(ptr.get()));
return true; return true;
} }
@ -594,7 +532,6 @@ public:
} }
} }
id_manager::on_stop<Get>::func(static_cast<Get*>(ptr.get()));
return {ptr, static_cast<Get*>(ptr.get())}; return {ptr, static_cast<Get*>(ptr.get())};
} }
@ -612,8 +549,6 @@ public:
{ {
func(*_ptr); func(*_ptr);
std::shared_ptr<void> ptr = std::move(found->second); std::shared_ptr<void> ptr = std::move(found->second);
lock.unlock();
id_manager::on_stop<Get>::func(static_cast<Get*>(ptr.get()));
return {ptr, static_cast<Get*>(ptr.get())}; return {ptr, static_cast<Get*>(ptr.get())};
} }
else else
@ -627,8 +562,6 @@ public:
} }
std::shared_ptr<void> ptr = std::move(found->second); std::shared_ptr<void> ptr = std::move(found->second);
lock.unlock();
id_manager::on_stop<Get>::func(static_cast<Get*>(ptr.get()));
return {{ptr, static_cast<Get*>(ptr.get())}, std::move(ret)}; return {{ptr, static_cast<Get*>(ptr.get())}, std::move(ret)};
} }
} }
@ -641,7 +574,7 @@ public:
class fxm class fxm
{ {
// Type Index -> Object. Use global since only one process is supported atm. // Type Index -> Object. Use global since only one process is supported atm.
static std::vector<std::pair<void(*)(void*), std::shared_ptr<void>>> g_vec; static std::vector<std::shared_ptr<void>> g_vec;
template <typename T> template <typename T>
static inline u32 get_type() static inline u32 get_type()
@ -664,14 +597,12 @@ public:
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
auto& pair = g_vec[get_type<T>()]; auto& cur = g_vec[get_type<T>()];
if (!pair.second) if (!cur)
{ {
ptr = std::make_shared<Make>(std::forward<Args>(args)...); ptr = std::make_shared<Make>(std::forward<Args>(args)...);
cur = ptr;
pair.first = id_manager::typeinfo::get_stop<T>();
pair.second = ptr;
} }
else else
{ {
@ -679,7 +610,6 @@ public:
} }
} }
id_manager::on_init<T>::func(ptr.get(), ptr);
return ptr; return ptr;
} }
@ -692,21 +622,13 @@ public:
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
auto& pair = g_vec[get_type<T>()]; auto& cur = g_vec[get_type<T>()];
ptr = std::make_shared<Make>(std::forward<Args>(args)...); ptr = std::make_shared<Make>(std::forward<Args>(args)...);
old = std::move(pair.second); old = std::move(cur);
cur = ptr;
pair.first = id_manager::typeinfo::get_stop<T>();
pair.second = ptr;
} }
if (old)
{
id_manager::on_stop<T>::func(static_cast<T*>(old.get()));
}
id_manager::on_init<T>::func(ptr.get(), ptr);
return ptr; return ptr;
} }
@ -718,16 +640,15 @@ public:
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
auto& pair = g_vec[get_type<T>()]; auto& cur = g_vec[get_type<T>()];
if (!pair.second) if (!cur)
{ {
ptr = provider(); ptr = provider();
if (ptr) if (ptr)
{ {
pair.first = id_manager::typeinfo::get_stop<T>(); cur = ptr;
pair.second = ptr;
} }
} }
@ -737,7 +658,6 @@ public:
} }
} }
id_manager::on_init<T>::func(ptr.get(), ptr);
return ptr; return ptr;
} }
@ -750,16 +670,14 @@ public:
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
auto& pair = g_vec[get_type<T>()]; auto& cur = g_vec[get_type<T>()];
ptr = provider(); ptr = provider();
if (ptr) if (ptr)
{ {
old = std::move(pair.second); old = std::move(cur);
cur = ptr;
pair.first = id_manager::typeinfo::get_stop<T>();
pair.second = ptr;
} }
else else
{ {
@ -767,12 +685,6 @@ public:
} }
} }
if (old)
{
id_manager::on_stop<T>::func(static_cast<T*>(old.get()));
}
id_manager::on_init<T>::func(ptr.get(), ptr);
return ptr; return ptr;
} }
@ -784,22 +696,19 @@ public:
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
auto& pair = g_vec[get_type<T>()]; auto& old = g_vec[get_type<T>()];
if (auto& old = pair.second) if (old)
{ {
return {old, static_cast<T*>(old.get())}; return {old, static_cast<T*>(old.get())};
} }
else else
{ {
ptr = std::make_shared<Make>(std::forward<Args>(args)...); ptr = std::make_shared<Make>(std::forward<Args>(args)...);
old = ptr;
pair.first = id_manager::typeinfo::get_stop<T>();
pair.second = ptr;
} }
} }
id_manager::on_init<T>::func(ptr.get(), ptr);
return ptr; return ptr;
} }
@ -807,7 +716,7 @@ public:
template <typename T> template <typename T>
static inline T* check_unlocked() static inline T* check_unlocked()
{ {
return static_cast<T*>(g_vec[get_type<T>()].second.get()); return static_cast<T*>(g_vec[get_type<T>()].get());
} }
// Check whether the object exists // Check whether the object exists
@ -825,7 +734,7 @@ public:
{ {
reader_lock lock(id_manager::g_mutex); reader_lock lock(id_manager::g_mutex);
auto& ptr = g_vec[get_type<T>()].second; auto& ptr = g_vec[get_type<T>()];
return {ptr, static_cast<T*>(ptr.get())}; return {ptr, static_cast<T*>(ptr.get())};
} }
@ -837,12 +746,7 @@ public:
std::shared_ptr<void> ptr; std::shared_ptr<void> ptr;
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
ptr = std::move(g_vec[get_type<T>()].second); ptr = std::move(g_vec[get_type<T>()]);
}
if (ptr)
{
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
} }
return ptr.operator bool(); return ptr.operator bool();
@ -855,12 +759,7 @@ public:
std::shared_ptr<void> ptr; std::shared_ptr<void> ptr;
{ {
std::lock_guard lock(id_manager::g_mutex); std::lock_guard lock(id_manager::g_mutex);
ptr = std::move(g_vec[get_type<T>()].second); ptr = std::move(g_vec[get_type<T>()]);
}
if (ptr)
{
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
} }
return {ptr, static_cast<T*>(ptr.get())}; return {ptr, static_cast<T*>(ptr.get())};

View File

@ -127,11 +127,6 @@ namespace vm
void cleanup_unlock(cpu_thread& cpu) noexcept void cleanup_unlock(cpu_thread& cpu) noexcept
{ {
if (g_tls_locked && cpu.get() == thread_ctrl::get_current())
{
g_tls_locked = nullptr;
}
for (u32 i = 0; i < g_locks.size(); i++) for (u32 i = 0; i < g_locks.size(); i++)
{ {
if (g_locks[i] == &cpu) if (g_locks[i] == &cpu)
@ -225,7 +220,7 @@ namespace vm
{ {
while (cpu_thread* ptr = lock) while (cpu_thread* ptr = lock)
{ {
if (ptr->state & (cpu_flag::dbg_global_stop + cpu_flag::exit)) if (ptr->is_stopped())
{ {
break; break;
} }
@ -533,11 +528,21 @@ namespace vm
} }
} }
const u32 page_addr = addr + (this->flags & 0x10 ? 0x1000 : 0);
const u32 page_size = size - (this->flags & 0x10 ? 0x2000 : 0);
if (this->flags & 0x10)
{
// Mark overflow/underflow guard pages as allocated
verify(HERE), !g_pages[addr / 4096].flags.exchange(page_allocated);
verify(HERE), !g_pages[addr / 4096 + size / 4096 - 1].flags.exchange(page_allocated);
}
// Map "real" memory pages // Map "real" memory pages
_page_map(addr, flags, size, shm.get()); _page_map(page_addr, flags, page_size, shm.get());
// Add entry // Add entry
m_map[addr] = std::move(shm); m_map[addr] = std::make_pair(size, std::move(shm));
return true; return true;
} }
@ -589,11 +594,11 @@ namespace vm
vm::writer_lock lock(0); vm::writer_lock lock(0);
// Deallocate all memory // Deallocate all memory
for (auto it = m_map.begin(), end = m_map.end(); it != end;) for (auto it = m_map.begin(), end = m_map.end(); !m_common && it != end;)
{ {
const auto next = std::next(it); const auto next = std::next(it);
const auto size = (next == end ? this->addr + this->size : next->first) - it->first; const auto size = it->second.first;
_page_unmap(it->first, size, it->second.get()); _page_unmap(it->first, size, it->second.second.get());
it = next; it = next;
} }
@ -614,7 +619,7 @@ namespace vm
const u32 min_page_size = flags & 0x100 ? 0x1000 : 0x10000; const u32 min_page_size = flags & 0x100 ? 0x1000 : 0x10000;
// Align to minimal page size // Align to minimal page size
const u32 size = ::align(orig_size, min_page_size); const u32 size = ::align(orig_size, min_page_size) + (flags & 0x10 ? 0x2000 : 0);
// Check alignment (it's page allocation, so passing small values there is just silly) // Check alignment (it's page allocation, so passing small values there is just silly)
if (align < min_page_size || align != (0x80000000u >> utils::cntlz32(align, true))) if (align < min_page_size || align != (0x80000000u >> utils::cntlz32(align, true)))
@ -623,7 +628,7 @@ namespace vm
} }
// Return if size is invalid // Return if size is invalid
if (!size || size > this->size) if (!orig_size || !size || size > this->size)
{ {
return 0; return 0;
} }
@ -654,7 +659,7 @@ namespace vm
{ {
if (try_alloc(addr, pflags, size, std::move(shm))) if (try_alloc(addr, pflags, size, std::move(shm)))
{ {
return addr; return addr + (flags & 0x10 ? 0x1000 : 0);
} }
} }
@ -672,7 +677,7 @@ namespace vm
const u32 size = ::align(orig_size, min_page_size); const u32 size = ::align(orig_size, min_page_size);
// return if addr or size is invalid // return if addr or size is invalid
if (!size || size > this->size || addr < this->addr || addr + size - 1 > this->addr + this->size - 1) if (!size || size > this->size || addr < this->addr || addr + size - 1 > this->addr + this->size - 1 || flags & 0x10)
{ {
return 0; return 0;
} }
@ -708,37 +713,42 @@ namespace vm
u32 block_t::dealloc(u32 addr, const std::shared_ptr<utils::shm>* src) u32 block_t::dealloc(u32 addr, const std::shared_ptr<utils::shm>* src)
{ {
u32 result = 0;
{ {
vm::writer_lock lock(0); vm::writer_lock lock(0);
const auto found = m_map.find(addr); const auto found = m_map.find(addr - (flags & 0x10 ? 0x1000 : 0));
if (found == m_map.end()) if (found == m_map.end())
{ {
return 0; return 0;
} }
if (src && found->second.get() != src->get()) if (src && found->second.second.get() != src->get())
{ {
return 0; return 0;
} }
// Approximate allocation size // Get allocation size
const auto next = std::next(found); const auto size = found->second.first - (flags & 0x10 ? 0x2000 : 0);
const auto size = (next == m_map.end() ? this->addr + this->size : next->first) - found->first;
if (flags & 0x10)
{
// Clear guard pages
verify(HERE), g_pages[addr / 4096 - 1].flags.exchange(0) == page_allocated;
verify(HERE), g_pages[addr / 4096 + size / 4096].flags.exchange(0) == page_allocated;
}
// Unmap "real" memory pages // Unmap "real" memory pages
result = _page_unmap(addr, size, found->second.get()); verify(HERE), size == _page_unmap(addr, size, found->second.second.get());
// Remove entry // Remove entry
m_map.erase(found); m_map.erase(found);
return size;
}
} }
return result; std::pair<u32, std::shared_ptr<utils::shm>> block_t::get(u32 addr, u32 size)
}
std::pair<const u32, std::shared_ptr<utils::shm>> block_t::get(u32 addr, u32 size)
{ {
if (addr < this->addr || std::max<u32>(size, addr - this->addr + size) >= this->size) if (addr < this->addr || std::max<u32>(size, addr - this->addr + size) >= this->size)
{ {
@ -769,12 +779,12 @@ namespace vm
} }
// Range check // Range check
if (std::max<u32>(size, addr - found->first + size) > found->second->size()) if (std::max<u32>(size, addr - found->first + size) > found->second.second->size())
{ {
return {addr, nullptr}; return {addr, nullptr};
} }
return *found; return {found->first, found->second.second};
} }
u32 block_t::imp_used(const vm::writer_lock&) u32 block_t::imp_used(const vm::writer_lock&)
@ -783,7 +793,7 @@ namespace vm
for (auto& entry : m_map) for (auto& entry : m_map)
{ {
result += entry.second->size(); result += entry.second.first - (flags & 0x10 ? 0x2000 : 0);
} }
return result; return result;
@ -967,7 +977,7 @@ namespace vm
std::make_shared<block_t>(0x20000000, 0x10000000, 0x201), // user 64k pages std::make_shared<block_t>(0x20000000, 0x10000000, 0x201), // user 64k pages
nullptr, // user 1m pages nullptr, // user 1m pages
std::make_shared<block_t>(0xC0000000, 0x10000000), // video std::make_shared<block_t>(0xC0000000, 0x10000000), // video
std::make_shared<block_t>(0xD0000000, 0x10000000, 0x101), // stack std::make_shared<block_t>(0xD0000000, 0x10000000, 0x111), // stack
std::make_shared<block_t>(0xE0000000, 0x20000000), // SPU reserved std::make_shared<block_t>(0xE0000000, 0x20000000), // SPU reserved
}; };
} }

View File

@ -146,7 +146,7 @@ namespace vm
class block_t final class block_t final
{ {
// Mapped regions: addr -> shm handle // Mapped regions: addr -> shm handle
std::map<u32, std::shared_ptr<utils::shm>> m_map; std::map<u32, std::pair<u32, std::shared_ptr<utils::shm>>> m_map;
// Common mapped region for special cases // Common mapped region for special cases
std::shared_ptr<utils::shm> m_common; std::shared_ptr<utils::shm> m_common;
@ -173,7 +173,7 @@ namespace vm
u32 dealloc(u32 addr, const std::shared_ptr<utils::shm>* = nullptr); u32 dealloc(u32 addr, const std::shared_ptr<utils::shm>* = nullptr);
// Get memory at specified address (if size = 0, addr assumed exact) // Get memory at specified address (if size = 0, addr assumed exact)
std::pair<const u32, std::shared_ptr<utils::shm>> get(u32 addr, u32 size = 0); std::pair<u32, std::shared_ptr<utils::shm>> get(u32 addr, u32 size = 0);
// Internal // Internal
u32 imp_used(const vm::writer_lock&); u32 imp_used(const vm::writer_lock&);

View File

@ -7,6 +7,7 @@
#include "Emu/RSX/GSRender.h" #include "Emu/RSX/GSRender.h"
#include <map> #include <map>
#include <exception>
namespace rsx namespace rsx
{ {
@ -203,7 +204,7 @@ namespace rsx
} }
} }
void rsx_replay_thread::cpu_task() void rsx_replay_thread::on_task()
{ {
be_t<u32> context_id = allocate_context(); be_t<u32> context_id = allocate_context();
@ -284,7 +285,18 @@ namespace rsx
// random pause to not destroy gpu // random pause to not destroy gpu
std::this_thread::sleep_for(10ms); std::this_thread::sleep_for(10ms);
} }
}
state += cpu_flag::exit; void rsx_replay_thread::operator()()
{
try
{
on_task();
}
catch (const std::exception& e)
{
LOG_FATAL(RSX, "%s thrown: %s", typeid(e).name(), e.what());
Emu.Pause();
}
} }
} }

View File

@ -210,7 +210,7 @@ namespace rsx
}; };
class rsx_replay_thread : public ppu_thread class rsx_replay_thread
{ {
struct rsx_context struct rsx_context
{ {
@ -236,9 +236,12 @@ namespace rsx
public: public:
rsx_replay_thread(std::unique_ptr<frame_capture_data>&& frame_data) rsx_replay_thread(std::unique_ptr<frame_capture_data>&& frame_data)
: ppu_thread("Rsx Capture Replay Thread"), frame(std::move(frame_data)) {}; :frame(std::move(frame_data))
{
}
virtual void cpu_task() override; void on_task();
void operator()();
private: private:
be_t<u32> allocate_context(); be_t<u32> allocate_context();
std::vector<u32> alloc_write_fifo(be_t<u32> context_id); std::vector<u32> alloc_write_fifo(be_t<u32> context_id);

View File

@ -134,6 +134,11 @@ namespace
} }
} }
u64 D3D12GSRender::get_cycles()
{
return thread_ctrl::get_cycles(static_cast<named_thread<D3D12GSRender>&>(*this));
}
D3D12GSRender::D3D12GSRender() D3D12GSRender::D3D12GSRender()
: GSRender() : GSRender()
, m_d3d12_lib() , m_d3d12_lib()

View File

@ -121,6 +121,7 @@ private:
ComPtr<ID3D12DescriptorHeap> m_current_sampler_descriptors; ComPtr<ID3D12DescriptorHeap> m_current_sampler_descriptors;
public: public:
u64 get_cycles() override final;
D3D12GSRender(); D3D12GSRender();
virtual ~D3D12GSRender(); virtual ~D3D12GSRender();

View File

@ -22,6 +22,11 @@ namespace
} }
} }
u64 GLGSRender::get_cycles()
{
return thread_ctrl::get_cycles(static_cast<named_thread<GLGSRender>&>(*this));
}
GLGSRender::GLGSRender() : GSRender() GLGSRender::GLGSRender() : GSRender()
{ {
m_shaders_cache.reset(new gl::shader_cache(m_prog_buffer, "opengl", "v1.6")); m_shaders_cache.reset(new gl::shader_cache(m_prog_buffer, "opengl", "v1.6"));

View File

@ -346,6 +346,7 @@ private:
std::vector<u8> m_scratch_buffer; std::vector<u8> m_scratch_buffer;
public: public:
u64 get_cycles() override final;
GLGSRender(); GLGSRender();
private: private:

View File

@ -2,6 +2,11 @@
#include "NullGSRender.h" #include "NullGSRender.h"
#include "Emu/System.h" #include "Emu/System.h"
u64 NullGSRender::get_cycles()
{
return thread_ctrl::get_cycles(static_cast<named_thread<NullGSRender>&>(*this));
}
NullGSRender::NullGSRender() : GSRender() NullGSRender::NullGSRender() : GSRender()
{ {
} }

View File

@ -1,11 +1,12 @@
#pragma once #pragma once
#include "Emu/RSX/GSRender.h" #include "Emu/RSX/GSRender.h"
class NullGSRender final : public GSRender class NullGSRender : public GSRender
{ {
public: public:
u64 get_cycles() override final;
NullGSRender(); NullGSRender();
private: private:
bool do_method(u32 cmd, u32 value) override; bool do_method(u32 cmd, u32 value) override final;
}; };

View File

@ -230,7 +230,6 @@ namespace rsx
u32 ppus{0}; u32 ppus{0};
u32 spus{0}; u32 spus{0};
u32 rawspus{0};
f32 cpu_usage{-1.f}; f32 cpu_usage{-1.f};
u32 total_threads{0}; u32 total_threads{0};
@ -260,16 +259,20 @@ namespace rsx
} }
case detail_level::medium: case detail_level::medium:
{ {
ppus = idm::select<ppu_thread>([&ppu_cycles](u32, ppu_thread& ppu) { ppu_cycles += ppu.get()->get_cycles(); }); ppus = idm::select<named_thread<ppu_thread>>([&ppu_cycles](u32, named_thread<ppu_thread>& ppu)
{
ppu_cycles += thread_ctrl::get_cycles(ppu);
});
spus = idm::select<SPUThread>([&spu_cycles](u32, SPUThread& spu) { spu_cycles += spu.get()->get_cycles(); }); spus = idm::select<named_thread<spu_thread>>([&spu_cycles](u32, named_thread<spu_thread>& spu)
{
rawspus = idm::select<RawSPUThread>([&spu_cycles](u32, RawSPUThread& rawspu) { spu_cycles += rawspu.get()->get_cycles(); }); spu_cycles += thread_ctrl::get_cycles(spu);
});
if (!rsx_thread) if (!rsx_thread)
rsx_thread = fxm::get<GSRender>(); rsx_thread = fxm::get<GSRender>();
rsx_cycles += rsx_thread->get()->get_cycles(); rsx_cycles += rsx_thread->get_cycles();
total_cycles = ppu_cycles + spu_cycles + rsx_cycles; total_cycles = ppu_cycles + spu_cycles + rsx_cycles;
cpu_usage = m_cpu_stats.get_usage(); cpu_usage = m_cpu_stats.get_usage();
@ -329,7 +332,7 @@ namespace rsx
" Total : %04.1f %% (%2u)\n\n" " Total : %04.1f %% (%2u)\n\n"
"%s\n" "%s\n"
" RSX : %02u %%", " RSX : %02u %%",
fps, frametime, std::string(title1_high.size(), ' '), ppu_usage, ppus, spu_usage, spus + rawspus, rsx_usage, cpu_usage, total_threads, std::string(title2.size(), ' '), rsx_load); fps, frametime, std::string(title1_high.size(), ' '), ppu_usage, ppus, spu_usage, spus, rsx_usage, cpu_usage, total_threads, std::string(title2.size(), ' '), rsx_load);
break; break;
} }
} }

View File

@ -977,13 +977,13 @@ namespace rsx
this->on_close = on_close; this->on_close = on_close;
if (interactive) if (interactive)
{ {
thread_ctrl::make_shared("dialog input thread", [&] thread_ctrl::spawn("dialog input thread", [&]
{ {
if (auto error = run_input_loop()) if (auto error = run_input_loop())
{ {
LOG_ERROR(RSX, "Dialog input loop exited with error code=%d", error); LOG_ERROR(RSX, "Dialog input loop exited with error code=%d", error);
} }
})->detach(); });
} }
return CELL_OK; return CELL_OK;

View File

@ -23,6 +23,7 @@
#include <sstream> #include <sstream>
#include <thread> #include <thread>
#include <unordered_set> #include <unordered_set>
#include <exception>
#include <fenv.h> #include <fenv.h>
class GSRender; class GSRender;
@ -367,13 +368,36 @@ namespace rsx
} }
} }
void thread::on_spawn() void thread::operator()()
{ {
m_rsx_thread = std::this_thread::get_id(); try
{
// Wait for startup (TODO)
while (m_rsx_thread_exiting)
{
thread_ctrl::wait_for(1000);
if (Emu.IsStopped())
{
return;
}
}
on_task();
}
catch (const std::exception& e)
{
LOG_FATAL(RSX, "%s thrown: %s", typeid(e).name(), e.what());
Emu.Pause();
}
on_exit();
} }
void thread::on_task() void thread::on_task()
{ {
m_rsx_thread = std::this_thread::get_id();
if (supports_native_ui) if (supports_native_ui)
{ {
m_overlay_manager = fxm::make_always<rsx::overlays::display_manager>(); m_overlay_manager = fxm::make_always<rsx::overlays::display_manager>();
@ -406,7 +430,7 @@ namespace rsx
last_flip_time = get_system_time() - 1000000; last_flip_time = get_system_time() - 1000000;
thread_ctrl::spawn(m_vblank_thread, "VBlank Thread", [this]() named_thread vblank_thread("VBlank Thread", [this]()
{ {
const u64 start_time = get_system_time(); const u64 start_time = get_system_time();
@ -428,7 +452,7 @@ namespace rsx
{ ppu_cmd::sleep, 0 } { ppu_cmd::sleep, 0 }
}); });
intr_thread->notify(); thread_ctrl::notify(*intr_thread);
} }
continue; continue;
@ -441,7 +465,7 @@ namespace rsx
} }
}); });
thread_ctrl::spawn(m_decompiler_thread, "RSX Decompiler Thread", [this] named_thread decompiler_thread ("RSX Decompiler Thread", [this]
{ {
if (g_cfg.video.disable_asynchronous_shader_compiler) if (g_cfg.video.disable_asynchronous_shader_compiler)
{ {
@ -1000,22 +1024,6 @@ namespace rsx
void thread::on_exit() void thread::on_exit()
{ {
m_rsx_thread_exiting = true; m_rsx_thread_exiting = true;
if (m_vblank_thread)
{
m_vblank_thread->join();
m_vblank_thread.reset();
}
if (m_decompiler_thread)
{
m_decompiler_thread->join();
m_decompiler_thread.reset();
}
}
std::string thread::get_name() const
{
return "rsx::thread";
} }
void thread::fill_scale_offset_data(void *buffer, bool flip_y) const void thread::fill_scale_offset_data(void *buffer, bool flip_y) const
@ -2179,10 +2187,8 @@ namespace rsx
memset(display_buffers, 0, sizeof(display_buffers)); memset(display_buffers, 0, sizeof(display_buffers));
m_rsx_thread_exiting = false;
on_init_rsx(); on_init_rsx();
start_thread(fxm::get<GSRender>()); m_rsx_thread_exiting = false;
} }
GcmTileInfo *thread::find_tile(u32 offset, u32 location) GcmTileInfo *thread::find_tile(u32 offset, u32 location)
@ -2908,7 +2914,7 @@ namespace rsx
{ ppu_cmd::sleep, 0 } { ppu_cmd::sleep, 0 }
}); });
intr_thread->notify(); thread_ctrl::notify(*intr_thread);
} }
sys_rsx_context_attribute(0x55555555, 0xFEC, buffer, 0, 0, 0); sys_rsx_context_attribute(0x55555555, 0xFEC, buffer, 0, 0, 0);

View File

@ -361,11 +361,8 @@ namespace rsx
struct sampled_image_descriptor_base; struct sampled_image_descriptor_base;
class thread : public old_thread class thread
{ {
std::shared_ptr<thread_base> m_vblank_thread;
std::shared_ptr<thread_base> m_decompiler_thread;
u64 timestamp_ctrl = 0; u64 timestamp_ctrl = 0;
u64 timestamp_subvalue = 0; u64 timestamp_subvalue = 0;
@ -434,7 +431,7 @@ namespace rsx
void capture_frame(const std::string &name); void capture_frame(const std::string &name);
public: public:
std::shared_ptr<class ppu_thread> intr_thread; std::shared_ptr<named_thread<class ppu_thread>> intr_thread;
// I hate this flag, but until hle is closer to lle, its needed // I hate this flag, but until hle is closer to lle, its needed
bool isHLE{ false }; bool isHLE{ false };
@ -516,13 +513,14 @@ namespace rsx
bool zcull_rendering_enabled = false; bool zcull_rendering_enabled = false;
bool zcull_pixel_cnt_enabled = false; bool zcull_pixel_cnt_enabled = false;
void operator()();
virtual u64 get_cycles() = 0;
protected: protected:
thread(); thread();
virtual ~thread(); virtual ~thread();
virtual void on_task();
virtual void on_spawn() override; virtual void on_exit();
virtual void on_task() override;
virtual void on_exit() override;
/** /**
* Execute a backend local task queue * Execute a backend local task queue
@ -534,11 +532,6 @@ namespace rsx
virtual bool on_decompiler_task() { return false; } virtual bool on_decompiler_task() { return false; }
public: public:
virtual std::string get_name() const override;
virtual void on_init(const std::shared_ptr<void>&) override {} // disable start() (TODO)
virtual void on_stop() override {} // disable join()
virtual void begin(); virtual void begin();
virtual void end(); virtual void end();

View File

@ -502,6 +502,11 @@ namespace
} }
} }
u64 VKGSRender::get_cycles()
{
return thread_ctrl::get_cycles(static_cast<named_thread<VKGSRender>&>(*this));
}
VKGSRender::VKGSRender() : GSRender() VKGSRender::VKGSRender() : GSRender()
{ {
u32 instance_handle = m_thread_context.createInstance("RPCS3"); u32 instance_handle = m_thread_context.createInstance("RPCS3");

View File

@ -377,6 +377,7 @@ private:
#endif #endif
public: public:
u64 get_cycles() override final;
VKGSRender(); VKGSRender();
~VKGSRender(); ~VKGSRender();

View File

@ -1133,7 +1133,7 @@ namespace rsx
{ ppu_cmd::sleep, 0 } { ppu_cmd::sleep, 0 }
}); });
rsx->intr_thread->notify(); thread_ctrl::notify(*rsx->intr_thread);
} }
} }

View File

@ -486,9 +486,7 @@ bool Emulator::BootRsxCapture(const std::string& path)
GetCallbacks().on_run(); GetCallbacks().on_run();
m_state = system_state::running; m_state = system_state::running;
auto&& rsxcapture = idm::make_ptr<ppu_thread, rsx::rsx_replay_thread>(std::move(frame)); fxm::make<rsx::rsx_replay_thread>(std::move(frame));
rsxcapture->run();
return true; return true;
} }
@ -736,7 +734,7 @@ void Emulator::Load(bool add_only)
// Workaround for analyser glitches // Workaround for analyser glitches
vm::falloc(0x10000, 0xf0000, vm::main); vm::falloc(0x10000, 0xf0000, vm::main);
return thread_ctrl::make_shared("SPRX Loader", [this] return thread_ctrl::spawn("SPRX Loader", [this]
{ {
std::vector<std::string> dir_queue; std::vector<std::string> dir_queue;
dir_queue.emplace_back(m_path + '/'); dir_queue.emplace_back(m_path + '/');
@ -744,7 +742,7 @@ void Emulator::Load(bool add_only)
std::vector<std::pair<std::string, u64>> file_queue; std::vector<std::pair<std::string, u64>> file_queue;
file_queue.reserve(2000); file_queue.reserve(2000);
std::queue<std::shared_ptr<thread_base>> thread_queue; std::queue<named_thread<std::function<void()>>> thread_queue;
const uint max_threads = std::thread::hardware_concurrency(); const uint max_threads = std::thread::hardware_concurrency();
// Initialize progress dialog // Initialize progress dialog
@ -820,12 +818,12 @@ void Emulator::Load(bool add_only)
std::this_thread::sleep_for(10ms); std::this_thread::sleep_for(10ms);
} }
thread_queue.emplace(thread_ctrl::make_shared("Worker " + std::to_string(thread_queue.size()), [_prx = std::move(prx)] thread_queue.emplace("Worker " + std::to_string(thread_queue.size()), [_prx = std::move(prx)]
{ {
ppu_initialize(*_prx); ppu_initialize(*_prx);
ppu_unload_prx(*_prx); ppu_unload_prx(*_prx);
g_progr_fdone++; g_progr_fdone++;
})); });
continue; continue;
} }
@ -846,7 +844,7 @@ void Emulator::Load(bool add_only)
{ {
Emu.Stop(); Emu.Stop();
}); });
})->detach(); });
} }
// Detect boot location // Detect boot location
@ -1237,12 +1235,12 @@ void Emulator::Run()
auto on_select = [](u32, cpu_thread& cpu) auto on_select = [](u32, cpu_thread& cpu)
{ {
cpu.run(); cpu.state -= cpu_flag::stop;
cpu.notify();
}; };
idm::select<ppu_thread>(on_select); idm::select<named_thread<ppu_thread>>(on_select);
idm::select<RawSPUThread>(on_select); idm::select<named_thread<spu_thread>>(on_select);
idm::select<SPUThread>(on_select);
#ifdef WITH_GDB_DEBUGGER #ifdef WITH_GDB_DEBUGGER
// Initialize debug server at the end of emu run sequence // Initialize debug server at the end of emu run sequence
@ -1273,9 +1271,8 @@ bool Emulator::Pause()
cpu.state += cpu_flag::dbg_global_pause; cpu.state += cpu_flag::dbg_global_pause;
}; };
idm::select<ppu_thread>(on_select); idm::select<named_thread<ppu_thread>>(on_select);
idm::select<RawSPUThread>(on_select); idm::select<named_thread<spu_thread>>(on_select);
idm::select<SPUThread>(on_select);
return true; return true;
} }
@ -1338,9 +1335,8 @@ void Emulator::Resume()
cpu.notify(); cpu.notify();
}; };
idm::select<ppu_thread>(on_select); idm::select<named_thread<ppu_thread>>(on_select);
idm::select<RawSPUThread>(on_select); idm::select<named_thread<spu_thread>>(on_select);
idm::select<SPUThread>(on_select);
GetCallbacks().on_resume(); GetCallbacks().on_resume();
} }
@ -1369,23 +1365,14 @@ void Emulator::Stop(bool restart)
fxm::remove<GDBDebugServer>(); fxm::remove<GDBDebugServer>();
#endif #endif
auto e_stop = std::make_exception_ptr(cpu_flag::dbg_global_stop);
auto on_select = [&](u32, cpu_thread& cpu) auto on_select = [&](u32, cpu_thread& cpu)
{ {
cpu.state += cpu_flag::dbg_global_stop; cpu.state += cpu_flag::dbg_global_stop;
cpu.notify();
// Can't normally be null.
// Hack for a possible vm deadlock on thread creation.
if (auto thread = cpu.get())
{
thread->set_exception(e_stop);
}
}; };
idm::select<ppu_thread>(on_select); idm::select<named_thread<ppu_thread>>(on_select);
idm::select<RawSPUThread>(on_select); idm::select<named_thread<spu_thread>>(on_select);
idm::select<SPUThread>(on_select);
LOG_NOTICE(GENERAL, "All threads signaled..."); LOG_NOTICE(GENERAL, "All threads signaled...");

View File

@ -234,13 +234,13 @@ void rpcs3_app::InitializeCallbacks()
{ {
switch (video_renderer type = g_cfg.video.renderer) switch (video_renderer type = g_cfg.video.renderer)
{ {
case video_renderer::null: return std::make_shared<NullGSRender>(); case video_renderer::null: return std::make_shared<named_thread<NullGSRender>>("rsx::thread");
case video_renderer::opengl: return std::make_shared<GLGSRender>(); case video_renderer::opengl: return std::make_shared<named_thread<GLGSRender>>("rsx::thread");
#if defined(_WIN32) || defined(HAVE_VULKAN) #if defined(_WIN32) || defined(HAVE_VULKAN)
case video_renderer::vulkan: return std::make_shared<VKGSRender>(); case video_renderer::vulkan: return std::make_shared<named_thread<VKGSRender>>("rsx::thread");
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
case video_renderer::dx12: return std::make_shared<D3D12GSRender>(); case video_renderer::dx12: return std::make_shared<named_thread<D3D12GSRender>>("rsx::thread");
#endif #endif
default: fmt::throw_exception("Invalid video renderer: %s" HERE, type); default: fmt::throw_exception("Invalid video renderer: %s" HERE, type);
} }

View File

@ -60,7 +60,7 @@ void breakpoint_list::AddBreakpoint(u32 pc)
m_breakpoint_handler->AddBreakpoint(pc); m_breakpoint_handler->AddBreakpoint(pc);
const auto cpu = this->cpu.lock(); const auto cpu = this->cpu.lock();
const u32 cpu_offset = cpu->id_type() != 1 ? static_cast<SPUThread&>(*cpu).offset : 0; const u32 cpu_offset = cpu->id_type() != 1 ? static_cast<spu_thread&>(*cpu).offset : 0;
m_disasm->offset = (u8*)vm::base(cpu_offset); m_disasm->offset = (u8*)vm::base(cpu_offset);
m_disasm->disasm(m_disasm->dump_pc = pc); m_disasm->disasm(m_disasm->dump_pc = pc);

View File

@ -258,7 +258,7 @@ u32 debugger_frame::GetPc() const
return 0; return 0;
} }
return cpu->id_type() == 1 ? static_cast<ppu_thread*>(cpu.get())->cia : static_cast<SPUThread*>(cpu.get())->pc; return cpu->id_type() == 1 ? static_cast<ppu_thread*>(cpu.get())->cia : static_cast<spu_thread*>(cpu.get())->pc;
} }
void debugger_frame::UpdateUI() void debugger_frame::UpdateUI()
@ -340,9 +340,8 @@ void debugger_frame::UpdateUnitList()
{ {
const QSignalBlocker blocker(m_choice_units); const QSignalBlocker blocker(m_choice_units);
idm::select<ppu_thread>(on_select); idm::select<named_thread<ppu_thread>>(on_select);
idm::select<RawSPUThread>(on_select); idm::select<named_thread<spu_thread>>(on_select);
idm::select<SPUThread>(on_select);
} }
OnSelectUnit(); OnSelectUnit();
@ -369,21 +368,16 @@ void debugger_frame::OnSelectUnit()
return data == &cpu; return data == &cpu;
}; };
if (auto ppu = idm::select<ppu_thread>(on_select)) if (auto ppu = idm::select<named_thread<ppu_thread>>(on_select))
{ {
m_disasm = std::make_unique<PPUDisAsm>(CPUDisAsm_InterpreterMode); m_disasm = std::make_unique<PPUDisAsm>(CPUDisAsm_InterpreterMode);
cpu = ppu.ptr; cpu = ppu.ptr;
} }
else if (auto spu1 = idm::select<SPUThread>(on_select)) else if (auto spu1 = idm::select<named_thread<spu_thread>>(on_select))
{ {
m_disasm = std::make_unique<SPUDisAsm>(CPUDisAsm_InterpreterMode); m_disasm = std::make_unique<SPUDisAsm>(CPUDisAsm_InterpreterMode);
cpu = spu1.ptr; cpu = spu1.ptr;
} }
else if (auto rspu = idm::select<RawSPUThread>(on_select))
{
m_disasm = std::make_unique<SPUDisAsm>(CPUDisAsm_InterpreterMode);
cpu = rspu.ptr;
}
} }
m_debugger_list->UpdateCPUData(this->cpu, m_disasm); m_debugger_list->UpdateCPUData(this->cpu, m_disasm);
@ -540,7 +534,7 @@ u64 debugger_frame::EvaluateExpression(const QString& expression)
} }
else else
{ {
auto spu = static_cast<SPUThread*>(thread.get()); auto spu = static_cast<spu_thread*>(thread.get());
for (int i = 0; i < 128; ++i) for (int i = 0; i < 128; ++i)
{ {

View File

@ -37,7 +37,7 @@ u32 debugger_list::GetPc() const
return 0; return 0;
} }
return cpu->id_type() == 1 ? static_cast<ppu_thread*>(cpu.get())->cia : static_cast<SPUThread*>(cpu.get())->pc; return cpu->id_type() == 1 ? static_cast<ppu_thread*>(cpu.get())->cia : static_cast<spu_thread*>(cpu.get())->pc;
} }
u32 debugger_list::GetCenteredAddress(u32 address) const u32 debugger_list::GetCenteredAddress(u32 address) const
@ -73,7 +73,7 @@ void debugger_list::ShowAddress(u32 addr)
else else
{ {
const bool is_spu = cpu->id_type() != 1; const bool is_spu = cpu->id_type() != 1;
const u32 cpu_offset = is_spu ? static_cast<SPUThread&>(*cpu).offset : 0; const u32 cpu_offset = is_spu ? static_cast<spu_thread&>(*cpu).offset : 0;
const u32 address_limits = is_spu ? 0x3ffff : ~0; const u32 address_limits = is_spu ? 0x3ffff : ~0;
m_pc &= address_limits; m_pc &= address_limits;
m_disasm->offset = (u8*)vm::base(cpu_offset); m_disasm->offset = (u8*)vm::base(cpu_offset);
@ -90,7 +90,7 @@ void debugger_list::ShowAddress(u32 addr)
item(i)->setText((IsBreakpoint(m_pc) ? ">>> " : " ") + qstr(m_disasm->last_opcode)); item(i)->setText((IsBreakpoint(m_pc) ? ">>> " : " ") + qstr(m_disasm->last_opcode));
if (cpu->state & cpu_state_pause && m_pc == GetPc()) if (cpu->is_paused() && m_pc == GetPc())
{ {
item(i)->setTextColor(m_text_color_pc); item(i)->setTextColor(m_text_color_pc);
item(i)->setBackgroundColor(m_color_pc); item(i)->setBackgroundColor(m_color_pc);

View File

@ -17,7 +17,7 @@ instruction_editor_dialog::instruction_editor_dialog(QWidget *parent, u32 _pc, c
setMinimumSize(300, sizeHint().height()); setMinimumSize(300, sizeHint().height());
const auto cpu = _cpu.get(); const auto cpu = _cpu.get();
m_cpu_offset = cpu->id_type() != 1 ? static_cast<SPUThread&>(*cpu).offset : 0; m_cpu_offset = cpu->id_type() != 1 ? static_cast<spu_thread&>(*cpu).offset : 0;
QString instruction = qstr(fmt::format("%08x", vm::read32(m_cpu_offset + m_pc).value())); QString instruction = qstr(fmt::format("%08x", vm::read32(m_cpu_offset + m_pc).value()));
QVBoxLayout* vbox_panel(new QVBoxLayout()); QVBoxLayout* vbox_panel(new QVBoxLayout());

View File

@ -261,18 +261,18 @@ void kernel_explorer::Update()
lv2_types.emplace_back(l_addTreeChild(root, "PPU Threads")); lv2_types.emplace_back(l_addTreeChild(root, "PPU Threads"));
idm::select<ppu_thread>([&](u32 id, ppu_thread& ppu) idm::select<named_thread<ppu_thread>>([&](u32 id, ppu_thread& ppu)
{ {
lv2_types.back().count++; lv2_types.back().count++;
l_addTreeChild(lv2_types.back().node, qstr(fmt::format("PPU Thread: ID = 0x%08x '%s'", id, ppu.get_name()))); l_addTreeChild(lv2_types.back().node, qstr(fmt::format("PPU Thread: ID = 0x%08x '%s'", id, ppu.ppu_name.get())));
}); });
lv2_types.emplace_back(l_addTreeChild(root, "SPU Threads")); lv2_types.emplace_back(l_addTreeChild(root, "SPU Threads"));
idm::select<SPUThread>([&](u32 id, SPUThread& spu) idm::select<named_thread<spu_thread>>([&](u32 id, spu_thread& spu)
{ {
lv2_types.back().count++; lv2_types.back().count++;
l_addTreeChild(lv2_types.back().node, qstr(fmt::format("SPU Thread: ID = 0x%08x '%s'", id, spu.get_name()))); l_addTreeChild(lv2_types.back().node, qstr(fmt::format("SPU Thread: ID = 0x%08x '%s'", id, spu.spu_name.get())));
}); });
lv2_types.emplace_back(l_addTreeChild(root, "SPU Thread Groups")); lv2_types.emplace_back(l_addTreeChild(root, "SPU Thread Groups"));

View File

@ -111,7 +111,7 @@ void register_editor_dialog::updateRegister(const QString& text)
} }
else else
{ {
auto& spu = *static_cast<SPUThread*>(cpu.get()); auto& spu = *static_cast<spu_thread*>(cpu.get());
std::string::size_type first_brk = reg.find('['); std::string::size_type first_brk = reg.find('[');
if (first_brk != std::string::npos) if (first_brk != std::string::npos)
@ -179,7 +179,7 @@ void register_editor_dialog::OnOkay(const std::shared_ptr<cpu_thread>& _cpu)
} }
else else
{ {
auto& spu = *static_cast<SPUThread*>(cpu); auto& spu = *static_cast<spu_thread*>(cpu);
while (value.length() < 32) value = "0" + value; while (value.length() < 32) value = "0" + value;
const auto first_brk = reg.find('['); const auto first_brk = reg.find('[');