2014-07-11 13:59:13 +02:00
|
|
|
#pragma once
|
2015-07-09 02:33:15 +02:00
|
|
|
|
2015-01-18 19:19:10 +01:00
|
|
|
#include "Emu/Cell/Common.h"
|
2015-02-26 01:54:49 +01:00
|
|
|
#include "Emu/CPU/CPUThread.h"
|
2014-11-19 15:16:30 +01:00
|
|
|
#include "Emu/Memory/vm.h"
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
class PPUThread final : public cpu_thread
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
public:
|
|
|
|
|
virtual std::string get_name() const override;
|
|
|
|
|
virtual std::string dump() const override;
|
|
|
|
|
virtual void cpu_init() override;
|
|
|
|
|
virtual void cpu_task() override;
|
|
|
|
|
virtual bool handle_interrupt() override;
|
|
|
|
|
virtual ~PPUThread() override;
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-04-25 12:49:12 +02:00
|
|
|
PPUThread(const std::string& name);
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
u64 GPR[32]{}; // General-Purpose Registers
|
|
|
|
|
f64 FPR[32]{}; // Floating Point Registers
|
|
|
|
|
v128 VR[32]{}; // Vector Registers
|
|
|
|
|
alignas(16) bool CR[32]{}; // Condition Registers
|
|
|
|
|
bool SO{}; // XER: Summary overflow
|
|
|
|
|
bool OV{}; // XER: Overflow
|
|
|
|
|
bool CA{}; // XER: Carry
|
|
|
|
|
u8 XCNT{}; // XER: 0..6
|
2013-06-30 10:46:29 +02:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
/*
|
2013-06-30 10:46:29 +02:00
|
|
|
Saturation. A sticky status bit indicating that some field in a saturating instruction saturated since the last
|
2014-07-12 16:53:36 +02:00
|
|
|
time SAT was cleared. In other words when SAT = '1' it remains set to '1' until it is cleared to '0' by an
|
2013-06-30 10:46:29 +02:00
|
|
|
mtvscr instruction.
|
|
|
|
|
1 The vector saturate instruction implicitly sets when saturation has occurred on the results one of
|
|
|
|
|
the vector instructions having saturate in its name:
|
|
|
|
|
Move To VSCR (mtvscr)
|
|
|
|
|
Vector Add Integer with Saturation (vaddubs, vadduhs, vadduws, vaddsbs, vaddshs,
|
|
|
|
|
vaddsws)
|
|
|
|
|
Vector Subtract Integer with Saturation (vsububs, vsubuhs, vsubuws, vsubsbs, vsubshs,
|
|
|
|
|
vsubsws)
|
|
|
|
|
Vector Multiply-Add Integer with Saturation (vmhaddshs, vmhraddshs)
|
|
|
|
|
Vector Multiply-Sum with Saturation (vmsumuhs, vmsumshs, vsumsws)
|
|
|
|
|
Vector Sum-Across with Saturation (vsumsws, vsum2sws, vsum4sbs, vsum4shs,
|
|
|
|
|
vsum4ubs)
|
|
|
|
|
Vector Pack with Saturation (vpkuhus, vpkuwus, vpkshus, vpkswus, vpkshss, vpkswss)
|
|
|
|
|
Vector Convert to Fixed-Point with Saturation (vctuxs, vctsxs)
|
|
|
|
|
0 Indicates no saturation occurred; mtvscr can explicitly clear this bit.
|
2016-04-14 01:09:41 +02:00
|
|
|
*/
|
|
|
|
|
bool SAT{};
|
2013-06-30 10:46:29 +02:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
/*
|
2013-06-30 10:46:29 +02:00
|
|
|
Non-Java. A mode control bit that determines whether vector floating-point operations will be performed
|
2014-07-12 16:53:36 +02:00
|
|
|
in a Java-IEEE-C9X-compliant mode or a possibly faster non-Java/non-IEEE mode.
|
|
|
|
|
0 The Java-IEEE-C9X-compliant mode is selected. Denormalized values are handled as specified
|
2013-06-30 10:46:29 +02:00
|
|
|
by Java, IEEE, and C9X standard.
|
2014-07-12 16:53:36 +02:00
|
|
|
1 The non-Java/non-IEEE-compliant mode is selected. If an element in a source vector register
|
|
|
|
|
contains a denormalized value, the value '0' is used instead. If an instruction causes an underflow
|
|
|
|
|
exception, the corresponding element in the target VR is cleared to '0'. In both cases, the '0'
|
2013-06-30 10:46:29 +02:00
|
|
|
has the same sign as the denormalized or underflowing value.
|
2016-04-14 01:09:41 +02:00
|
|
|
*/
|
|
|
|
|
bool NJ{ true };
|
|
|
|
|
|
|
|
|
|
bool FL{}; // FPSCR.FPCC.FL
|
|
|
|
|
bool FG{}; // FPSCR.FPCC.FG
|
|
|
|
|
bool FE{}; // FPSCR.FPCC.FE
|
|
|
|
|
bool FU{}; // FPSCR.FPCC.FU
|
|
|
|
|
|
|
|
|
|
u64 LR{}; // Link Register
|
|
|
|
|
u64 CTR{}; // Counter Register
|
|
|
|
|
u32 VRSAVE{};
|
|
|
|
|
u32 PC{};
|
|
|
|
|
|
|
|
|
|
s32 prio = 0; // Thread priority
|
|
|
|
|
u32 stack_addr = 0; // Stack address
|
|
|
|
|
u32 stack_size = 0; // Stack size
|
2015-07-01 00:25:52 +02:00
|
|
|
bool is_joinable = true;
|
|
|
|
|
bool is_joining = false;
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
std::function<void(PPUThread&)> custom_task;
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
// Function name can be stored here. Used to print the last called function.
|
|
|
|
|
const char* last_function = nullptr;
|
2014-09-24 20:44:26 +02:00
|
|
|
|
2015-09-26 22:46:04 +02:00
|
|
|
// When a thread has met an exception, this variable is used to retro propagate it through stack call.
|
2015-08-30 18:16:38 +02:00
|
|
|
std::exception_ptr pending_exception;
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
// Pack CR bits
|
|
|
|
|
u32 GetCR() const
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
u32 result{};
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
for (u32 bit : CR)
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
result = (result << 1) | bit;
|
2012-11-15 00:39:56 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
return result;
|
2012-11-15 00:39:56 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
// Unpack CR bits
|
|
|
|
|
void SetCR(u32 value)
|
2013-07-06 01:49:38 +02:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
for (bool& b : CR)
|
2013-07-06 01:49:38 +02:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
b = (value & 0x1) != 0;
|
|
|
|
|
value >>= 1;
|
2013-07-06 01:49:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
// Set CR field
|
|
|
|
|
void SetCR(u32 field, bool le, bool gt, bool eq, bool so)
|
2013-07-06 01:49:38 +02:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
CR[field * 4 + 0] = le;
|
|
|
|
|
CR[field * 4 + 1] = gt;
|
|
|
|
|
CR[field * 4 + 2] = eq;
|
|
|
|
|
CR[field * 4 + 3] = so;
|
2013-07-06 01:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
// Set CR field for comparison
|
|
|
|
|
template<typename T>
|
|
|
|
|
void SetCR(u32 field, const T& a, const T& b)
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
SetCR(field, a < b, a > b, a == b, SO);
|
2012-11-15 00:39:56 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
// Set overflow bit
|
2015-01-17 23:00:58 +01:00
|
|
|
void SetOV(const bool set)
|
|
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
OV = set;
|
|
|
|
|
SO |= set;
|
2012-11-15 00:39:56 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
u64 get_next_arg(u32& g_count)
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2015-02-19 12:18:28 +01:00
|
|
|
if (g_count < 8)
|
|
|
|
|
{
|
|
|
|
|
return GPR[g_count++ + 3];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return *get_stack_arg(++g_count);
|
2015-02-19 12:18:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64));
|
2015-07-19 13:36:32 +02:00
|
|
|
void fast_call(u32 addr, u32 rtoc);
|
2013-06-30 10:46:29 +02:00
|
|
|
};
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
template<typename T, typename = void>
|
|
|
|
|
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
|
|
|
};
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
template<typename T>
|
|
|
|
|
struct ppu_gpr_cast_impl<T, std::enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value>>
|
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<>()");
|
|
|
|
|
static_assert(std::is_same<CV T, CV bool>::value == 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
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<>
|
2016-04-14 01:09:41 +02:00
|
|
|
struct ppu_gpr_cast_impl<b8, void>
|
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
|
|
|
};
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
template<typename T, typename AT>
|
|
|
|
|
struct ppu_gpr_cast_impl<vm::_ptr_base<T, AT>, void>
|
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-04-14 01:09:41 +02:00
|
|
|
return{ ppu_gpr_cast_impl<AT>::from(reg), vm::addr };
|
2015-01-19 15:16:31 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
template<typename T, typename AT>
|
|
|
|
|
struct ppu_gpr_cast_impl<vm::_ref_base<T, AT>, void>
|
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-04-14 01:09:41 +02:00
|
|
|
return{ ppu_gpr_cast_impl<AT>::from(reg), vm::addr };
|
2015-01-19 15:16:31 +01:00
|
|
|
}
|
2015-01-07 17:44:47 +01:00
|
|
|
};
|
2015-01-14 20:45:36 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
template<typename To = u64, typename From>
|
|
|
|
|
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
|
|
|
}
|