mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-19 04:09:44 -06:00

Makes the enum values strongly-typed and prevents the identifiers from polluting the PowerPC namespace. This also cleans up the parameters of some functions where we were accepting an ambiguous int type and expecting the correct values to be passed in. Now those parameters accept a PowerPC::CPUCore type only, making it immediately obvious which values should be passed in. It also turns out we were storing these core types into other structures as plain ints, which have also been corrected. As this type is used directly with the configuration code, we need to provide our own overloaded insertion (<<) and extraction (>>) operators in order to make it compatible with it. These are fairly trivial to implement, so there's no issue here. A minor adjustment to TryParse() was required, as our generic function was doing the following: N tmp = 0; which is problematic, as custom types may not be able to have that assignment performed (e.g. strongly-typed enums), so we change this to: N tmp; which is sufficient, as the value is attempted to be initialized immediately under that statement.
345 lines
9.6 KiB
C++
345 lines
9.6 KiB
C++
// Copyright 2008 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <iosfwd>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
|
|
#include "Core/Debugger/PPCDebugInterface.h"
|
|
#include "Core/PowerPC/BreakPoints.h"
|
|
#include "Core/PowerPC/Gekko.h"
|
|
#include "Core/PowerPC/PPCCache.h"
|
|
|
|
class CPUCoreBase;
|
|
class PointerWrap;
|
|
|
|
namespace PowerPC
|
|
{
|
|
// The gaps in the CPUCore numbering are from cores that only existed in the past.
|
|
// We avoid re-numbering cores so that settings will be compatible across versions.
|
|
enum class CPUCore
|
|
{
|
|
Interpreter = 0,
|
|
JIT64 = 1,
|
|
JITARM64 = 4,
|
|
CachedInterpreter = 5,
|
|
};
|
|
|
|
// For reading from and writing to our config.
|
|
std::istream& operator>>(std::istream& is, CPUCore& core);
|
|
std::ostream& operator<<(std::ostream& os, CPUCore core);
|
|
|
|
enum class CoreMode
|
|
{
|
|
Interpreter,
|
|
JIT,
|
|
};
|
|
|
|
// TLB cache
|
|
constexpr size_t TLB_SIZE = 128;
|
|
constexpr size_t NUM_TLBS = 2;
|
|
constexpr size_t TLB_WAYS = 2;
|
|
|
|
struct TLBEntry
|
|
{
|
|
static constexpr u32 INVALID_TAG = 0xffffffff;
|
|
|
|
u32 tag[TLB_WAYS] = {INVALID_TAG, INVALID_TAG};
|
|
u32 paddr[TLB_WAYS] = {};
|
|
u32 pte[TLB_WAYS] = {};
|
|
u8 recent = 0;
|
|
};
|
|
|
|
// This contains the entire state of the emulated PowerPC "Gekko" CPU.
|
|
struct PowerPCState
|
|
{
|
|
u32 gpr[32]; // General purpose registers. r1 = stack pointer.
|
|
|
|
u32 pc; // program counter
|
|
u32 npc;
|
|
|
|
// Optimized CR implementation. Instead of storing CR in its PowerPC format
|
|
// (4 bit value, SO/EQ/LT/GT), we store instead a 64 bit value for each of
|
|
// the 8 CR register parts. This 64 bit value follows this format:
|
|
// - SO iff. bit 61 is set
|
|
// - EQ iff. lower 32 bits == 0
|
|
// - GT iff. (s64)cr_val > 0
|
|
// - LT iff. bit 62 is set
|
|
//
|
|
// This has the interesting property that sign-extending the result of an
|
|
// operation from 32 to 64 bits results in a 64 bit value that works as a
|
|
// CR value. Checking each part of CR is also fast, as it is equivalent to
|
|
// testing one bit or the low 32 bit part of a register. And CR can still
|
|
// be manipulated bit by bit fairly easily.
|
|
u64 cr_val[8];
|
|
|
|
UReg_MSR msr; // machine state register
|
|
UReg_FPSCR fpscr; // floating point flags/status bits
|
|
|
|
// Exception management.
|
|
u32 Exceptions;
|
|
|
|
// Downcount for determining when we need to do timing
|
|
// This isn't quite the right location for it, but it is here to accelerate the ARM JIT
|
|
// This variable should be inside of the CoreTiming namespace if we wanted to be correct.
|
|
int downcount;
|
|
|
|
// XER, reformatted into byte fields for easier access.
|
|
u8 xer_ca;
|
|
u8 xer_so_ov; // format: (SO << 1) | OV
|
|
// The Broadway CPU implements bits 16-23 of the XER register... even though it doesn't support
|
|
// lscbx
|
|
u16 xer_stringctrl;
|
|
|
|
// gather pipe pointer for JIT access
|
|
u8* gather_pipe_ptr;
|
|
u8* gather_pipe_base_ptr;
|
|
|
|
#if _M_X86_64
|
|
// This member exists for the purpose of an assertion in x86 JitBase.cpp
|
|
// that its offset <= 0x100. To minimize code size on x86, we want as much
|
|
// useful stuff in the one-byte offset range as possible - which is why ps
|
|
// is sitting down here. It currently doesn't make a difference on other
|
|
// supported architectures.
|
|
std::tuple<> above_fits_in_first_0x100;
|
|
#endif
|
|
|
|
// The paired singles are strange : PS0 is stored in the full 64 bits of each FPR
|
|
// but ps calculations are only done in 32-bit precision, and PS1 is only 32 bits.
|
|
// Since we want to use SIMD, SSE2 is the only viable alternative - 2x double.
|
|
alignas(16) u64 ps[32][2];
|
|
|
|
u32 sr[16]; // Segment registers.
|
|
|
|
// special purpose registers - controls quantizers, DMA, and lots of other misc extensions.
|
|
// also for power management, but we don't care about that.
|
|
u32 spr[1024];
|
|
|
|
// Storage for the stack pointer of the BLR optimization.
|
|
u8* stored_stack_pointer;
|
|
|
|
std::array<std::array<TLBEntry, TLB_SIZE / TLB_WAYS>, NUM_TLBS> tlb;
|
|
|
|
u32 pagetable_base;
|
|
u32 pagetable_hashmask;
|
|
|
|
InstructionCache iCache;
|
|
};
|
|
|
|
#if _M_X86_64
|
|
static_assert(offsetof(PowerPC::PowerPCState, above_fits_in_first_0x100) <= 0x100,
|
|
"top of PowerPCState too big");
|
|
#endif
|
|
|
|
extern PowerPCState ppcState;
|
|
|
|
extern BreakPoints breakpoints;
|
|
extern MemChecks memchecks;
|
|
extern PPCDebugInterface debug_interface;
|
|
|
|
const std::vector<CPUCore>& AvailableCPUCores();
|
|
CPUCore DefaultCPUCore();
|
|
|
|
void Init(CPUCore cpu_core);
|
|
void Reset();
|
|
void Shutdown();
|
|
void DoState(PointerWrap& p);
|
|
void ScheduleInvalidateCacheThreadSafe(u32 address);
|
|
|
|
CoreMode GetMode();
|
|
// [NOT THREADSAFE] CPU Thread or CPU::PauseAndLock or Core::State::Uninitialized
|
|
void SetMode(CoreMode _coreType);
|
|
const char* GetCPUName();
|
|
|
|
// Set the current CPU Core to the given implementation until removed.
|
|
// Remove the current injected CPU Core by passing nullptr.
|
|
// While an external CPUCoreBase is injected, GetMode() will return CoreMode::Interpreter.
|
|
// Init() will be called when added and Shutdown() when removed.
|
|
// [Threadsafety: Same as SetMode(), except it cannot be called from inside the CPU
|
|
// run loop on the CPU Thread - it doesn't make sense for a CPU to remove itself
|
|
// while it is in State::Running]
|
|
void InjectExternalCPUCore(CPUCoreBase* core);
|
|
|
|
// Stepping requires the CPU Execution lock (CPU::PauseAndLock or CPU Thread)
|
|
// It's not threadsafe otherwise.
|
|
void SingleStep();
|
|
void CheckExceptions();
|
|
void CheckExternalExceptions();
|
|
void CheckBreakPoints();
|
|
void RunLoop();
|
|
|
|
u32 CompactCR();
|
|
void ExpandCR(u32 cr);
|
|
|
|
void UpdatePerformanceMonitor(u32 cycles, u32 num_load_stores, u32 num_fp_inst);
|
|
|
|
// Easy register access macros.
|
|
#define HID0 ((UReg_HID0&)PowerPC::ppcState.spr[SPR_HID0])
|
|
#define HID2 ((UReg_HID2&)PowerPC::ppcState.spr[SPR_HID2])
|
|
#define HID4 ((UReg_HID4&)PowerPC::ppcState.spr[SPR_HID4])
|
|
#define DMAU (*(UReg_DMAU*)&PowerPC::ppcState.spr[SPR_DMAU])
|
|
#define DMAL (*(UReg_DMAL*)&PowerPC::ppcState.spr[SPR_DMAL])
|
|
#define MMCR0 ((UReg_MMCR0&)PowerPC::ppcState.spr[SPR_MMCR0])
|
|
#define MMCR1 ((UReg_MMCR1&)PowerPC::ppcState.spr[SPR_MMCR1])
|
|
#define PC PowerPC::ppcState.pc
|
|
#define NPC PowerPC::ppcState.npc
|
|
#define FPSCR PowerPC::ppcState.fpscr
|
|
#define MSR PowerPC::ppcState.msr
|
|
#define GPR(n) PowerPC::ppcState.gpr[n]
|
|
|
|
#define rGPR PowerPC::ppcState.gpr
|
|
#define rSPR(i) PowerPC::ppcState.spr[i]
|
|
#define LR PowerPC::ppcState.spr[SPR_LR]
|
|
#define CTR PowerPC::ppcState.spr[SPR_CTR]
|
|
#define rDEC PowerPC::ppcState.spr[SPR_DEC]
|
|
#define SRR0 PowerPC::ppcState.spr[SPR_SRR0]
|
|
#define SRR1 PowerPC::ppcState.spr[SPR_SRR1]
|
|
#define SPRG0 PowerPC::ppcState.spr[SPR_SPRG0]
|
|
#define SPRG1 PowerPC::ppcState.spr[SPR_SPRG1]
|
|
#define SPRG2 PowerPC::ppcState.spr[SPR_SPRG2]
|
|
#define SPRG3 PowerPC::ppcState.spr[SPR_SPRG3]
|
|
#define GQR(x) PowerPC::ppcState.spr[SPR_GQR0 + (x)]
|
|
#define TL PowerPC::ppcState.spr[SPR_TL]
|
|
#define TU PowerPC::ppcState.spr[SPR_TU]
|
|
|
|
#define rPS0(i) (*(double*)(&PowerPC::ppcState.ps[i][0]))
|
|
#define rPS1(i) (*(double*)(&PowerPC::ppcState.ps[i][1]))
|
|
|
|
#define riPS0(i) (*(u64*)(&PowerPC::ppcState.ps[i][0]))
|
|
#define riPS1(i) (*(u64*)(&PowerPC::ppcState.ps[i][1]))
|
|
|
|
enum CRBits
|
|
{
|
|
CR_SO = 1,
|
|
CR_EQ = 2,
|
|
CR_GT = 4,
|
|
CR_LT = 8,
|
|
|
|
CR_SO_BIT = 0,
|
|
CR_EQ_BIT = 1,
|
|
CR_GT_BIT = 2,
|
|
CR_LT_BIT = 3,
|
|
};
|
|
|
|
// Convert between PPC and internal representation of CR.
|
|
inline u64 PPCCRToInternal(u8 value)
|
|
{
|
|
u64 cr_val = 0x100000000;
|
|
cr_val |= (u64) !!(value & CR_SO) << 61;
|
|
cr_val |= (u64) !(value & CR_EQ);
|
|
cr_val |= (u64) !(value & CR_GT) << 63;
|
|
cr_val |= (u64) !!(value & CR_LT) << 62;
|
|
|
|
return cr_val;
|
|
}
|
|
|
|
// convert flags into 64-bit CR values with a lookup table
|
|
extern const std::array<u64, 16> m_crTable;
|
|
|
|
// Warning: these CR operations are fairly slow since they need to convert from
|
|
// PowerPC format (4 bit) to our internal 64 bit format. See the definition of
|
|
// ppcState.cr_val for more explanations.
|
|
inline void SetCRField(u32 cr_field, u32 value)
|
|
{
|
|
PowerPC::ppcState.cr_val[cr_field] = m_crTable[value];
|
|
}
|
|
|
|
inline u32 GetCRField(u32 cr_field)
|
|
{
|
|
const u64 cr_val = PowerPC::ppcState.cr_val[cr_field];
|
|
u32 ppc_cr = 0;
|
|
|
|
// SO
|
|
ppc_cr |= !!(cr_val & (1ull << 61));
|
|
// EQ
|
|
ppc_cr |= ((cr_val & 0xFFFFFFFF) == 0) << 1;
|
|
// GT
|
|
ppc_cr |= (static_cast<s64>(cr_val) > 0) << 2;
|
|
// LT
|
|
ppc_cr |= !!(cr_val & (1ull << 62)) << 3;
|
|
|
|
return ppc_cr;
|
|
}
|
|
|
|
inline u32 GetCRBit(u32 bit)
|
|
{
|
|
return (GetCRField(bit >> 2) >> (3 - (bit & 3))) & 1;
|
|
}
|
|
|
|
inline void SetCRBit(u32 bit, u32 value)
|
|
{
|
|
if (value & 1)
|
|
SetCRField(bit >> 2, GetCRField(bit >> 2) | (0x8 >> (bit & 3)));
|
|
else
|
|
SetCRField(bit >> 2, GetCRField(bit >> 2) & ~(0x8 >> (bit & 3)));
|
|
}
|
|
|
|
// SetCR and GetCR are fairly slow. Should be avoided if possible.
|
|
inline void SetCR(u32 new_cr)
|
|
{
|
|
PowerPC::ExpandCR(new_cr);
|
|
}
|
|
|
|
inline u32 GetCR()
|
|
{
|
|
return PowerPC::CompactCR();
|
|
}
|
|
|
|
inline void SetCarry(u32 ca)
|
|
{
|
|
PowerPC::ppcState.xer_ca = ca;
|
|
}
|
|
|
|
inline u32 GetCarry()
|
|
{
|
|
return PowerPC::ppcState.xer_ca;
|
|
}
|
|
|
|
inline UReg_XER GetXER()
|
|
{
|
|
u32 xer = 0;
|
|
xer |= PowerPC::ppcState.xer_stringctrl;
|
|
xer |= PowerPC::ppcState.xer_ca << XER_CA_SHIFT;
|
|
xer |= PowerPC::ppcState.xer_so_ov << XER_OV_SHIFT;
|
|
return UReg_XER{xer};
|
|
}
|
|
|
|
inline void SetXER(UReg_XER new_xer)
|
|
{
|
|
PowerPC::ppcState.xer_stringctrl = new_xer.BYTE_COUNT + (new_xer.BYTE_CMP << 8);
|
|
PowerPC::ppcState.xer_ca = new_xer.CA;
|
|
PowerPC::ppcState.xer_so_ov = (new_xer.SO << 1) + new_xer.OV;
|
|
}
|
|
|
|
inline u32 GetXER_SO()
|
|
{
|
|
return PowerPC::ppcState.xer_so_ov >> 1;
|
|
}
|
|
|
|
inline void SetXER_SO(bool value)
|
|
{
|
|
PowerPC::ppcState.xer_so_ov |= static_cast<u32>(value) << 1;
|
|
}
|
|
|
|
inline u32 GetXER_OV()
|
|
{
|
|
return PowerPC::ppcState.xer_so_ov & 1;
|
|
}
|
|
|
|
inline void SetXER_OV(bool value)
|
|
{
|
|
PowerPC::ppcState.xer_so_ov = (PowerPC::ppcState.xer_so_ov & 0xFE) | static_cast<u32>(value);
|
|
SetXER_SO(value);
|
|
}
|
|
|
|
void UpdateFPRF(double dvalue);
|
|
|
|
} // namespace PowerPC
|