mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-09 00:11:24 +12:00
984 lines
25 KiB
C++
984 lines
25 KiB
C++
#pragma once
|
|
#include "Emu/Cell/Common.h"
|
|
#include "Emu/Cell/PPCThread.h"
|
|
#include "Emu/Memory/vm.h"
|
|
|
|
enum
|
|
{
|
|
XER_SO = 0x80000000,
|
|
XER_OV = 0x40000000,
|
|
XER_CA = 0x20000000,
|
|
};
|
|
|
|
enum
|
|
{
|
|
CR_LT = 0x8,
|
|
CR_GT = 0x4,
|
|
CR_EQ = 0x2,
|
|
CR_SO = 0x1,
|
|
};
|
|
|
|
enum
|
|
{
|
|
PPU_THREAD_STATUS_IDLE = (1 << 0),
|
|
PPU_THREAD_STATUS_RUNNABLE = (1 << 1),
|
|
PPU_THREAD_STATUS_ONPROC = (1 << 2),
|
|
PPU_THREAD_STATUS_SLEEP = (1 << 3),
|
|
PPU_THREAD_STATUS_STOP = (1 << 4),
|
|
PPU_THREAD_STATUS_ZOMBIE = (1 << 5),
|
|
PPU_THREAD_STATUS_DELETED = (1 << 6),
|
|
PPU_THREAD_STATUS_UNKNOWN = (1 << 7),
|
|
};
|
|
|
|
enum FPSCR_EXP
|
|
{
|
|
FPSCR_FX = 0x80000000,
|
|
FPSCR_FEX = 0x40000000,
|
|
FPSCR_VX = 0x20000000,
|
|
FPSCR_OX = 0x10000000,
|
|
|
|
FPSCR_UX = 0x08000000,
|
|
FPSCR_ZX = 0x04000000,
|
|
FPSCR_XX = 0x02000000,
|
|
FPSCR_VXSNAN = 0x01000000,
|
|
|
|
FPSCR_VXISI = 0x00800000,
|
|
FPSCR_VXIDI = 0x00400000,
|
|
FPSCR_VXZDZ = 0x00200000,
|
|
FPSCR_VXIMZ = 0x00100000,
|
|
|
|
FPSCR_VXVC = 0x00080000,
|
|
FPSCR_FR = 0x00040000,
|
|
FPSCR_FI = 0x00020000,
|
|
|
|
FPSCR_VXSOFT = 0x00000400,
|
|
FPSCR_VXSQRT = 0x00000200,
|
|
FPSCR_VXCVI = 0x00000100,
|
|
|
|
FPSCR_VX_ALL = FPSCR_VXSNAN | FPSCR_VXISI | FPSCR_VXIDI | FPSCR_VXZDZ | FPSCR_VXIMZ | FPSCR_VXVC | FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI,
|
|
};
|
|
|
|
static const u64 DOUBLE_SIGN = 0x8000000000000000ULL;
|
|
static const u64 DOUBLE_EXP = 0x7FF0000000000000ULL;
|
|
static const u64 DOUBLE_FRAC = 0x000FFFFFFFFFFFFFULL;
|
|
static const u64 DOUBLE_ZERO = 0x0000000000000000ULL;
|
|
|
|
union FPSCRhdr
|
|
{
|
|
struct
|
|
{
|
|
u32 RN :2; //Floating-point rounding control
|
|
u32 NI :1; //Floating-point non-IEEE mode
|
|
u32 XE :1; //Floating-point inexact exception enable
|
|
u32 ZE :1; //IEEE floating-point zero divide exception enable
|
|
u32 UE :1; //IEEE floating-point underflow exception enable
|
|
u32 OE :1; //IEEE floating-point overflow exception enable
|
|
u32 VE :1; //Floating-point invalid operation exception enable
|
|
u32 VXCVI :1; //Floating-point invalid operation exception for invalid integer convert
|
|
u32 VXSQRT :1; //Floating-point invalid operation exception for invalid square root
|
|
u32 VXSOFT :1; //Floating-point invalid operation exception for software request
|
|
u32 :1; //Reserved
|
|
u32 FPRF :5; //Floating-point result flags
|
|
u32 FI :1; //Floating-point fraction inexact
|
|
u32 FR :1; //Floating-point fraction rounded
|
|
u32 VXVC :1; //Floating-point invalid operation exception for invalid compare
|
|
u32 VXIMZ :1; //Floating-point invalid operation exception for * * 0
|
|
u32 VXZDZ :1; //Floating-point invalid operation exception for 0 / 0
|
|
u32 VXIDI :1; //Floating-point invalid operation exception for * + *
|
|
u32 VXISI :1; //Floating-point invalid operation exception for * - *
|
|
u32 VXSNAN :1; //Floating-point invalid operation exception for SNaN
|
|
u32 XX :1; //Floating-point inexact exception
|
|
u32 ZX :1; //Floating-point zero divide exception
|
|
u32 UX :1; //Floating-point underflow exception
|
|
u32 OX :1; //Floating-point overflow exception
|
|
u32 VX :1; //Floating-point invalid operation exception summary
|
|
u32 FEX :1; //Floating-point enabled exception summary
|
|
u32 FX :1; //Floating-point exception summary
|
|
};
|
|
|
|
u32 FPSCR;
|
|
};
|
|
|
|
union MSRhdr
|
|
{
|
|
struct
|
|
{
|
|
//Little-endian mode enable
|
|
//0 The processor runs in big-endian mode.
|
|
//1 The processor runs in little-endian mode.
|
|
u64 LE : 1;
|
|
|
|
//Recoverable exception (for system reset and machine check exceptions).
|
|
//0 Exception is not recoverable.
|
|
//1 Exception is recoverable.
|
|
u64 RI : 1;
|
|
|
|
//Reserved
|
|
u64 : 2;
|
|
|
|
//Data address translation
|
|
//0 Data address translation is disabled.
|
|
//1 Data address translation is enabled.
|
|
u64 DR : 1;
|
|
|
|
//Instruction address translation
|
|
//0 Instruction address translation is disabled.
|
|
//1 Instruction address translation is enabled.
|
|
u64 IR : 1;
|
|
|
|
//Exception prefix. The setting of this bit specifies whether an exception vector offset
|
|
//is prepended with Fs or 0s. In the following description, nnnnn is the offset of the
|
|
//exception.
|
|
//0 Exceptions are vectored to the physical address 0x0000_0000_000n_nnnn in 64-bit implementations.
|
|
//1 Exceptions are vectored to the physical address 0xFFFF_FFFF_FFFn_nnnn in 64-bit implementations.
|
|
u64 IP : 1;
|
|
|
|
//Reserved
|
|
u64 : 1;
|
|
|
|
//Floating-point exception mode 1
|
|
u64 FE1 : 1;
|
|
|
|
//Branch trace enable (Optional)
|
|
//0 The processor executes branch instructions normally.
|
|
//1 The processor generates a branch trace exception after completing the
|
|
//execution of a branch instruction, regardless of whether or not the branch was
|
|
//taken.
|
|
//Note: If the function is not implemented, this bit is treated as reserved.
|
|
u64 BE : 1;
|
|
|
|
//Single-step trace enable (Optional)
|
|
//0 The processor executes instructions normally.
|
|
//1 The processor generates a single-step trace exception upon the successful
|
|
//execution of the next instruction.
|
|
//Note: If the function is not implemented, this bit is treated as reserved.
|
|
u64 SE : 1;
|
|
|
|
//Floating-point exception mode 0
|
|
u64 FE0 : 1;
|
|
|
|
//Machine check enable
|
|
//0 Machine check exceptions are disabled.
|
|
//1 Machine check exceptions are enabled.
|
|
u64 ME : 1;
|
|
|
|
//Floating-point available
|
|
//0 The processor prevents dispatch of floating-point instructions, including
|
|
//floating-point loads, stores, and moves.
|
|
//1 The processor can execute floating-point instructions.
|
|
u64 FP : 1;
|
|
|
|
//Privilege level
|
|
//0 The processor can execute both user- and supervisor-level instructions.
|
|
//1 The processor can only execute user-level instructions.
|
|
u64 PR : 1;
|
|
|
|
//External interrupt enable
|
|
//0 While the bit is cleared the processor delays recognition of external interrupts
|
|
//and decrementer exception conditions.
|
|
//1 The processor is enabled to take an external interrupt or the decrementer
|
|
//exception.
|
|
u64 EE : 1;
|
|
|
|
//Exception little-endian mode. When an exception occurs, this bit is copied into
|
|
//MSR[LE] to select the endian mode for the context established by the exception
|
|
u64 ILE : 1;
|
|
|
|
//Reserved
|
|
u64 : 1;
|
|
|
|
//Power management enable
|
|
//0 Power management disabled (normal operation mode).
|
|
//1 Power management enabled (reduced power mode).
|
|
//Note: Power management functions are implementation-dependent. If the function
|
|
//is not implemented, this bit is treated as reserved.
|
|
u64 POW : 1;
|
|
|
|
//Reserved
|
|
u64 : 44;
|
|
|
|
//Sixty-four bit mode
|
|
//0 The 64-bit processor runs in 32-bit mode.
|
|
//1 The 64-bit processor runs in 64-bit mode. Note that this is the default setting.
|
|
u64 SF : 1;
|
|
};
|
|
|
|
u64 MSR;
|
|
};
|
|
|
|
union PVRhdr
|
|
{
|
|
struct
|
|
{
|
|
u16 revision;
|
|
u16 version;
|
|
};
|
|
|
|
u32 PVR;
|
|
};
|
|
|
|
union CRhdr
|
|
{
|
|
u32 CR;
|
|
|
|
struct
|
|
{
|
|
u8 cr7 : 4;
|
|
u8 cr6 : 4;
|
|
u8 cr5 : 4;
|
|
u8 cr4 : 4;
|
|
u8 cr3 : 4;
|
|
u8 cr2 : 4;
|
|
u8 cr1 : 4;
|
|
u8 cr0 : 4;
|
|
};
|
|
};
|
|
|
|
union XERhdr
|
|
{
|
|
u64 XER;
|
|
|
|
struct
|
|
{
|
|
u32 L : 29;
|
|
u32 CA : 1;
|
|
u32 OV : 1;
|
|
u32 SO : 1;
|
|
u32 : 32;
|
|
};
|
|
};
|
|
|
|
union VSCRhdr
|
|
{
|
|
u32 VSCR;
|
|
|
|
struct
|
|
{
|
|
/*
|
|
Saturation. A sticky status bit indicating that some field in a saturating instruction saturated since the last
|
|
time SAT was cleared. In other words when SAT = '1' it remains set to '1' until it is cleared to '0' by an
|
|
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.
|
|
*/
|
|
u32 SAT : 1;
|
|
u32 X : 15;
|
|
|
|
/*
|
|
Non-Java. A mode control bit that determines whether vector floating-point operations will be performed
|
|
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
|
|
by Java, IEEE, and C9X standard.
|
|
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'
|
|
has the same sign as the denormalized or underflowing value.
|
|
*/
|
|
u32 NJ : 1;
|
|
u32 Y : 15;
|
|
};
|
|
};
|
|
|
|
enum FPRType
|
|
{
|
|
//FPR_NORM,
|
|
//FPR_ZERO,
|
|
//FPR_SNAN,
|
|
//FPR_QNAN,
|
|
//FPR_INF,
|
|
FPR_PZ = 0x2,
|
|
FPR_PN = 0x4,
|
|
FPR_PINF = 0x5,
|
|
FPR_NN = 0x8,
|
|
FPR_NINF = 0x9,
|
|
FPR_QNAN = 0x11,
|
|
FPR_NZ = 0x12,
|
|
FPR_PD = 0x14,
|
|
FPR_ND = 0x18,
|
|
};
|
|
|
|
static const u64 FPR_NAN_I = 0x7FF8000000000000ULL;
|
|
static const double& FPR_NAN = (double&)FPR_NAN_I;
|
|
|
|
struct PPCdouble
|
|
{
|
|
union
|
|
{
|
|
double _double;
|
|
u64 _u64;
|
|
|
|
struct
|
|
{
|
|
u64 frac : 52;
|
|
u64 exp : 11;
|
|
u64 sign : 1;
|
|
};
|
|
|
|
struct
|
|
{
|
|
u64 : 51;
|
|
u64 nan : 1;
|
|
u64 : 12;
|
|
};
|
|
};
|
|
|
|
FPRType type;
|
|
|
|
operator double&() { return _double; }
|
|
operator const double&() const { return _double; }
|
|
|
|
PPCdouble& operator = (const PPCdouble& r)
|
|
{
|
|
_u64 = r._u64;
|
|
type = UpdateType();
|
|
return *this;
|
|
}
|
|
|
|
FPRType UpdateType() const
|
|
{
|
|
const int fpc = _fpclass(_double);
|
|
|
|
#ifndef __GNUG__
|
|
switch(fpc)
|
|
{
|
|
case _FPCLASS_SNAN:// return FPR_SNAN;
|
|
case _FPCLASS_QNAN: return FPR_QNAN;
|
|
case _FPCLASS_NINF: return FPR_NINF;
|
|
case _FPCLASS_NN: return FPR_NN;
|
|
case _FPCLASS_ND: return FPR_ND;
|
|
case _FPCLASS_NZ: return FPR_NZ;
|
|
case _FPCLASS_PZ: return FPR_PZ;
|
|
case _FPCLASS_PD: return FPR_PD;
|
|
case _FPCLASS_PN: return FPR_PN;
|
|
case _FPCLASS_PINF: return FPR_PINF;
|
|
default: throw fmt::Format("PPCdouble::UpdateType() -> unknown fpclass (0x%04x).", fpc);
|
|
}
|
|
#else
|
|
switch (fpc)
|
|
{
|
|
case FP_NAN: return FPR_QNAN;
|
|
case FP_INFINITE: return std::signbit(_double) ? FPR_NINF : FPR_PINF;
|
|
case FP_SUBNORMAL: return std::signbit(_double) ? FPR_ND : FPR_PD;
|
|
case FP_ZERO: return std::signbit(_double) ? FPR_NZ : FPR_PZ;
|
|
default: return std::signbit(_double) ? FPR_NN : FPR_PN;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
FPRType GetType() const
|
|
{
|
|
return type;
|
|
}
|
|
|
|
u32 To32() const
|
|
{
|
|
float res = (float)_double;
|
|
|
|
return (u32&)res;
|
|
}
|
|
|
|
u64 To64() const
|
|
{
|
|
return (u64&)_double;
|
|
}
|
|
|
|
u32 GetZerosCount() const
|
|
{
|
|
u32 ret;
|
|
u32 dd = frac >> 32;
|
|
if(dd)
|
|
{
|
|
ret = 31;
|
|
}
|
|
else
|
|
{
|
|
dd = frac;
|
|
ret = 63;
|
|
}
|
|
|
|
if(dd > 0xffff)
|
|
{
|
|
ret -= 16;
|
|
dd >>= 16;
|
|
}
|
|
if(dd > 0xff)
|
|
{
|
|
ret -= 8;
|
|
dd >>= 8;
|
|
}
|
|
if(dd & 0xf0)
|
|
{
|
|
ret -= 4;
|
|
dd >>= 4;
|
|
}
|
|
if(dd & 0xc)
|
|
{
|
|
ret -= 2;
|
|
dd >>= 2;
|
|
}
|
|
if(dd & 0x2) ret--;
|
|
|
|
return ret;
|
|
}
|
|
|
|
PPCdouble() : _u64(0)
|
|
{
|
|
type = UpdateType();
|
|
}
|
|
|
|
PPCdouble(double val) : _double(val)
|
|
{
|
|
type = UpdateType();
|
|
}
|
|
|
|
PPCdouble(u64 val) : _u64(val)
|
|
{
|
|
type = UpdateType();
|
|
}
|
|
|
|
PPCdouble(u32 val) : _u64(val)
|
|
{
|
|
type = UpdateType();
|
|
}
|
|
};
|
|
|
|
struct FPRdouble
|
|
{
|
|
static const u64 double_sign = 0x8000000000000000ULL;
|
|
static const u64 double_frac = 0x000FFFFFFFFFFFFFULL;
|
|
|
|
static bool IsINF(PPCdouble d);
|
|
static bool IsNaN(PPCdouble d);
|
|
static bool IsQNaN(PPCdouble d);
|
|
static bool IsSNaN(PPCdouble d);
|
|
|
|
static int Cmp(PPCdouble a, PPCdouble b);
|
|
};
|
|
|
|
class PPUThread : public PPCThread
|
|
{
|
|
public:
|
|
PPCdouble FPR[32]; //Floating Point Register
|
|
FPSCRhdr FPSCR; //Floating Point Status and Control Register
|
|
u64 GPR[32]; //General-Purpose Register
|
|
u128 VPR[32];
|
|
u32 vpcr;
|
|
|
|
CRhdr CR; //Condition Register
|
|
//CR0
|
|
// 0 : LT - Negative (is negative)
|
|
// : 0 - Result is not negative
|
|
// : 1 - Result is negative
|
|
// 1 : GT - Positive (is positive)
|
|
// : 0 - Result is not positive
|
|
// : 1 - Result is positive
|
|
// 2 : EQ - Zero (is zero)
|
|
// : 0 - Result is not equal to zero
|
|
// : 1 - Result is equal to zero
|
|
// 3 : SO - Summary overflow (copy of the final state XER[S0])
|
|
// : 0 - No overflow occurred
|
|
// : 1 - Overflow occurred
|
|
|
|
//CRn
|
|
// 0 : LT - Less than (rA > X)
|
|
// : 0 - rA is not less than
|
|
// : 1 - rA is less than
|
|
// 1 : GT - Greater than (rA < X)
|
|
// : 0 - rA is not greater than
|
|
// : 1 - rA is greater than
|
|
// 2 : EQ - Equal to (rA == X)
|
|
// : 0 - Result is not equal to zero
|
|
// : 1 - Result is equal to zero
|
|
// 3 : SO - Summary overflow (copy of the final state XER[S0])
|
|
// : 0 - No overflow occurred
|
|
// : 1 - Overflow occurred
|
|
|
|
//SPR : Special-Purpose Registers
|
|
|
|
XERhdr XER; //SPR 0x001 : Fixed-Point Expection Register
|
|
// 0 : SO - Summary overflow
|
|
// : 0 - No overflow occurred
|
|
// : 1 - Overflow occurred
|
|
// 1 : OV - Overflow
|
|
// : 0 - No overflow occurred
|
|
// : 1 - Overflow occurred
|
|
// 2 : CA - Carry
|
|
// : 0 - Carry did not occur
|
|
// : 1 - Carry occured
|
|
// 3 - 24 : Reserved
|
|
// 25 - 31 : TBC
|
|
// Transfer-byte count
|
|
|
|
MSRhdr MSR; //Machine State Register
|
|
PVRhdr PVR; //Processor Version Register
|
|
|
|
VSCRhdr VSCR; // Vector Status and Control Register
|
|
|
|
u64 LR; //SPR 0x008 : Link Register
|
|
u64 CTR; //SPR 0x009 : Count Register
|
|
|
|
u32 VRSAVE; //SPR 0x100: VR Save/Restore Register (32 bits)
|
|
|
|
u64 SPRG[8]; //SPR 0x110 - 0x117 : SPR General-Purpose Registers
|
|
|
|
//TBR : Time-Base Registers
|
|
u64 TB; //TBR 0x10C - 0x10D
|
|
|
|
u32 owned_mutexes;
|
|
std::function<void(PPUThread& CPU)> custom_task;
|
|
|
|
public:
|
|
PPUThread();
|
|
virtual ~PPUThread();
|
|
|
|
inline u8 GetCR(const u8 n) const
|
|
{
|
|
switch(n)
|
|
{
|
|
case 0: return CR.cr0;
|
|
case 1: return CR.cr1;
|
|
case 2: return CR.cr2;
|
|
case 3: return CR.cr3;
|
|
case 4: return CR.cr4;
|
|
case 5: return CR.cr5;
|
|
case 6: return CR.cr6;
|
|
case 7: return CR.cr7;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
inline void SetCR(const u8 n, const u32 value)
|
|
{
|
|
switch(n)
|
|
{
|
|
case 0: CR.cr0 = value; break;
|
|
case 1: CR.cr1 = value; break;
|
|
case 2: CR.cr2 = value; break;
|
|
case 3: CR.cr3 = value; break;
|
|
case 4: CR.cr4 = value; break;
|
|
case 5: CR.cr5 = value; break;
|
|
case 6: CR.cr6 = value; break;
|
|
case 7: CR.cr7 = value; break;
|
|
}
|
|
}
|
|
|
|
inline void SetCRBit(const u8 n, const u32 bit, const bool value)
|
|
{
|
|
switch(n)
|
|
{
|
|
case 0: CR.cr0 = (value ? CR.cr0 | bit : CR.cr0 & ~bit); break;
|
|
case 1: CR.cr1 = (value ? CR.cr1 | bit : CR.cr1 & ~bit); break;
|
|
case 2: CR.cr2 = (value ? CR.cr2 | bit : CR.cr2 & ~bit); break;
|
|
case 3: CR.cr3 = (value ? CR.cr3 | bit : CR.cr3 & ~bit); break;
|
|
case 4: CR.cr4 = (value ? CR.cr4 | bit : CR.cr4 & ~bit); break;
|
|
case 5: CR.cr5 = (value ? CR.cr5 | bit : CR.cr5 & ~bit); break;
|
|
case 6: CR.cr6 = (value ? CR.cr6 | bit : CR.cr6 & ~bit); break;
|
|
case 7: CR.cr7 = (value ? CR.cr7 | bit : CR.cr7 & ~bit); break;
|
|
}
|
|
}
|
|
|
|
inline void SetCR_EQ(const u8 n, const bool value) { SetCRBit(n, CR_EQ, value); }
|
|
inline void SetCR_GT(const u8 n, const bool value) { SetCRBit(n, CR_GT, value); }
|
|
inline void SetCR_LT(const u8 n, const bool value) { SetCRBit(n, CR_LT, value); }
|
|
inline void SetCR_SO(const u8 n, const bool value) { SetCRBit(n, CR_SO, value); }
|
|
|
|
inline bool IsCR_EQ(const u8 n) const { return (GetCR(n) & CR_EQ) ? 1 : 0; }
|
|
inline bool IsCR_GT(const u8 n) const { return (GetCR(n) & CR_GT) ? 1 : 0; }
|
|
inline bool IsCR_LT(const u8 n) const { return (GetCR(n) & CR_LT) ? 1 : 0; }
|
|
|
|
template<typename T> void UpdateCRn(const u8 n, const T a, const T b)
|
|
{
|
|
if (a < b) SetCR(n, CR_LT);
|
|
else if (a > b) SetCR(n, CR_GT);
|
|
else if (a == b) SetCR(n, CR_EQ);
|
|
|
|
SetCR_SO(n, XER.SO);
|
|
}
|
|
|
|
void UpdateCRnU(const u8 l, const u8 n, const u64 a, const u64 b)
|
|
{
|
|
if(l)
|
|
{
|
|
UpdateCRn<u64>(n, a, b);
|
|
}
|
|
else
|
|
{
|
|
UpdateCRn<u32>(n, (u32)a, (u32)b);
|
|
}
|
|
}
|
|
|
|
void UpdateCRnS(const u8 l, const u8 n, const u64 a, const u64 b)
|
|
{
|
|
if(l)
|
|
{
|
|
UpdateCRn<s64>(n, (s64)a, (s64)b);
|
|
}
|
|
else
|
|
{
|
|
UpdateCRn<s32>(n, (s32)a, (s32)b);
|
|
}
|
|
}
|
|
|
|
template<typename T> void UpdateCR0(const T val)
|
|
{
|
|
UpdateCRn<T>(0, val, 0);
|
|
}
|
|
|
|
void UpdateCR1()
|
|
{
|
|
SetCR_LT(1, FPSCR.FX);
|
|
SetCR_GT(1, FPSCR.FEX);
|
|
SetCR_EQ(1, FPSCR.VX);
|
|
SetCR_SO(1, FPSCR.OX);
|
|
}
|
|
|
|
const u8 GetCRBit(const u32 bit) const { return 1 << (3 - (bit % 4)); }
|
|
|
|
void SetCRBit (const u32 bit, bool set) { SetCRBit(bit >> 2, GetCRBit(bit), set); }
|
|
void SetCRBit2(const u32 bit, bool set) { SetCRBit(bit >> 2, 0x8 >> (bit & 3), set); }
|
|
|
|
const u8 IsCR(const u32 bit) const { return (GetCR(bit >> 2) & GetCRBit(bit)) ? 1 : 0; }
|
|
|
|
bool IsCarry(const u64 a, const u64 b) { return (a + b) < a; }
|
|
bool IsCarry(const u64 a, const u64 b, const u64 c) { return IsCarry(a, b) || IsCarry(a + b, c); }
|
|
|
|
void SetOV(const bool set)
|
|
{
|
|
XER.OV = set;
|
|
XER.SO |= set;
|
|
}
|
|
|
|
void UpdateFPSCR_FEX()
|
|
{
|
|
const u32 exceptions = (FPSCR.FPSCR >> 25) & 0x1F;
|
|
const u32 enabled = (FPSCR.FPSCR >> 3) & 0x1F;
|
|
if (exceptions & enabled) FPSCR.FEX = 1;
|
|
}
|
|
|
|
void UpdateFPSCR_VX()
|
|
{
|
|
if (FPSCR.FPSCR & FPSCR_VX_ALL) FPSCR.VX = 1;
|
|
}
|
|
|
|
void SetFPSCR(const u32 val)
|
|
{
|
|
FPSCR.FPSCR = val & ~(FPSCR_FEX | FPSCR_VX);
|
|
UpdateFPSCR_VX();
|
|
UpdateFPSCR_FEX();
|
|
}
|
|
|
|
void SetFPSCRException(const FPSCR_EXP mask)
|
|
{
|
|
if ((FPSCR.FPSCR & mask) != mask) FPSCR.FX = 1;
|
|
FPSCR.FPSCR |= mask;
|
|
UpdateFPSCR_VX();
|
|
UpdateFPSCR_FEX();
|
|
}
|
|
|
|
void SetFPSCR_FI(const u32 val)
|
|
{
|
|
if(val) SetFPSCRException(FPSCR_XX);
|
|
FPSCR.FI = val;
|
|
}
|
|
|
|
virtual std::string RegsToString()
|
|
{
|
|
std::string ret = "Registers:\n=========\n";
|
|
|
|
for(uint i=0; i<32; ++i) ret += fmt::Format("GPR[%d] = 0x%llx\n", i, GPR[i]);
|
|
for(uint i=0; i<32; ++i) ret += fmt::Format("FPR[%d] = %.6G\n", i, (double)FPR[i]);
|
|
for(uint i=0; i<32; ++i) ret += fmt::Format("VPR[%d] = 0x%s [%s]\n", i, VPR[i].to_hex().c_str(), VPR[i].to_xyzw().c_str());
|
|
ret += fmt::Format("CR = 0x%08x\n", CR.CR);
|
|
ret += fmt::Format("LR = 0x%llx\n", LR);
|
|
ret += fmt::Format("CTR = 0x%llx\n", CTR);
|
|
ret += fmt::Format("XER = 0x%llx [CA=%lld | OV=%lld | SO=%lld]\n", XER.XER, fmt::by_value(XER.CA), fmt::by_value(XER.OV), fmt::by_value(XER.SO));
|
|
ret += fmt::Format("FPSCR = 0x%x "
|
|
"[RN=%d | NI=%d | XE=%d | ZE=%d | UE=%d | OE=%d | VE=%d | "
|
|
"VXCVI=%d | VXSQRT=%d | VXSOFT=%d | FPRF=%d | "
|
|
"FI=%d | FR=%d | VXVC=%d | VXIMZ=%d | "
|
|
"VXZDZ=%d | VXIDI=%d | VXISI=%d | VXSNAN=%d | "
|
|
"XX=%d | ZX=%d | UX=%d | OX=%d | VX=%d | FEX=%d | FX=%d]\n",
|
|
FPSCR.FPSCR,
|
|
fmt::by_value(FPSCR.RN),
|
|
fmt::by_value(FPSCR.NI), fmt::by_value(FPSCR.XE), fmt::by_value(FPSCR.ZE), fmt::by_value(FPSCR.UE), fmt::by_value(FPSCR.OE), fmt::by_value(FPSCR.VE),
|
|
fmt::by_value(FPSCR.VXCVI), fmt::by_value(FPSCR.VXSQRT), fmt::by_value(FPSCR.VXSOFT), fmt::by_value(FPSCR.FPRF),
|
|
fmt::by_value(FPSCR.FI), fmt::by_value(FPSCR.FR), fmt::by_value(FPSCR.VXVC), fmt::by_value(FPSCR.VXIMZ),
|
|
fmt::by_value(FPSCR.VXZDZ), fmt::by_value(FPSCR.VXIDI), fmt::by_value(FPSCR.VXISI), fmt::by_value(FPSCR.VXSNAN),
|
|
fmt::by_value(FPSCR.XX), fmt::by_value(FPSCR.ZX), fmt::by_value(FPSCR.UX), fmt::by_value(FPSCR.OX), fmt::by_value(FPSCR.VX), fmt::by_value(FPSCR.FEX), fmt::by_value(FPSCR.FX));
|
|
|
|
return ret;
|
|
}
|
|
|
|
virtual std::string ReadRegString(const std::string& reg)
|
|
{
|
|
std::string::size_type first_brk = reg.find('[');
|
|
if (first_brk != std::string::npos)
|
|
{
|
|
long reg_index = atol(reg.substr(first_brk+1,reg.length()-first_brk-2).c_str());
|
|
if (reg.find("GPR")==0) return fmt::Format("%016llx", GPR[reg_index]);
|
|
if (reg.find("FPR")==0) return fmt::Format("%016llx", (double)FPR[reg_index]);
|
|
if (reg.find("VPR")==0) return fmt::Format("%016llx%016llx", VPR[reg_index]._u64[1], VPR[reg_index]._u64[0]);
|
|
}
|
|
if (reg == "CR") return fmt::Format("%08x", CR.CR);
|
|
if (reg == "LR") return fmt::Format("%016llx", LR);
|
|
if (reg == "CTR") return fmt::Format("%016llx", CTR);
|
|
if (reg == "XER") return fmt::Format("%016llx", XER.XER);
|
|
if (reg == "FPSCR") return fmt::Format("%08x", FPSCR.FPSCR);
|
|
|
|
return "";
|
|
}
|
|
|
|
bool WriteRegString(const std::string& reg, std::string value) {
|
|
while (value.length() < 32) value = "0"+value;
|
|
std::string::size_type first_brk = reg.find('[');
|
|
try
|
|
{
|
|
if (first_brk != std::string::npos)
|
|
{
|
|
long reg_index = atol(reg.substr(first_brk + 1, reg.length() - first_brk - 2).c_str());
|
|
if (reg.find("GPR")==0 || reg.find("FPR")==0 )
|
|
{
|
|
unsigned long long reg_value;
|
|
reg_value = std::stoull(value.substr(16, 31),0,16);
|
|
if (reg.find("GPR")==0) GPR[reg_index] = (u64)reg_value;
|
|
if (reg.find("FPR")==0) FPR[reg_index] = (u64)reg_value;
|
|
return true;
|
|
}
|
|
if (reg.find("VPR")==0)
|
|
{
|
|
unsigned long long reg_value0;
|
|
unsigned long long reg_value1;
|
|
reg_value0 = std::stoull(value.substr(16, 31), 0, 16);
|
|
reg_value1 = std::stoull(value.substr(0, 15), 0, 16);
|
|
VPR[reg_index]._u64[0] = (u64)reg_value0;
|
|
VPR[reg_index]._u64[1] = (u64)reg_value1;
|
|
return true;
|
|
}
|
|
}
|
|
if (reg == "LR" || reg == "CTR" || reg == "XER")
|
|
{
|
|
unsigned long long reg_value;
|
|
reg_value = std::stoull(value.substr(16, 31), 0, 16);
|
|
if (reg == "LR") LR = (u64)reg_value;
|
|
if (reg == "CTR") CTR = (u64)reg_value;
|
|
if (reg == "XER") XER.XER = (u64)reg_value;
|
|
return true;
|
|
}
|
|
if (reg == "CR" || reg == "FPSCR")
|
|
{
|
|
unsigned long long reg_value;
|
|
reg_value = std::stoull(value.substr(24, 31), 0, 16);
|
|
if (reg == "CR") CR.CR = (u32)reg_value;
|
|
if (reg == "FPSCR") FPSCR.FPSCR = (u32)reg_value;
|
|
return true;
|
|
}
|
|
}
|
|
catch (std::invalid_argument&)//if any of the stoull conversion fail
|
|
{
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
virtual void InitRegs() override;
|
|
virtual void InitStack() override;
|
|
virtual void CloseStack() override;
|
|
virtual void Task() override;
|
|
u64 GetStackArg(s32 i);
|
|
u64 FastCall2(u32 addr, u32 rtoc);
|
|
void FastStop();
|
|
virtual void DoRun() override;
|
|
|
|
protected:
|
|
virtual void DoReset() override;
|
|
virtual void DoPause() override;
|
|
virtual void DoResume() override;
|
|
virtual void DoStop() override;
|
|
};
|
|
|
|
PPUThread& GetCurrentPPUThread();
|
|
|
|
class ppu_thread : cpu_thread
|
|
{
|
|
static const u32 stack_align = 0x10;
|
|
vm::ptr<u64> argv;
|
|
u32 argc;
|
|
vm::ptr<u64> envp;
|
|
|
|
public:
|
|
ppu_thread(u32 entry, const std::string& name = "", u32 stack_size = 0, u32 prio = 0);
|
|
|
|
cpu_thread& args(std::initializer_list<std::string> values) override;
|
|
cpu_thread& run() override;
|
|
ppu_thread& gpr(uint index, u64 value);
|
|
};
|
|
|
|
template<typename T, bool is_enum = std::is_enum<T>::value>
|
|
struct cast_ppu_gpr
|
|
{
|
|
static_assert(is_enum, "Invalid type for cast_ppu_gpr");
|
|
|
|
typedef typename std::underlying_type<T>::type underlying_type;
|
|
|
|
__forceinline static u64 to_gpr(const T& value)
|
|
{
|
|
return cast_ppu_gpr<underlying_type>::to_gpr(static_cast<underlying_type>(value));
|
|
}
|
|
|
|
__forceinline static T from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<T>(cast_ppu_gpr<underlying_type>::from_gpr(reg));
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<u8, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const u8& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static u8 from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<u8>(reg);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<u16, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const u16& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static u16 from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<u16>(reg);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<u32, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const u32& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static u32 from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<u32>(reg);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<u64, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const u64& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static u64 from_gpr(const u64 reg)
|
|
{
|
|
return reg;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<s8, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const s8& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static s8 from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<s8>(reg);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<s16, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const s16& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static s16 from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<s16>(reg);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<s32, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const s32& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static s32 from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<s32>(reg);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<s64, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const s64& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static s64 from_gpr(const u64 reg)
|
|
{
|
|
return static_cast<s64>(reg);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct cast_ppu_gpr<bool, false>
|
|
{
|
|
__forceinline static u64 to_gpr(const bool& value)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
__forceinline static bool from_gpr(const u64& reg)
|
|
{
|
|
return reinterpret_cast<const bool&>(reg);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
__forceinline u64 cast_to_ppu_gpr(const T& value)
|
|
{
|
|
return cast_ppu_gpr<T>::to_gpr(value);
|
|
}
|
|
|
|
template<typename T>
|
|
__forceinline T cast_from_ppu_gpr(const u64 reg)
|
|
{
|
|
return cast_ppu_gpr<T>::from_gpr(reg);
|
|
}
|