2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2021-07-05 03:22:19 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2017-02-08 04:27:04 -05:00
|
|
|
#include "Core/PowerPC/PowerPC.h"
|
|
|
|
|
2018-12-14 13:23:12 -05:00
|
|
|
#include <algorithm>
|
2017-03-02 12:15:02 -05:00
|
|
|
#include <cstring>
|
2018-06-15 08:11:18 -04:00
|
|
|
#include <istream>
|
|
|
|
#include <ostream>
|
|
|
|
#include <type_traits>
|
2017-04-15 16:23:48 +02:00
|
|
|
#include <vector>
|
2017-03-02 12:15:02 -05:00
|
|
|
|
2016-01-10 04:04:28 -05:00
|
|
|
#include "Common/Assert.h"
|
2018-12-14 13:23:12 -05:00
|
|
|
#include "Common/BitUtils.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Common/ChunkFile.h"
|
2014-09-07 20:06:58 -05:00
|
|
|
#include "Common/CommonTypes.h"
|
2014-02-20 04:11:52 +01:00
|
|
|
#include "Common/FPURoundMode.h"
|
2018-05-07 01:18:41 -04:00
|
|
|
#include "Common/FloatUtils.h"
|
2016-01-10 04:04:28 -05:00
|
|
|
#include "Common/Logging/Log.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
|
2021-12-31 03:00:39 +01:00
|
|
|
#include "Core/Config/MainSettings.h"
|
2016-01-10 04:04:28 -05:00
|
|
|
#include "Core/ConfigManager.h"
|
2021-06-10 19:54:07 +02:00
|
|
|
#include "Core/Core.h"
|
2016-08-23 00:27:49 -04:00
|
|
|
#include "Core/CoreTiming.h"
|
2016-05-12 09:17:17 +00:00
|
|
|
#include "Core/HW/CPU.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/HW/SystemTimers.h"
|
|
|
|
#include "Core/Host.h"
|
|
|
|
#include "Core/PowerPC/CPUCoreBase.h"
|
2021-10-01 09:56:26 -04:00
|
|
|
#include "Core/PowerPC/GDBStub.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/PowerPC/Interpreter/Interpreter.h"
|
|
|
|
#include "Core/PowerPC/JitInterface.h"
|
2018-05-17 17:09:55 -04:00
|
|
|
#include "Core/PowerPC/MMU.h"
|
2020-07-06 18:16:32 -04:00
|
|
|
#include "Core/PowerPC/PPCSymbolDB.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2008-07-12 17:40:22 +00:00
|
|
|
namespace PowerPC
|
|
|
|
{
|
2009-03-23 21:19:43 +00:00
|
|
|
// STATE_TO_SAVE
|
2019-09-11 23:32:26 -04:00
|
|
|
PowerPCState ppcState;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
static CPUCoreBase* s_cpu_core_base = nullptr;
|
|
|
|
static bool s_cpu_core_base_is_injected = false;
|
|
|
|
Interpreter* const s_interpreter = Interpreter::getInstance();
|
2017-02-04 19:18:13 -05:00
|
|
|
static CoreMode s_mode = CoreMode::Interpreter;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-06-28 11:47:39 +00:00
|
|
|
BreakPoints breakpoints;
|
|
|
|
MemChecks memchecks;
|
2009-06-28 12:15:31 +00:00
|
|
|
PPCDebugInterface debug_interface;
|
2009-06-28 11:47:39 +00:00
|
|
|
|
2016-09-01 10:54:18 +00:00
|
|
|
static CoreTiming::EventType* s_invalidate_cache_thread_safe;
|
2018-12-14 13:23:12 -05:00
|
|
|
|
|
|
|
double PairedSingle::PS0AsDouble() const
|
|
|
|
{
|
|
|
|
return Common::BitCast<double>(ps0);
|
|
|
|
}
|
|
|
|
|
|
|
|
double PairedSingle::PS1AsDouble() const
|
|
|
|
{
|
|
|
|
return Common::BitCast<double>(ps1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PairedSingle::SetPS0(double value)
|
|
|
|
{
|
|
|
|
ps0 = Common::BitCast<u64>(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PairedSingle::SetPS1(double value)
|
|
|
|
{
|
|
|
|
ps1 = Common::BitCast<u64>(value);
|
|
|
|
}
|
|
|
|
|
2016-08-23 00:27:49 -04:00
|
|
|
static void InvalidateCacheThreadSafe(u64 userdata, s64 cyclesLate)
|
|
|
|
{
|
|
|
|
ppcState.iCache.Invalidate(static_cast<u32>(userdata));
|
|
|
|
}
|
|
|
|
|
2018-06-15 08:11:18 -04:00
|
|
|
std::istream& operator>>(std::istream& is, CPUCore& core)
|
|
|
|
{
|
|
|
|
std::underlying_type_t<CPUCore> val{};
|
|
|
|
|
|
|
|
if (is >> val)
|
|
|
|
{
|
|
|
|
core = static_cast<CPUCore>(val);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Upon failure, fall back to the cached interpreter
|
|
|
|
// to ensure we always initialize our core reference.
|
|
|
|
core = CPUCore::CachedInterpreter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return is;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, CPUCore core)
|
|
|
|
{
|
|
|
|
os << static_cast<std::underlying_type_t<CPUCore>>(core);
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2009-03-23 21:19:43 +00:00
|
|
|
void DoState(PointerWrap& p)
|
|
|
|
{
|
2011-12-17 17:26:38 -08:00
|
|
|
// some of this code has been disabled, because
|
|
|
|
// it changes registers even in MODE_MEASURE (which is suspicious and seems like it could cause
|
|
|
|
// desyncs)
|
|
|
|
// and because the values it's changing have been added to CoreTiming::DoState, so it might
|
|
|
|
// conflict to mess with them here.
|
|
|
|
|
|
|
|
// rSPR(SPR_DEC) = SystemTimers::GetFakeDecrementer();
|
|
|
|
// *((u64 *)&TL) = SystemTimers::GetFakeTimeBase(); //works since we are little endian and TL
|
|
|
|
// comes first :)
|
2010-09-01 23:44:03 +00:00
|
|
|
|
2017-01-18 22:52:56 -05:00
|
|
|
p.DoArray(ppcState.gpr);
|
|
|
|
p.Do(ppcState.pc);
|
|
|
|
p.Do(ppcState.npc);
|
2018-12-22 16:09:44 +01:00
|
|
|
p.DoArray(ppcState.cr.fields);
|
2017-01-18 22:52:56 -05:00
|
|
|
p.Do(ppcState.msr);
|
|
|
|
p.Do(ppcState.fpscr);
|
|
|
|
p.Do(ppcState.Exceptions);
|
|
|
|
p.Do(ppcState.downcount);
|
|
|
|
p.Do(ppcState.xer_ca);
|
|
|
|
p.Do(ppcState.xer_so_ov);
|
|
|
|
p.Do(ppcState.xer_stringctrl);
|
|
|
|
p.DoArray(ppcState.ps);
|
|
|
|
p.DoArray(ppcState.sr);
|
|
|
|
p.DoArray(ppcState.spr);
|
|
|
|
p.DoArray(ppcState.tlb);
|
|
|
|
p.Do(ppcState.pagetable_base);
|
|
|
|
p.Do(ppcState.pagetable_hashmask);
|
|
|
|
|
2021-07-13 10:36:17 +02:00
|
|
|
p.Do(ppcState.reserve);
|
|
|
|
p.Do(ppcState.reserve_address);
|
|
|
|
|
2017-01-18 22:52:56 -05:00
|
|
|
ppcState.iCache.DoState(p);
|
|
|
|
|
2016-09-27 15:43:16 +00:00
|
|
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
|
|
|
{
|
2021-06-10 19:54:07 +02:00
|
|
|
RoundingModeUpdated();
|
2016-09-27 15:43:16 +00:00
|
|
|
IBATUpdated();
|
|
|
|
DBATUpdated();
|
|
|
|
}
|
2010-09-01 23:44:03 +00:00
|
|
|
|
2011-12-17 17:26:38 -08:00
|
|
|
// SystemTimers::DecrementerSet();
|
|
|
|
// SystemTimers::TimeBaseSet();
|
2011-10-15 22:19:42 +11:00
|
|
|
|
2013-02-26 13:49:00 -06:00
|
|
|
JitInterface::DoState(p);
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2014-07-08 14:29:26 +02:00
|
|
|
static void ResetRegisters()
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2018-12-14 13:23:12 -05:00
|
|
|
std::fill(std::begin(ppcState.ps), std::end(ppcState.ps), PairedSingle{});
|
|
|
|
std::fill(std::begin(ppcState.sr), std::end(ppcState.sr), 0U);
|
|
|
|
std::fill(std::begin(ppcState.gpr), std::end(ppcState.gpr), 0U);
|
|
|
|
std::fill(std::begin(ppcState.spr), std::end(ppcState.spr), 0U);
|
|
|
|
|
2019-05-23 00:30:14 -04:00
|
|
|
// Gamecube:
|
|
|
|
// 0x00080200 = lonestar 2.0
|
|
|
|
// 0x00088202 = lonestar 2.2
|
|
|
|
// 0x70000100 = gekko 1.0
|
|
|
|
// 0x00080100 = gekko 2.0
|
|
|
|
// 0x00083203 = gekko 2.3a
|
|
|
|
// 0x00083213 = gekko 2.3b
|
|
|
|
// 0x00083204 = gekko 2.4
|
|
|
|
// 0x00083214 = gekko 2.4e (8SE) - retail HW2
|
|
|
|
// Wii:
|
|
|
|
// 0x00087102 = broadway retail hw
|
|
|
|
if (SConfig::GetInstance().bWii)
|
|
|
|
{
|
|
|
|
ppcState.spr[SPR_PVR] = 0x00087102;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ppcState.spr[SPR_PVR] = 0x00083214;
|
|
|
|
}
|
2009-12-16 08:59:18 +00:00
|
|
|
ppcState.spr[SPR_HID1] = 0x80000000; // We're running at 3x the bus clock
|
|
|
|
ppcState.spr[SPR_ECID_U] = 0x0d96e200;
|
|
|
|
ppcState.spr[SPR_ECID_M] = 0x1840c00d;
|
|
|
|
ppcState.spr[SPR_ECID_L] = 0x82bb08e8;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-05-06 18:41:01 -04:00
|
|
|
ppcState.fpscr.Hex = 0;
|
2009-03-23 21:19:43 +00:00
|
|
|
ppcState.pc = 0;
|
|
|
|
ppcState.npc = 0;
|
|
|
|
ppcState.Exceptions = 0;
|
2019-03-16 14:59:08 +01:00
|
|
|
|
|
|
|
ppcState.reserve = false;
|
|
|
|
ppcState.reserve_address = 0;
|
|
|
|
|
2018-12-22 16:09:44 +01:00
|
|
|
for (auto& v : ppcState.cr.fields)
|
|
|
|
{
|
Optimize PPC CR emulation by using magic 64 bit values
PowerPC has a 32 bit CR register, which is used to store flags for results of
computations. Most instructions have an optional bit that tells the CPU whether
the flags should be updated. This 32 bit register actually contains 8 sets of 4
flags: Summary Overflow (SO), Equals (EQ), Greater Than (GT), Less Than (LT).
These 8 sets are usually called CR0-CR7 and accessed independently. In the most
common operations, the flags are computed from the result of the operation in
the following fashion:
* EQ is set iff result == 0
* LT is set iff result < 0
* GT is set iff result > 0
* (Dolphin does not emulate SO)
While X86 architectures have a similar concept of flags, it is very difficult
to access the FLAGS register directly to translate its value to an equivalent
PowerPC value. With the current Dolphin implementation, updating a PPC CR
register requires CPU branching, which has a few performance issues: it uses
space in the BTB, and in the worst case (!GT, !LT, EQ) requires 2 branches not
taken.
After some brainstorming on IRC about how this could be improved, calc84maniac
figured out a neat trick that makes common CR operations way more efficient to
JIT on 64 bit X86 architectures. It relies on emulating each CRn bitfield with
a 64 bit register internally, whose value is the result of the operation from
which flags are updated, sign extended to 64 bits. Then, checking if a CR bit
is set can be done in the following way:
* EQ is set iff LOWER_32_BITS(cr_64b_val) == 0
* GT is set iff (s64)cr_64b_val > 0
* LT is set iff bit 62 of cr_64b_val is set
To take a few examples, if the result of an operation is:
* -1 (0xFFFFFFFFFFFFFFFF) -> lower 32 bits not 0 => !EQ
-> (s64)val (-1) is not > 0 => !GT
-> bit 62 is set => LT
!EQ, !GT, LT
* 0 (0x0000000000000000) -> lower 32 bits are 0 => EQ
-> (s64)val (0) is not > 0 => !GT
-> bit 62 is not set => !LT
EQ, !GT, !LT
* 1 (0x0000000000000001) -> lower 32 bits not 0 => !EQ
-> (s64)val (1) is > 0 => GT
-> bit 62 is not set => !LT
!EQ, GT, !LT
Sometimes we need to convert PPC CR values to these 64 bit values. The
following convention is used in this case:
* Bit 0 (LSB) is set iff !EQ
* Bit 62 is set iff LT
* Bit 63 is set iff !GT
* Bit 32 always set to disambiguize between EQ and GT
Some more examples:
* !EQ, GT, LT -> 0x4000000100000001 (!B63, B62, B32, B0)
-> lower 32 bits not 0 => !EQ
-> (s64)val is > 0 => GT
-> bit 62 is set => LT
* EQ, GT, !LT -> 0x0000000100000000
-> lower 32 bits are 0 => EQ
-> (s64)val is > 0 (note: B32) => GT
-> bit 62 is not set => !LT
2014-05-31 00:43:52 +02:00
|
|
|
v = 0x8000000000000001;
|
2018-12-22 16:09:44 +01:00
|
|
|
}
|
2019-04-27 15:38:02 +12:00
|
|
|
SetXER({});
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-06-10 19:54:07 +02:00
|
|
|
RoundingModeUpdated();
|
2016-09-27 15:43:16 +00:00
|
|
|
DBATUpdated();
|
|
|
|
IBATUpdated();
|
|
|
|
|
2009-03-23 21:19:43 +00:00
|
|
|
TL = 0;
|
|
|
|
TU = 0;
|
2010-09-01 23:44:03 +00:00
|
|
|
SystemTimers::TimeBaseSet();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2009-12-16 08:59:18 +00:00
|
|
|
// MSR should be 0x40, but we don't emulate BS1, so it would never be turned off :}
|
2018-05-05 17:51:34 -04:00
|
|
|
ppcState.msr.Hex = 0;
|
2009-03-23 21:19:43 +00:00
|
|
|
rDEC = 0xFFFFFFFF;
|
2010-09-01 23:44:03 +00:00
|
|
|
SystemTimers::DecrementerSet();
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2009-03-23 08:49:17 +00:00
|
|
|
|
2018-06-15 08:11:18 -04:00
|
|
|
static void InitializeCPUCore(CPUCore cpu_core)
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2010-08-26 17:44:13 +00:00
|
|
|
// We initialize the interpreter because
|
|
|
|
// it is used on boot and code window independently.
|
2016-05-12 09:17:17 +00:00
|
|
|
s_interpreter->Init();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-08-26 17:44:13 +00:00
|
|
|
switch (cpu_core)
|
|
|
|
{
|
2018-06-15 08:11:18 -04:00
|
|
|
case CPUCore::Interpreter:
|
2016-05-12 09:17:17 +00:00
|
|
|
s_cpu_core_base = s_interpreter;
|
2015-02-19 10:29:21 -05:00
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-19 10:29:21 -05:00
|
|
|
default:
|
2016-05-12 09:17:17 +00:00
|
|
|
s_cpu_core_base = JitInterface::InitJitCore(cpu_core);
|
|
|
|
if (!s_cpu_core_base) // Handle Situations where JIT core isn't available
|
2010-08-26 17:44:13 +00:00
|
|
|
{
|
2020-11-25 08:45:21 -05:00
|
|
|
WARN_LOG_FMT(POWERPC, "CPU core {} not available. Falling back to default.", cpu_core);
|
2018-05-26 14:19:53 +02:00
|
|
|
s_cpu_core_base = JitInterface::InitJitCore(DefaultCPUCore());
|
2010-08-26 17:44:13 +00:00
|
|
|
}
|
2013-02-26 13:49:00 -06:00
|
|
|
break;
|
2010-08-26 17:44:13 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-05-26 14:19:53 +02:00
|
|
|
s_mode = s_cpu_core_base == s_interpreter ? CoreMode::Interpreter : CoreMode::JIT;
|
2017-01-18 19:13:52 -05:00
|
|
|
}
|
|
|
|
|
2017-04-15 16:23:48 +02:00
|
|
|
const std::vector<CPUCore>& AvailableCPUCores()
|
|
|
|
{
|
|
|
|
static const std::vector<CPUCore> cpu_cores = {
|
|
|
|
#ifdef _M_X86_64
|
2018-06-15 08:11:18 -04:00
|
|
|
CPUCore::JIT64,
|
2017-04-15 16:23:48 +02:00
|
|
|
#elif defined(_M_ARM_64)
|
2018-06-15 08:11:18 -04:00
|
|
|
CPUCore::JITARM64,
|
2017-04-15 16:23:48 +02:00
|
|
|
#endif
|
2019-04-04 09:10:04 -04:00
|
|
|
CPUCore::CachedInterpreter,
|
|
|
|
CPUCore::Interpreter,
|
2017-04-15 16:23:48 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
return cpu_cores;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPUCore DefaultCPUCore()
|
|
|
|
{
|
|
|
|
#ifdef _M_X86_64
|
2018-06-15 08:11:18 -04:00
|
|
|
return CPUCore::JIT64;
|
2017-04-15 16:23:48 +02:00
|
|
|
#elif defined(_M_ARM_64)
|
2018-06-15 08:11:18 -04:00
|
|
|
return CPUCore::JITARM64;
|
2017-04-15 16:23:48 +02:00
|
|
|
#else
|
2018-06-15 08:11:18 -04:00
|
|
|
return CPUCore::CachedInterpreter;
|
2017-04-15 16:23:48 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-06-15 08:11:18 -04:00
|
|
|
void Init(CPUCore cpu_core)
|
2017-01-18 19:13:52 -05:00
|
|
|
{
|
|
|
|
s_invalidate_cache_thread_safe =
|
|
|
|
CoreTiming::RegisterEvent("invalidateEmulatedCache", InvalidateCacheThreadSafe);
|
|
|
|
|
2017-01-28 18:12:05 +01:00
|
|
|
Reset();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-01-18 19:13:52 -05:00
|
|
|
InitializeCPUCore(cpu_core);
|
2012-01-02 02:20:22 -08:00
|
|
|
ppcState.iCache.Init();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-12-31 03:00:39 +01:00
|
|
|
if (Config::Get(Config::MAIN_ENABLE_DEBUGGING))
|
2014-10-18 11:02:26 +11:00
|
|
|
breakpoints.ClearAllTemporary();
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2017-01-28 18:12:05 +01:00
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
ppcState.pagetable_base = 0;
|
|
|
|
ppcState.pagetable_hashmask = 0;
|
|
|
|
ppcState.tlb = {};
|
|
|
|
|
|
|
|
ResetRegisters();
|
|
|
|
ppcState.iCache.Reset();
|
|
|
|
}
|
|
|
|
|
2016-08-23 00:27:49 -04:00
|
|
|
void ScheduleInvalidateCacheThreadSafe(u32 address)
|
|
|
|
{
|
2017-03-28 10:55:00 -04:00
|
|
|
if (CPU::GetState() == CPU::State::Running)
|
|
|
|
{
|
2016-08-23 00:27:49 -04:00
|
|
|
CoreTiming::ScheduleEvent(0, s_invalidate_cache_thread_safe, address,
|
|
|
|
CoreTiming::FromThread::NON_CPU);
|
2017-03-28 10:55:00 -04:00
|
|
|
}
|
2016-08-23 00:27:49 -04:00
|
|
|
else
|
2017-03-28 10:55:00 -04:00
|
|
|
{
|
2016-08-23 00:27:49 -04:00
|
|
|
PowerPC::ppcState.iCache.Invalidate(static_cast<u32>(address));
|
2017-03-28 10:55:00 -04:00
|
|
|
}
|
2016-08-23 00:27:49 -04:00
|
|
|
}
|
|
|
|
|
2009-03-23 21:19:43 +00:00
|
|
|
void Shutdown()
|
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
InjectExternalCPUCore(nullptr);
|
2013-02-26 13:49:00 -06:00
|
|
|
JitInterface::Shutdown();
|
2016-05-12 09:17:17 +00:00
|
|
|
s_interpreter->Shutdown();
|
|
|
|
s_cpu_core_base = nullptr;
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2011-12-31 15:18:48 +11:00
|
|
|
CoreMode GetMode()
|
|
|
|
{
|
2017-02-04 19:18:13 -05:00
|
|
|
return !s_cpu_core_base_is_injected ? s_mode : CoreMode::Interpreter;
|
2011-12-31 15:18:48 +11:00
|
|
|
}
|
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
static void ApplyMode()
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
switch (s_mode)
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2017-02-04 19:18:13 -05:00
|
|
|
case CoreMode::Interpreter: // Switching from JIT to interpreter
|
2016-05-12 09:17:17 +00:00
|
|
|
s_cpu_core_base = s_interpreter;
|
2009-03-23 21:19:43 +00:00
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-02-04 19:18:13 -05:00
|
|
|
case CoreMode::JIT: // Switching from interpreter to JIT.
|
2009-03-23 21:19:43 +00:00
|
|
|
// Don't really need to do much. It'll work, the cache will refill itself.
|
2016-05-12 09:17:17 +00:00
|
|
|
s_cpu_core_base = JitInterface::GetCore();
|
|
|
|
if (!s_cpu_core_base) // Has a chance to not get a working JIT core if one isn't active on host
|
|
|
|
s_cpu_core_base = s_interpreter;
|
2009-03-23 21:19:43 +00:00
|
|
|
break;
|
2008-07-12 17:40:22 +00:00
|
|
|
}
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
void SetMode(CoreMode new_mode)
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
if (new_mode == s_mode)
|
|
|
|
return; // We don't need to do anything.
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
s_mode = new_mode;
|
2009-02-17 22:48:16 +00:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
// If we're using an external CPU core implementation then don't do anything.
|
|
|
|
if (s_cpu_core_base_is_injected)
|
|
|
|
return;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
ApplyMode();
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
const char* GetCPUName()
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
return s_cpu_core_base->GetName();
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
void InjectExternalCPUCore(CPUCoreBase* new_cpu)
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
// Previously injected.
|
|
|
|
if (s_cpu_core_base_is_injected)
|
|
|
|
s_cpu_core_base->Shutdown();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
// nullptr means just remove
|
|
|
|
if (!new_cpu)
|
|
|
|
{
|
|
|
|
if (s_cpu_core_base_is_injected)
|
|
|
|
{
|
|
|
|
s_cpu_core_base_is_injected = false;
|
|
|
|
ApplyMode();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
new_cpu->Init();
|
|
|
|
s_cpu_core_base = new_cpu;
|
|
|
|
s_cpu_core_base_is_injected = true;
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
void SingleStep()
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
s_cpu_core_base->SingleStep();
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
|
|
|
|
2016-05-12 09:17:17 +00:00
|
|
|
void RunLoop()
|
2015-05-10 20:56:52 -05:00
|
|
|
{
|
2016-05-12 09:17:17 +00:00
|
|
|
s_cpu_core_base->Run();
|
|
|
|
Host_UpdateDisasmDialog();
|
2015-05-10 20:56:52 -05:00
|
|
|
}
|
|
|
|
|
2018-06-19 13:06:37 -04:00
|
|
|
u64 ReadFullTimeBaseValue()
|
|
|
|
{
|
|
|
|
u64 value;
|
|
|
|
std::memcpy(&value, &TL, sizeof(value));
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteFullTimeBaseValue(u64 value)
|
|
|
|
{
|
|
|
|
std::memcpy(&TL, &value, sizeof(value));
|
|
|
|
}
|
|
|
|
|
2012-04-28 20:42:45 +10:00
|
|
|
void UpdatePerformanceMonitor(u32 cycles, u32 num_load_stores, u32 num_fp_inst)
|
|
|
|
{
|
|
|
|
switch (MMCR0.PMC1SELECT)
|
|
|
|
{
|
|
|
|
case 0: // No change
|
|
|
|
break;
|
|
|
|
case 1: // Processor cycles
|
|
|
|
PowerPC::ppcState.spr[SPR_PMC1] += cycles;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2012-04-28 20:42:45 +10:00
|
|
|
switch (MMCR0.PMC2SELECT)
|
|
|
|
{
|
|
|
|
case 0: // No change
|
|
|
|
break;
|
|
|
|
case 1: // Processor cycles
|
|
|
|
PowerPC::ppcState.spr[SPR_PMC2] += cycles;
|
|
|
|
break;
|
|
|
|
case 11: // Number of loads and stores completed
|
|
|
|
PowerPC::ppcState.spr[SPR_PMC2] += num_load_stores;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2012-04-28 20:42:45 +10:00
|
|
|
switch (MMCR1.PMC3SELECT)
|
|
|
|
{
|
|
|
|
case 0: // No change
|
|
|
|
break;
|
|
|
|
case 1: // Processor cycles
|
|
|
|
PowerPC::ppcState.spr[SPR_PMC3] += cycles;
|
|
|
|
break;
|
|
|
|
case 11: // Number of FPU instructions completed
|
|
|
|
PowerPC::ppcState.spr[SPR_PMC3] += num_fp_inst;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2012-04-28 20:42:45 +10:00
|
|
|
switch (MMCR1.PMC4SELECT)
|
|
|
|
{
|
|
|
|
case 0: // No change
|
|
|
|
break;
|
|
|
|
case 1: // Processor cycles
|
|
|
|
PowerPC::ppcState.spr[SPR_PMC4] += cycles;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2012-05-01 20:52:35 +10:00
|
|
|
if ((MMCR0.PMC1INTCONTROL && (PowerPC::ppcState.spr[SPR_PMC1] & 0x80000000) != 0) ||
|
|
|
|
(MMCR0.PMCINTCONTROL && (PowerPC::ppcState.spr[SPR_PMC2] & 0x80000000) != 0) ||
|
|
|
|
(MMCR0.PMCINTCONTROL && (PowerPC::ppcState.spr[SPR_PMC3] & 0x80000000) != 0) ||
|
|
|
|
(MMCR0.PMCINTCONTROL && (PowerPC::ppcState.spr[SPR_PMC4] & 0x80000000) != 0))
|
2012-04-28 20:42:45 +10:00
|
|
|
PowerPC::ppcState.Exceptions |= EXCEPTION_PERFORMANCE_MONITOR;
|
|
|
|
}
|
|
|
|
|
2009-03-23 21:19:43 +00:00
|
|
|
void CheckExceptions()
|
|
|
|
{
|
2010-09-01 22:03:51 +00:00
|
|
|
u32 exceptions = ppcState.Exceptions;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2009-04-06 14:15:02 +00:00
|
|
|
// Example procedure:
|
2018-05-05 17:02:58 -04:00
|
|
|
// Set SRR0 to either PC or NPC
|
2009-04-06 14:15:02 +00:00
|
|
|
// SRR0 = NPC;
|
2018-05-05 17:02:58 -04:00
|
|
|
//
|
|
|
|
// Save specified MSR bits
|
|
|
|
// SRR1 = MSR.Hex & 0x87C0FFFF;
|
|
|
|
//
|
|
|
|
// Copy ILE bit to LE
|
2018-05-06 14:24:56 -04:00
|
|
|
// MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
//
|
|
|
|
// Clear MSR as specified
|
|
|
|
// MSR.Hex &= ~0x04EF36; // 0x04FF36 also clears ME (only for machine check exception)
|
|
|
|
//
|
|
|
|
// Set to exception type entry point
|
2012-05-01 20:26:05 +10:00
|
|
|
// NPC = 0x00000x00;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2014-05-18 00:56:18 +02:00
|
|
|
// TODO(delroth): Exception priority is completely wrong here: depending on
|
|
|
|
// the instruction class, exceptions should be executed in a given order,
|
|
|
|
// which is very different from the one arbitrarily chosen here. See §6.1.5
|
|
|
|
// in 6xx_pem.pdf.
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-09-01 22:03:51 +00:00
|
|
|
if (exceptions & EXCEPTION_ISI)
|
2009-04-06 14:15:02 +00:00
|
|
|
{
|
|
|
|
SRR0 = NPC;
|
2010-09-11 02:34:51 +00:00
|
|
|
// Page fault occurred
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = (MSR.Hex & 0x87C0FFFF) | (1 << 30);
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000400;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_ISI");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_ISI;
|
2009-04-06 14:15:02 +00:00
|
|
|
}
|
2010-09-01 22:03:51 +00:00
|
|
|
else if (exceptions & EXCEPTION_PROGRAM)
|
2009-04-06 14:15:02 +00:00
|
|
|
{
|
|
|
|
SRR0 = PC;
|
2021-08-16 17:22:12 +02:00
|
|
|
// SRR1 was partially set by GenerateProgramException, so bitwise or is used here
|
|
|
|
SRR1 |= MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000700;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_PROGRAM");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_PROGRAM;
|
2009-04-06 14:15:02 +00:00
|
|
|
}
|
2010-09-01 22:03:51 +00:00
|
|
|
else if (exceptions & EXCEPTION_SYSCALL)
|
2009-04-06 14:15:02 +00:00
|
|
|
{
|
|
|
|
SRR0 = NPC;
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000C00;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_SYSCALL (PC={:08x})", PC);
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_SYSCALL;
|
2009-04-06 14:15:02 +00:00
|
|
|
}
|
2010-09-01 22:03:51 +00:00
|
|
|
else if (exceptions & EXCEPTION_FPU_UNAVAILABLE)
|
2009-03-23 21:19:43 +00:00
|
|
|
{
|
2014-06-07 11:30:39 +09:00
|
|
|
// This happens a lot - GameCube OS uses deferred FPU context switching
|
2009-03-23 21:19:43 +00:00
|
|
|
SRR0 = PC; // re-execute the instruction
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000800;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_FPU_UNAVAILABLE");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE;
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2015-04-23 01:22:35 -04:00
|
|
|
else if (exceptions & EXCEPTION_FAKE_MEMCHECK_HIT)
|
|
|
|
{
|
|
|
|
ppcState.Exceptions &= ~EXCEPTION_DSI & ~EXCEPTION_FAKE_MEMCHECK_HIT;
|
|
|
|
}
|
2010-09-01 22:03:51 +00:00
|
|
|
else if (exceptions & EXCEPTION_DSI)
|
2009-03-23 08:49:17 +00:00
|
|
|
{
|
2009-04-06 14:15:02 +00:00
|
|
|
SRR0 = PC;
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000300;
|
2009-04-06 14:15:02 +00:00
|
|
|
// DSISR and DAR regs are changed in GenerateDSIException()
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_DSI");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_DSI;
|
2009-04-06 14:15:02 +00:00
|
|
|
}
|
2010-09-01 22:03:51 +00:00
|
|
|
else if (exceptions & EXCEPTION_ALIGNMENT)
|
2009-03-23 08:49:17 +00:00
|
|
|
{
|
2009-04-06 14:15:02 +00:00
|
|
|
SRR0 = PC;
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000600;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2009-04-06 14:15:02 +00:00
|
|
|
// TODO crazy amount of DSISR options to check out
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_ALIGNMENT");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT;
|
2009-03-23 08:49:17 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2009-06-13 14:10:10 +00:00
|
|
|
// EXTERNAL INTERRUPT
|
2016-03-04 18:44:47 +01:00
|
|
|
else
|
2009-03-23 07:58:59 +00:00
|
|
|
{
|
2016-03-04 18:44:47 +01:00
|
|
|
CheckExternalExceptions();
|
2012-04-15 21:34:15 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckExternalExceptions()
|
|
|
|
{
|
|
|
|
u32 exceptions = ppcState.Exceptions;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2012-04-15 21:34:15 +10:00
|
|
|
// EXTERNAL INTERRUPT
|
2018-05-05 17:02:58 -04:00
|
|
|
// Handling is delayed until MSR.EE=1.
|
|
|
|
if (exceptions && MSR.EE)
|
2012-04-15 21:34:15 +10:00
|
|
|
{
|
|
|
|
if (exceptions & EXCEPTION_EXTERNAL_INT)
|
|
|
|
{
|
|
|
|
// Pokemon gets this "too early", it hasn't a handler yet
|
|
|
|
SRR0 = NPC;
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000500;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_EXTERNAL_INT");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-03-14 20:34:35 -04:00
|
|
|
DEBUG_ASSERT_MSG(POWERPC, (SRR1 & 0x02) != 0, "EXTERNAL_INT unrecoverable???");
|
2009-03-23 07:58:59 +00:00
|
|
|
}
|
2012-04-28 20:42:45 +10:00
|
|
|
else if (exceptions & EXCEPTION_PERFORMANCE_MONITOR)
|
|
|
|
{
|
|
|
|
SRR0 = NPC;
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 21:00:57 +10:00
|
|
|
PC = NPC = 0x00000F00;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_PERFORMANCE_MONITOR");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_PERFORMANCE_MONITOR;
|
2012-04-28 20:42:45 +10:00
|
|
|
}
|
2010-09-01 22:03:51 +00:00
|
|
|
else if (exceptions & EXCEPTION_DECREMENTER)
|
2009-03-23 07:58:59 +00:00
|
|
|
{
|
|
|
|
SRR0 = NPC;
|
2018-05-05 17:02:58 -04:00
|
|
|
SRR1 = MSR.Hex & 0x87C0FFFF;
|
2018-05-06 14:24:56 -04:00
|
|
|
MSR.LE = MSR.ILE;
|
2018-05-05 17:02:58 -04:00
|
|
|
MSR.Hex &= ~0x04EF36;
|
2012-05-01 20:26:05 +10:00
|
|
|
PC = NPC = 0x00000900;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "EXCEPTION_DECREMENTER");
|
2015-01-30 15:05:50 -08:00
|
|
|
ppcState.Exceptions &= ~EXCEPTION_DECREMENTER;
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
|
|
|
else
|
2009-03-23 07:58:59 +00:00
|
|
|
{
|
2018-03-14 20:34:35 -04:00
|
|
|
DEBUG_ASSERT_MSG(POWERPC, 0, "Unknown EXT interrupt: Exceptions == %08x", exceptions);
|
2020-11-25 08:45:21 -05:00
|
|
|
ERROR_LOG_FMT(POWERPC, "Unknown EXTERNAL INTERRUPT exception: Exceptions == {:08x}",
|
|
|
|
exceptions);
|
2009-03-23 07:58:59 +00:00
|
|
|
}
|
|
|
|
}
|
2009-03-23 21:19:43 +00:00
|
|
|
}
|
2009-03-23 07:58:59 +00:00
|
|
|
|
2010-07-05 02:05:47 +00:00
|
|
|
void CheckBreakPoints()
|
|
|
|
{
|
2021-02-28 08:04:50 +00:00
|
|
|
if (!PowerPC::breakpoints.IsBreakPointEnable(PC))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (PowerPC::breakpoints.IsBreakPointBreakOnHit(PC))
|
2021-09-30 23:36:11 -04:00
|
|
|
{
|
2021-02-28 08:04:50 +00:00
|
|
|
CPU::Break();
|
2021-10-01 11:15:30 -04:00
|
|
|
if (GDBStub::IsActive())
|
|
|
|
GDBStub::TakeControl();
|
2021-09-30 23:36:11 -04:00
|
|
|
}
|
2021-02-28 08:04:50 +00:00
|
|
|
if (PowerPC::breakpoints.IsBreakPointLogOnHit(PC))
|
2010-07-05 02:05:47 +00:00
|
|
|
{
|
2021-02-28 08:04:50 +00:00
|
|
|
NOTICE_LOG_FMT(MEMMAP,
|
|
|
|
"BP {:08x} {}({:08x} {:08x} {:08x} {:08x} {:08x} {:08x} {:08x} {:08x} {:08x} "
|
|
|
|
"{:08x}) LR={:08x}",
|
|
|
|
PC, g_symbolDB.GetDescription(PC), GPR(3), GPR(4), GPR(5), GPR(6), GPR(7),
|
|
|
|
GPR(8), GPR(9), GPR(10), GPR(11), GPR(12), LR);
|
2010-07-05 02:05:47 +00:00
|
|
|
}
|
2021-02-28 08:04:50 +00:00
|
|
|
if (PowerPC::breakpoints.IsTempBreakPoint(PC))
|
|
|
|
PowerPC::breakpoints.Remove(PC);
|
2010-07-05 02:05:47 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:32:24 +01:00
|
|
|
void PowerPCState::SetSR(u32 index, u32 value)
|
|
|
|
{
|
2020-11-25 08:45:21 -05:00
|
|
|
DEBUG_LOG_FMT(POWERPC, "{:08x}: MMU: Segment register {} set to {:08x}", pc, index, value);
|
2019-03-13 21:32:24 +01:00
|
|
|
sr[index] = value;
|
|
|
|
}
|
|
|
|
|
2009-06-13 22:08:01 +00:00
|
|
|
// FPSCR update functions
|
|
|
|
|
2021-06-13 14:35:04 +02:00
|
|
|
void UpdateFPRFDouble(double dvalue)
|
2009-06-13 14:10:10 +00:00
|
|
|
{
|
2018-05-07 01:18:41 -04:00
|
|
|
FPSCR.FPRF = Common::ClassifyDouble(dvalue);
|
2009-06-13 20:55:25 +00:00
|
|
|
}
|
2018-03-22 18:24:04 -04:00
|
|
|
|
2021-06-13 14:35:04 +02:00
|
|
|
void UpdateFPRFSingle(float fvalue)
|
|
|
|
{
|
|
|
|
FPSCR.FPRF = Common::ClassifyFloat(fvalue);
|
|
|
|
}
|
|
|
|
|
2021-06-10 19:54:07 +02:00
|
|
|
void RoundingModeUpdated()
|
|
|
|
{
|
|
|
|
// The rounding mode is separate for each thread, so this must run on the CPU thread
|
|
|
|
ASSERT(Core::IsCPUThread());
|
|
|
|
|
|
|
|
FPURoundMode::SetSIMDMode(FPSCR.RN, FPSCR.NI);
|
|
|
|
}
|
|
|
|
|
2018-03-22 18:24:04 -04:00
|
|
|
} // namespace PowerPC
|