2020-12-05 13:08:24 +01:00
|
|
|
#pragma once
|
2015-07-09 02:33:15 +02:00
|
|
|
|
2016-06-07 22:24:20 +02:00
|
|
|
#include "../CPU/CPUThread.h"
|
2024-08-04 04:09:06 +02:00
|
|
|
#include "../CPU/Hypervisor.h"
|
2018-09-25 22:34:45 +02:00
|
|
|
#include "../Memory/vm_ptr.h"
|
2025-04-24 12:41:04 +02:00
|
|
|
#include "rx/cpu/cell/ppu/PPUContext.hpp"
|
2025-04-08 18:46:57 +02:00
|
|
|
#include "util/lockless.h"
|
|
|
|
|
#include "util/BitField.h"
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2020-12-22 09:42:57 +01:00
|
|
|
#include "util/logs.hpp"
|
2020-12-13 14:34:45 +01:00
|
|
|
#include "util/v128.hpp"
|
|
|
|
|
|
2020-02-01 09:31:27 +01:00
|
|
|
LOG_CHANNEL(ppu_log, "PPU");
|
|
|
|
|
|
2016-08-09 16:14:41 +02:00
|
|
|
enum class ppu_cmd : u32
|
2016-07-27 23:43:22 +02:00
|
|
|
{
|
2016-08-09 16:14:41 +02:00
|
|
|
null,
|
2016-07-27 23:43:22 +02:00
|
|
|
|
2025-04-05 21:50:45 +02:00
|
|
|
opcode, // Execute PPU instruction from arg
|
|
|
|
|
set_gpr, // Set gpr[arg] (+1 cmd)
|
|
|
|
|
set_args, // Set general-purpose args (+arg cmd)
|
|
|
|
|
lle_call, // Load addr and rtoc at *arg or *gpr[arg] and execute
|
|
|
|
|
hle_call, // Execute function by index (arg)
|
|
|
|
|
ptr_call, // Execute function by pointer
|
|
|
|
|
opd_call, // Execute function by provided rtoc and address (unlike lle_call, does not read memory)
|
|
|
|
|
cia_call, // Execute from current CIA, mo GPR modification applied
|
2023-06-07 13:34:39 +02:00
|
|
|
entry_call, // Load addr and rtoc from entry_func
|
2017-01-22 20:03:57 +01:00
|
|
|
initialize, // ppu_initialize()
|
2017-02-06 19:36:46 +01:00
|
|
|
sleep,
|
2018-03-06 03:36:33 +01:00
|
|
|
reset_stack, // resets stack address
|
2016-07-27 23:43:22 +02:00
|
|
|
};
|
|
|
|
|
|
2020-03-03 21:39:40 +01:00
|
|
|
enum class ppu_join_status : u32
|
|
|
|
|
{
|
|
|
|
|
joinable = 0,
|
|
|
|
|
detached = 1,
|
|
|
|
|
zombie = 2,
|
|
|
|
|
exited = 3,
|
|
|
|
|
max = 4, // Values above it indicate PPU id of joining thread
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-21 07:48:37 +02:00
|
|
|
enum ppu_thread_status : u32
|
|
|
|
|
{
|
|
|
|
|
PPU_THREAD_STATUS_IDLE,
|
|
|
|
|
PPU_THREAD_STATUS_RUNNABLE,
|
|
|
|
|
PPU_THREAD_STATUS_ONPROC,
|
|
|
|
|
PPU_THREAD_STATUS_SLEEP,
|
|
|
|
|
PPU_THREAD_STATUS_STOP,
|
|
|
|
|
PPU_THREAD_STATUS_ZOMBIE,
|
|
|
|
|
PPU_THREAD_STATUS_DELETED,
|
|
|
|
|
PPU_THREAD_STATUS_UNKNOWN,
|
|
|
|
|
};
|
|
|
|
|
|
2017-06-26 00:44:05 +02:00
|
|
|
// Formatting helper
|
|
|
|
|
enum class ppu_syscall_code : u64
|
|
|
|
|
{
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-03 05:18:14 +02:00
|
|
|
enum : u32
|
|
|
|
|
{
|
|
|
|
|
ppu_stack_start_offset = 0x70,
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-08 13:26:31 +02:00
|
|
|
// ppu function descriptor
|
|
|
|
|
struct ppu_func_opd_t
|
|
|
|
|
{
|
|
|
|
|
be_t<u32> addr;
|
|
|
|
|
be_t<u32> rtoc;
|
|
|
|
|
};
|
|
|
|
|
|
2018-10-11 00:17:19 +02:00
|
|
|
// ppu_thread constructor argument
|
|
|
|
|
struct ppu_thread_params
|
|
|
|
|
{
|
|
|
|
|
vm::addr_t stack_addr;
|
|
|
|
|
u32 stack_size;
|
|
|
|
|
u32 tls_addr;
|
2020-04-08 13:26:31 +02:00
|
|
|
ppu_func_opd_t entry;
|
2018-10-11 00:17:19 +02:00
|
|
|
u64 arg0;
|
|
|
|
|
u64 arg1;
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-12 11:12:39 +01:00
|
|
|
struct cmd64
|
|
|
|
|
{
|
|
|
|
|
u64 m_data = 0;
|
|
|
|
|
|
|
|
|
|
constexpr cmd64() noexcept = default;
|
|
|
|
|
|
|
|
|
|
struct pair_t
|
|
|
|
|
{
|
|
|
|
|
u32 arg1;
|
|
|
|
|
u32 arg2;
|
|
|
|
|
};
|
|
|
|
|
|
2021-04-16 05:40:54 +02:00
|
|
|
template <typename T, typename T2 = std::common_type_t<T>>
|
2020-12-12 11:12:39 +01:00
|
|
|
cmd64(const T& value)
|
|
|
|
|
: m_data(std::bit_cast<u64, T2>(value))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T1, typename T2>
|
|
|
|
|
cmd64(const T1& arg1, const T2& arg2)
|
|
|
|
|
: cmd64(pair_t{std::bit_cast<u32>(arg1), std::bit_cast<u32>(arg2)})
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
explicit operator bool() const
|
|
|
|
|
{
|
|
|
|
|
return m_data != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
T as() const
|
|
|
|
|
{
|
|
|
|
|
return std::bit_cast<T>(m_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
T arg1() const
|
|
|
|
|
{
|
|
|
|
|
return std::bit_cast<T>(std::bit_cast<pair_t>(m_data).arg1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
T arg2() const
|
|
|
|
|
{
|
|
|
|
|
return std::bit_cast<T>(std::bit_cast<pair_t>(m_data).arg2);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-07-10 16:43:59 +02:00
|
|
|
enum class ppu_debugger_mode : u32
|
|
|
|
|
{
|
|
|
|
|
_default,
|
|
|
|
|
is_decimal,
|
|
|
|
|
|
|
|
|
|
max_mode,
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-24 12:41:04 +02:00
|
|
|
class ppu_thread : public cpu_thread, public PPUContext
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
public:
|
2017-01-25 18:50:30 +01:00
|
|
|
static const u32 id_base = 0x01000000; // TODO (used to determine thread type)
|
|
|
|
|
static const u32 id_step = 1;
|
2020-09-30 20:08:09 +02:00
|
|
|
static const u32 id_count = 100;
|
2019-11-28 11:17:16 +01:00
|
|
|
static constexpr std::pair<u32, u32> id_invl_range = {12, 12};
|
2016-08-09 16:14:41 +02:00
|
|
|
|
2023-07-12 10:02:12 +02:00
|
|
|
virtual void dump_regs(std::string&, std::any& custom_data) const override;
|
2020-03-31 02:11:37 +02:00
|
|
|
virtual std::string dump_callstack() const override;
|
2020-07-03 06:56:55 +02:00
|
|
|
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
|
2020-03-31 02:11:37 +02:00
|
|
|
virtual std::string dump_misc() const override;
|
2022-06-22 11:00:06 +02:00
|
|
|
virtual void dump_all(std::string&) const override;
|
2018-10-11 00:17:19 +02:00
|
|
|
virtual void cpu_task() override final;
|
2017-02-06 19:36:46 +01:00
|
|
|
virtual void cpu_sleep() override;
|
2021-02-13 15:50:07 +01:00
|
|
|
virtual void cpu_on_stop() override;
|
2025-10-04 21:19:57 +02:00
|
|
|
virtual void cpu_wait(rx::EnumBitSet<cpu_flag> old) override;
|
2016-07-27 23:43:22 +02:00
|
|
|
virtual ~ppu_thread() override;
|
|
|
|
|
|
2022-07-04 15:02:17 +02:00
|
|
|
SAVESTATE_INIT_POS(3);
|
2016-07-27 23:43:22 +02:00
|
|
|
|
2022-07-04 15:02:17 +02:00
|
|
|
ppu_thread(const ppu_thread_params&, std::string_view name, u32 prio, int detached = 0);
|
|
|
|
|
ppu_thread(utils::serial& ar);
|
2021-03-30 17:31:46 +02:00
|
|
|
ppu_thread(const ppu_thread&) = delete;
|
|
|
|
|
ppu_thread& operator=(const ppu_thread&) = delete;
|
2022-07-04 15:02:17 +02:00
|
|
|
bool savable() const;
|
|
|
|
|
void serialize_common(utils::serial& ar);
|
|
|
|
|
void save(utils::serial& ar);
|
2021-03-30 17:31:46 +02:00
|
|
|
|
2021-06-27 12:18:48 +02:00
|
|
|
using cpu_thread::operator=;
|
|
|
|
|
|
2023-04-28 19:10:21 +02:00
|
|
|
union ppu_prio_t
|
|
|
|
|
{
|
|
|
|
|
u64 all;
|
2025-04-05 21:50:45 +02:00
|
|
|
bf_t<s64, 0, 13> prio; // Thread priority (0..3071) (firs 12-bits)
|
|
|
|
|
bf_t<s64, 13, 50> order; // Thread enqueue order (last 52-bits)
|
2024-03-26 12:16:23 +01:00
|
|
|
bf_t<u64, 63, 1> preserve_bit; // Preserve value for savestates
|
2023-04-28 19:10:21 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
atomic_t<ppu_prio_t> prio{};
|
2016-07-27 23:43:22 +02:00
|
|
|
const u32 stack_size; // Stack size
|
|
|
|
|
const u32 stack_addr; // Stack address
|
2018-04-03 16:19:07 +02:00
|
|
|
|
2020-03-03 21:39:40 +01:00
|
|
|
atomic_t<ppu_join_status> joiner; // Joining thread or status
|
2025-04-05 21:50:45 +02:00
|
|
|
u32 hw_sleep_time = 0; // Very specific delay for hardware threads switching, see lv2_obj::awake_unlocked for more details
|
2015-07-01 00:25:52 +02:00
|
|
|
|
2017-01-30 14:20:09 +01:00
|
|
|
lf_fifo<atomic_t<cmd64>, 127> cmd_queue; // Command queue for asynchronous operations.
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-08-09 16:14:41 +02:00
|
|
|
void cmd_push(cmd64);
|
|
|
|
|
void cmd_list(std::initializer_list<cmd64>);
|
2016-07-27 23:43:22 +02:00
|
|
|
void cmd_pop(u32 = 0);
|
2016-08-09 16:14:41 +02:00
|
|
|
cmd64 cmd_wait(); // Empty command means caller must return, like true from cpu_thread::check_status().
|
2025-04-05 21:50:45 +02:00
|
|
|
cmd64 cmd_get(u32 index)
|
|
|
|
|
{
|
|
|
|
|
return cmd_queue[cmd_queue.peek() + index].load();
|
|
|
|
|
}
|
2021-02-13 15:50:07 +01:00
|
|
|
atomic_t<u32> cmd_notify = 0;
|
2014-09-24 20:44:26 +02:00
|
|
|
|
2021-03-16 14:41:32 +01:00
|
|
|
alignas(64) const ppu_func_opd_t entry_func;
|
2025-04-05 21:50:45 +02:00
|
|
|
u64 start_time{0}; // Sleep start timepoint
|
|
|
|
|
u64 end_time{umax}; // Sleep end timepoint
|
|
|
|
|
s32 cancel_sleep{0}; // Flag to cancel the next lv2_obj::sleep call (when equals 2)
|
|
|
|
|
u64 syscall_args[8]{0}; // Last syscall arguments stored
|
2019-07-09 19:44:07 +02:00
|
|
|
const char* current_function{}; // Current function name for diagnosis, optimized for speed.
|
2025-04-05 21:50:45 +02:00
|
|
|
const char* last_function{}; // Sticky copy of current_function, is not cleared on function return
|
|
|
|
|
const char* current_module{}; // Current module name, for savestates.
|
2015-08-30 18:16:38 +02:00
|
|
|
|
2022-07-04 15:02:17 +02:00
|
|
|
const bool is_interrupt_thread; // True for interrupts-handler threads
|
|
|
|
|
|
2020-02-28 08:43:37 +01:00
|
|
|
// Thread name
|
2020-11-26 10:30:51 +01:00
|
|
|
atomic_ptr<std::string> ppu_tname;
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2024-08-08 01:37:46 +02:00
|
|
|
// Hypervisor context data
|
|
|
|
|
rpcs3::hypervisor_context_t hv_ctx; // HV context for gate enter exit. Keep at a low struct offset.
|
2021-01-31 19:38:47 +01:00
|
|
|
|
2020-10-29 19:46:50 +01:00
|
|
|
u64 last_ftsc = 0;
|
|
|
|
|
u64 last_ftime = 0;
|
|
|
|
|
u32 last_faddr = 0;
|
|
|
|
|
u64 last_fail = 0;
|
|
|
|
|
u64 last_succ = 0;
|
2021-12-30 17:39:18 +01:00
|
|
|
u64 exec_bytes = 0; // Amount of "bytes" executed (4 for each instruction)
|
2020-10-29 19:46:50 +01:00
|
|
|
|
2021-02-19 12:53:09 +01:00
|
|
|
u32 dbg_step_pc = 0;
|
2023-07-10 16:43:59 +02:00
|
|
|
atomic_t<ppu_debugger_mode> debugger_mode{};
|
2021-02-19 12:53:09 +01:00
|
|
|
|
2021-07-10 10:56:48 +02:00
|
|
|
struct call_history_t
|
|
|
|
|
{
|
|
|
|
|
std::vector<u32> data;
|
|
|
|
|
u64 index = 0;
|
2021-07-18 16:02:34 +02:00
|
|
|
u64 last_r1 = umax;
|
|
|
|
|
u64 last_r2 = umax;
|
2021-07-10 10:56:48 +02:00
|
|
|
} call_history;
|
|
|
|
|
|
|
|
|
|
static constexpr u32 call_history_max_size = 4096;
|
|
|
|
|
|
2023-05-19 17:41:17 +02:00
|
|
|
struct syscall_history_t
|
|
|
|
|
{
|
|
|
|
|
struct entry_t
|
|
|
|
|
{
|
|
|
|
|
u64 cia;
|
|
|
|
|
const char* func_name;
|
|
|
|
|
u64 error;
|
|
|
|
|
std::array<u64, 4> args;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<entry_t> data;
|
|
|
|
|
u64 index = 0;
|
|
|
|
|
u32 count_debug_arguments;
|
|
|
|
|
} syscall_history;
|
|
|
|
|
|
|
|
|
|
static constexpr u32 syscall_history_max_size = 2048;
|
|
|
|
|
|
2021-09-06 09:33:44 +02:00
|
|
|
struct hle_func_call_with_toc_info_t
|
|
|
|
|
{
|
|
|
|
|
u32 cia;
|
|
|
|
|
u64 saved_lr;
|
|
|
|
|
u64 saved_r2;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<hle_func_call_with_toc_info_t> hle_func_calls_with_toc_info;
|
|
|
|
|
|
2021-05-01 08:34:52 +02:00
|
|
|
// For named_thread ctor
|
|
|
|
|
const struct thread_name_t
|
|
|
|
|
{
|
|
|
|
|
const ppu_thread* _this;
|
|
|
|
|
|
|
|
|
|
operator std::string() const;
|
2025-04-05 21:50:45 +02:00
|
|
|
} thread_name{this};
|
2021-05-01 08:34:52 +02:00
|
|
|
|
2022-07-04 15:02:17 +02:00
|
|
|
// For savestates
|
2022-07-05 13:12:21 +02:00
|
|
|
bool stop_flag_removal_protection = false; // If set, Emulator::Run won't remove stop flag
|
2025-04-05 21:50:45 +02:00
|
|
|
bool loaded_from_savestate = false; // Indicates the thread had just started straight from savestate load
|
2022-07-05 13:12:21 +02:00
|
|
|
std::shared_ptr<utils::serial> optional_savestate_state;
|
2022-07-04 15:02:17 +02:00
|
|
|
bool interrupt_thread_executing = false;
|
|
|
|
|
|
2022-07-28 13:10:16 +02:00
|
|
|
ppu_thread* next_cpu{}; // LV2 sleep queues' node link
|
|
|
|
|
ppu_thread* next_ppu{}; // LV2 PPU running queue's node link
|
2022-07-25 17:57:47 +02:00
|
|
|
bool ack_suspend = false;
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64));
|
2016-07-27 23:43:22 +02:00
|
|
|
void exec_task();
|
2023-06-07 13:34:39 +02:00
|
|
|
void fast_call(u32 addr, u64 rtoc, bool is_thread_entry = false);
|
2016-08-09 16:14:41 +02:00
|
|
|
|
2021-06-26 13:15:10 +02:00
|
|
|
static std::pair<vm::addr_t, u32> stack_push(u32 size, u32 align_v);
|
2016-08-09 16:14:41 +02:00
|
|
|
static void stack_pop_verbose(u32 addr, u32 size) noexcept;
|
2013-06-30 10:46:29 +02:00
|
|
|
};
|
|
|
|
|
|
2020-03-14 18:06:58 +01:00
|
|
|
static_assert(ppu_join_status::max <= ppu_join_status{ppu_thread::id_base});
|
2020-03-03 21:39:40 +01:00
|
|
|
|
2025-01-03 11:00:18 +01:00
|
|
|
template <typename T>
|
2016-04-14 01:09:41 +02:00
|
|
|
struct ppu_gpr_cast_impl
|
2014-11-19 15:16:30 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
static_assert(!sizeof(T), "Invalid type for ppu_gpr_cast<>");
|
2015-01-07 17:44:47 +01:00
|
|
|
};
|
|
|
|
|
|
2025-01-03 11:00:18 +01:00
|
|
|
template <typename T>
|
|
|
|
|
requires std::is_integral_v<T> || std::is_enum_v<T>
|
|
|
|
|
struct ppu_gpr_cast_impl<T>
|
2015-01-07 17:44:47 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
static_assert(sizeof(T) <= 8, "Too big integral type for ppu_gpr_cast<>()");
|
2024-03-20 17:16:49 +01:00
|
|
|
static_assert(std::is_same_v<std::decay_t<T>, bool> == false, "bool type is deprecated in ppu_gpr_cast<>(), use b8 instead");
|
2015-01-19 15:16:31 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline u64 to(const T& value)
|
2015-01-19 15:16:31 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return static_cast<u64>(value);
|
2015-01-07 17:44:47 +01:00
|
|
|
}
|
2015-01-19 15:16:31 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline T from(const u64 reg)
|
2015-01-19 15:16:31 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return static_cast<T>(reg);
|
2015-01-19 15:16:31 +01:00
|
|
|
}
|
2015-01-07 17:44:47 +01:00
|
|
|
};
|
|
|
|
|
|
2025-01-03 11:00:18 +01:00
|
|
|
template <>
|
|
|
|
|
struct ppu_gpr_cast_impl<b8>
|
2015-01-07 17:44:47 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline u64 to(const b8& value)
|
2015-01-07 17:44:47 +01:00
|
|
|
{
|
|
|
|
|
return value;
|
|
|
|
|
}
|
2015-01-19 15:16:31 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline b8 from(const u64 reg)
|
2015-01-19 15:16:31 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return static_cast<u32>(reg) != 0;
|
2015-01-19 15:16:31 +01:00
|
|
|
}
|
2015-01-07 17:44:47 +01:00
|
|
|
};
|
|
|
|
|
|
2025-01-03 11:00:18 +01:00
|
|
|
template <typename T, typename AT>
|
|
|
|
|
struct ppu_gpr_cast_impl<vm::_ptr_base<T, AT>>
|
2015-01-07 17:44:47 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline u64 to(const vm::_ptr_base<T, AT>& value)
|
2015-01-07 17:44:47 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return ppu_gpr_cast_impl<AT>::to(value.addr());
|
2015-01-07 17:44:47 +01:00
|
|
|
}
|
2015-01-19 15:16:31 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline vm::_ptr_base<T, AT> from(const u64 reg)
|
2015-01-19 15:16:31 +01:00
|
|
|
{
|
2016-05-13 15:55:34 +02:00
|
|
|
return vm::cast(ppu_gpr_cast_impl<AT>::from(reg));
|
2015-01-19 15:16:31 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-03 11:00:18 +01:00
|
|
|
template <typename T, typename AT>
|
|
|
|
|
struct ppu_gpr_cast_impl<vm::_ref_base<T, AT>>
|
2015-01-19 15:16:31 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline u64 to(const vm::_ref_base<T, AT>& value)
|
2015-01-19 15:16:31 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return ppu_gpr_cast_impl<AT>::to(value.addr());
|
2015-01-19 15:16:31 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
static inline vm::_ref_base<T, AT> from(const u64 reg)
|
2015-01-19 15:16:31 +01:00
|
|
|
{
|
2016-05-13 15:55:34 +02:00
|
|
|
return vm::cast(ppu_gpr_cast_impl<AT>::from(reg));
|
2015-01-19 15:16:31 +01:00
|
|
|
}
|
2015-01-07 17:44:47 +01:00
|
|
|
};
|
2015-01-14 20:45:36 +01:00
|
|
|
|
2017-10-01 03:40:11 +02:00
|
|
|
template <>
|
2025-01-03 11:00:18 +01:00
|
|
|
struct ppu_gpr_cast_impl<vm::null_t>
|
2017-10-01 03:40:11 +02:00
|
|
|
{
|
2019-12-26 21:01:48 +01:00
|
|
|
static inline u64 to(const vm::null_t& /*value*/)
|
2017-10-01 03:40:11 +02:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 21:01:48 +01:00
|
|
|
static inline vm::null_t from(const u64 /*reg*/)
|
2017-10-01 03:40:11 +02:00
|
|
|
{
|
|
|
|
|
return vm::null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-05 21:50:45 +02:00
|
|
|
template <typename To = u64, typename From>
|
2016-04-14 01:09:41 +02:00
|
|
|
inline To ppu_gpr_cast(const From& value)
|
2015-01-14 20:45:36 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return ppu_gpr_cast_impl<To>::from(ppu_gpr_cast_impl<From>::to(value));
|
2015-01-14 20:45:36 +01:00
|
|
|
}
|