Merge pull request #11303 from AdmiralCurtiss/coretiming-class
CoreTiming: Refactor to class.
This commit is contained in:
commit
44f8b8c100
|
@ -926,8 +926,9 @@ void UpdateTitle(u64 elapsed_ms)
|
||||||
// interested.
|
// interested.
|
||||||
static u64 ticks = 0;
|
static u64 ticks = 0;
|
||||||
static u64 idleTicks = 0;
|
static u64 idleTicks = 0;
|
||||||
u64 newTicks = CoreTiming::GetTicks();
|
auto& core_timing = Core::System::GetInstance().GetCoreTiming();
|
||||||
u64 newIdleTicks = CoreTiming::GetIdleTicks();
|
u64 newTicks = core_timing.GetTicks();
|
||||||
|
u64 newIdleTicks = core_timing.GetIdleTicks();
|
||||||
|
|
||||||
u64 diff = (newTicks - ticks) / 1000000;
|
u64 diff = (newTicks - ticks) / 1000000;
|
||||||
u64 idleDiff = (newIdleTicks - idleTicks) / 1000000;
|
u64 idleDiff = (newIdleTicks - idleTicks) / 1000000;
|
||||||
|
|
|
@ -26,20 +26,6 @@
|
||||||
|
|
||||||
namespace CoreTiming
|
namespace CoreTiming
|
||||||
{
|
{
|
||||||
struct EventType
|
|
||||||
{
|
|
||||||
TimedCallback callback;
|
|
||||||
const std::string* name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Event
|
|
||||||
{
|
|
||||||
s64 time;
|
|
||||||
u64 fifo_order;
|
|
||||||
u64 userdata;
|
|
||||||
EventType* type;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sort by time, unless the times are the same, in which case sort by the order added to the queue
|
// Sort by time, unless the times are the same, in which case sort by the order added to the queue
|
||||||
static bool operator>(const Event& left, const Event& right)
|
static bool operator>(const Event& left, const Event& right)
|
||||||
{
|
{
|
||||||
|
@ -52,45 +38,6 @@ static bool operator<(const Event& left, const Event& right)
|
||||||
|
|
||||||
static constexpr int MAX_SLICE_LENGTH = 20000;
|
static constexpr int MAX_SLICE_LENGTH = 20000;
|
||||||
|
|
||||||
struct CoreTimingState::Data
|
|
||||||
{
|
|
||||||
// unordered_map stores each element separately as a linked list node so pointers to elements
|
|
||||||
// remain stable regardless of rehashes/resizing.
|
|
||||||
std::unordered_map<std::string, EventType> event_types;
|
|
||||||
|
|
||||||
// STATE_TO_SAVE
|
|
||||||
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
|
||||||
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
|
||||||
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated
|
|
||||||
// by the standard adaptor class.
|
|
||||||
std::vector<Event> event_queue;
|
|
||||||
u64 event_fifo_id;
|
|
||||||
std::mutex ts_write_lock;
|
|
||||||
Common::SPSCQueue<Event, false> ts_queue;
|
|
||||||
|
|
||||||
float last_oc_factor;
|
|
||||||
|
|
||||||
s64 idled_cycles;
|
|
||||||
u32 fake_dec_start_value;
|
|
||||||
u64 fake_dec_start_ticks;
|
|
||||||
|
|
||||||
// Are we in a function that has been called from Advance()
|
|
||||||
bool is_global_timer_sane;
|
|
||||||
|
|
||||||
EventType* ev_lost = nullptr;
|
|
||||||
|
|
||||||
size_t registered_config_callback_id;
|
|
||||||
float config_oc_factor;
|
|
||||||
float config_oc_inv_factor;
|
|
||||||
bool config_sync_on_skip_idle;
|
|
||||||
};
|
|
||||||
|
|
||||||
CoreTimingState::CoreTimingState() : m_data(std::make_unique<Data>())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreTimingState::~CoreTimingState() = default;
|
|
||||||
|
|
||||||
static void EmptyTimedCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
static void EmptyTimedCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -102,109 +49,95 @@ static void EmptyTimedCallback(Core::System& system, u64 userdata, s64 cyclesLat
|
||||||
//
|
//
|
||||||
// Technically it might be more accurate to call this changing the IPC instead of the CPU speed,
|
// Technically it might be more accurate to call this changing the IPC instead of the CPU speed,
|
||||||
// but the effect is largely the same.
|
// but the effect is largely the same.
|
||||||
static int DowncountToCycles(CoreTiming::Globals& g, int downcount)
|
int CoreTimingManager::DowncountToCycles(int downcount) const
|
||||||
{
|
{
|
||||||
return static_cast<int>(downcount * g.last_OC_factor_inverted);
|
return static_cast<int>(downcount * m_globals.last_OC_factor_inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int CyclesToDowncount(CoreTiming::CoreTimingState::Data& state, int cycles)
|
int CoreTimingManager::CyclesToDowncount(int cycles) const
|
||||||
{
|
{
|
||||||
return static_cast<int>(cycles * state.last_oc_factor);
|
return static_cast<int>(cycles * m_last_oc_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback)
|
EventType* CoreTimingManager::RegisterEvent(const std::string& name, TimedCallback callback)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
|
||||||
|
|
||||||
// check for existing type with same name.
|
// check for existing type with same name.
|
||||||
// we want event type names to remain unique so that we can use them for serialization.
|
// we want event type names to remain unique so that we can use them for serialization.
|
||||||
ASSERT_MSG(POWERPC, state.event_types.find(name) == state.event_types.end(),
|
ASSERT_MSG(POWERPC, m_event_types.find(name) == m_event_types.end(),
|
||||||
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
|
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
|
||||||
"during Init to avoid breaking save states.",
|
"during Init to avoid breaking save states.",
|
||||||
name);
|
name);
|
||||||
|
|
||||||
auto info = state.event_types.emplace(name, EventType{callback, nullptr});
|
auto info = m_event_types.emplace(name, EventType{callback, nullptr});
|
||||||
EventType* event_type = &info.first->second;
|
EventType* event_type = &info.first->second;
|
||||||
event_type->name = &info.first->first;
|
event_type->name = &info.first->first;
|
||||||
return event_type;
|
return event_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterAllEvents()
|
void CoreTimingManager::UnregisterAllEvents()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
ASSERT_MSG(POWERPC, m_event_queue.empty(), "Cannot unregister events with events pending");
|
||||||
|
m_event_types.clear();
|
||||||
ASSERT_MSG(POWERPC, state.event_queue.empty(), "Cannot unregister events with events pending");
|
|
||||||
state.event_types.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
void CoreTimingManager::Init()
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
m_registered_config_callback_id = Config::AddConfigChangedCallback(
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
[this]() { Core::RunAsCPUThread([this]() { RefreshConfig(); }); });
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
state.registered_config_callback_id =
|
|
||||||
Config::AddConfigChangedCallback([]() { Core::RunAsCPUThread([]() { RefreshConfig(); }); });
|
|
||||||
RefreshConfig();
|
RefreshConfig();
|
||||||
|
|
||||||
state.last_oc_factor = state.config_oc_factor;
|
m_last_oc_factor = m_config_oc_factor;
|
||||||
g.last_OC_factor_inverted = state.config_oc_inv_factor;
|
m_globals.last_OC_factor_inverted = m_config_oc_inv_factor;
|
||||||
PowerPC::ppcState.downcount = CyclesToDowncount(state, MAX_SLICE_LENGTH);
|
PowerPC::ppcState.downcount = CyclesToDowncount(MAX_SLICE_LENGTH);
|
||||||
g.slice_length = MAX_SLICE_LENGTH;
|
m_globals.slice_length = MAX_SLICE_LENGTH;
|
||||||
g.global_timer = 0;
|
m_globals.global_timer = 0;
|
||||||
state.idled_cycles = 0;
|
m_idled_cycles = 0;
|
||||||
|
|
||||||
// The time between CoreTiming being intialized and the first call to Advance() is considered
|
// The time between CoreTiming being intialized and the first call to Advance() is considered
|
||||||
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
|
// the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
|
||||||
// executing the first PPC cycle of each slice to prepare the slice length and downcount for
|
// executing the first PPC cycle of each slice to prepare the slice length and downcount for
|
||||||
// that slice.
|
// that slice.
|
||||||
state.is_global_timer_sane = true;
|
m_is_global_timer_sane = true;
|
||||||
|
|
||||||
state.event_fifo_id = 0;
|
m_event_fifo_id = 0;
|
||||||
state.ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
|
m_ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void CoreTimingManager::Shutdown()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
std::lock_guard lk(m_ts_write_lock);
|
||||||
std::lock_guard lk(state.ts_write_lock);
|
|
||||||
MoveEvents();
|
MoveEvents();
|
||||||
ClearPendingEvents();
|
ClearPendingEvents();
|
||||||
UnregisterAllEvents();
|
UnregisterAllEvents();
|
||||||
Config::RemoveConfigChangedCallback(state.registered_config_callback_id);
|
Config::RemoveConfigChangedCallback(m_registered_config_callback_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefreshConfig()
|
void CoreTimingManager::RefreshConfig()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
m_config_oc_factor =
|
||||||
state.config_oc_factor =
|
|
||||||
Config::Get(Config::MAIN_OVERCLOCK_ENABLE) ? Config::Get(Config::MAIN_OVERCLOCK) : 1.0f;
|
Config::Get(Config::MAIN_OVERCLOCK_ENABLE) ? Config::Get(Config::MAIN_OVERCLOCK) : 1.0f;
|
||||||
state.config_oc_inv_factor = 1.0f / state.config_oc_factor;
|
m_config_oc_inv_factor = 1.0f / m_config_oc_factor;
|
||||||
state.config_sync_on_skip_idle = Config::Get(Config::MAIN_SYNC_ON_SKIP_IDLE);
|
m_config_sync_on_skip_idle = Config::Get(Config::MAIN_SYNC_ON_SKIP_IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoState(PointerWrap& p)
|
void CoreTimingManager::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
std::lock_guard lk(m_ts_write_lock);
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
p.Do(m_globals.slice_length);
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
p.Do(m_globals.global_timer);
|
||||||
|
p.Do(m_idled_cycles);
|
||||||
std::lock_guard lk(state.ts_write_lock);
|
p.Do(m_fake_dec_start_value);
|
||||||
p.Do(g.slice_length);
|
p.Do(m_fake_dec_start_ticks);
|
||||||
p.Do(g.global_timer);
|
p.Do(m_globals.fake_TB_start_value);
|
||||||
p.Do(state.idled_cycles);
|
p.Do(m_globals.fake_TB_start_ticks);
|
||||||
p.Do(state.fake_dec_start_value);
|
p.Do(m_last_oc_factor);
|
||||||
p.Do(state.fake_dec_start_ticks);
|
m_globals.last_OC_factor_inverted = 1.0f / m_last_oc_factor;
|
||||||
p.Do(g.fake_TB_start_value);
|
p.Do(m_event_fifo_id);
|
||||||
p.Do(g.fake_TB_start_ticks);
|
|
||||||
p.Do(state.last_oc_factor);
|
|
||||||
g.last_OC_factor_inverted = 1.0f / state.last_oc_factor;
|
|
||||||
p.Do(state.event_fifo_id);
|
|
||||||
|
|
||||||
p.DoMarker("CoreTimingData");
|
p.DoMarker("CoreTimingData");
|
||||||
|
|
||||||
MoveEvents();
|
MoveEvents();
|
||||||
p.DoEachElement(state.event_queue, [&state](PointerWrap& pw, Event& ev) {
|
p.DoEachElement(m_event_queue, [this](PointerWrap& pw, Event& ev) {
|
||||||
pw.Do(ev.time);
|
pw.Do(ev.time);
|
||||||
pw.Do(ev.fifo_order);
|
pw.Do(ev.fifo_order);
|
||||||
|
|
||||||
|
@ -221,8 +154,8 @@ void DoState(PointerWrap& p)
|
||||||
pw.Do(name);
|
pw.Do(name);
|
||||||
if (pw.IsReadMode())
|
if (pw.IsReadMode())
|
||||||
{
|
{
|
||||||
auto itr = state.event_types.find(name);
|
auto itr = m_event_types.find(name);
|
||||||
if (itr != state.event_types.end())
|
if (itr != m_event_types.end())
|
||||||
{
|
{
|
||||||
ev.type = &itr->second;
|
ev.type = &itr->second;
|
||||||
}
|
}
|
||||||
|
@ -231,7 +164,7 @@ void DoState(PointerWrap& p)
|
||||||
WARN_LOG_FMT(POWERPC,
|
WARN_LOG_FMT(POWERPC,
|
||||||
"Lost event from savestate because its type, \"{}\", has not been registered.",
|
"Lost event from savestate because its type, \"{}\", has not been registered.",
|
||||||
name);
|
name);
|
||||||
ev.type = state.ev_lost;
|
ev.type = m_ev_lost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -241,46 +174,37 @@ void DoState(PointerWrap& p)
|
||||||
// The exact layout of the heap in memory is implementation defined, therefore it is platform
|
// The exact layout of the heap in memory is implementation defined, therefore it is platform
|
||||||
// and library version specific.
|
// and library version specific.
|
||||||
if (p.IsReadMode())
|
if (p.IsReadMode())
|
||||||
std::make_heap(state.event_queue.begin(), state.event_queue.end(), std::greater<Event>());
|
std::make_heap(m_event_queue.begin(), m_event_queue.end(), std::greater<Event>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should only be called from the CPU thread. If you are calling
|
// This should only be called from the CPU thread. If you are calling
|
||||||
// it from any other thread, you are doing something evil
|
// it from any other thread, you are doing something evil
|
||||||
u64 GetTicks()
|
u64 CoreTimingManager::GetTicks() const
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
u64 ticks = static_cast<u64>(m_globals.global_timer);
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
if (!m_is_global_timer_sane)
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
u64 ticks = static_cast<u64>(g.global_timer);
|
|
||||||
if (!state.is_global_timer_sane)
|
|
||||||
{
|
{
|
||||||
int downcount = DowncountToCycles(g, PowerPC::ppcState.downcount);
|
int downcount = DowncountToCycles(PowerPC::ppcState.downcount);
|
||||||
ticks += g.slice_length - downcount;
|
ticks += m_globals.slice_length - downcount;
|
||||||
}
|
}
|
||||||
return ticks;
|
return ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetIdleTicks()
|
u64 CoreTimingManager::GetIdleTicks() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
return static_cast<u64>(m_idled_cycles);
|
||||||
return static_cast<u64>(state.idled_cycles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearPendingEvents()
|
void CoreTimingManager::ClearPendingEvents()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
m_event_queue.clear();
|
||||||
state.event_queue.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata, FromThread from)
|
void CoreTimingManager::ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata,
|
||||||
|
FromThread from)
|
||||||
{
|
{
|
||||||
ASSERT_MSG(POWERPC, event_type, "Event type is nullptr, will crash now.");
|
ASSERT_MSG(POWERPC, event_type, "Event type is nullptr, will crash now.");
|
||||||
|
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
bool from_cpu_thread;
|
bool from_cpu_thread;
|
||||||
if (from == FromThread::ANY)
|
if (from == FromThread::ANY)
|
||||||
{
|
{
|
||||||
|
@ -299,11 +223,11 @@ void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata,
|
||||||
s64 timeout = GetTicks() + cycles_into_future;
|
s64 timeout = GetTicks() + cycles_into_future;
|
||||||
|
|
||||||
// If this event needs to be scheduled before the next advance(), force one early
|
// If this event needs to be scheduled before the next advance(), force one early
|
||||||
if (!state.is_global_timer_sane)
|
if (!m_is_global_timer_sane)
|
||||||
ForceExceptionCheck(cycles_into_future);
|
ForceExceptionCheck(cycles_into_future);
|
||||||
|
|
||||||
state.event_queue.emplace_back(Event{timeout, state.event_fifo_id++, userdata, event_type});
|
m_event_queue.emplace_back(Event{timeout, m_event_fifo_id++, userdata, event_type});
|
||||||
std::push_heap(state.event_queue.begin(), state.event_queue.end(), std::greater<Event>());
|
std::push_heap(m_event_queue.begin(), m_event_queue.end(), std::greater<Event>());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -315,93 +239,85 @@ void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata,
|
||||||
*event_type->name);
|
*event_type->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard lk(state.ts_write_lock);
|
std::lock_guard lk(m_ts_write_lock);
|
||||||
state.ts_queue.Push(Event{g.global_timer + cycles_into_future, 0, userdata, event_type});
|
m_ts_queue.Push(Event{m_globals.global_timer + cycles_into_future, 0, userdata, event_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveEvent(EventType* event_type)
|
void CoreTimingManager::RemoveEvent(EventType* event_type)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
auto itr = std::remove_if(m_event_queue.begin(), m_event_queue.end(),
|
||||||
|
|
||||||
auto itr = std::remove_if(state.event_queue.begin(), state.event_queue.end(),
|
|
||||||
[&](const Event& e) { return e.type == event_type; });
|
[&](const Event& e) { return e.type == event_type; });
|
||||||
|
|
||||||
// Removing random items breaks the invariant so we have to re-establish it.
|
// Removing random items breaks the invariant so we have to re-establish it.
|
||||||
if (itr != state.event_queue.end())
|
if (itr != m_event_queue.end())
|
||||||
{
|
{
|
||||||
state.event_queue.erase(itr, state.event_queue.end());
|
m_event_queue.erase(itr, m_event_queue.end());
|
||||||
std::make_heap(state.event_queue.begin(), state.event_queue.end(), std::greater<Event>());
|
std::make_heap(m_event_queue.begin(), m_event_queue.end(), std::greater<Event>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveAllEvents(EventType* event_type)
|
void CoreTimingManager::RemoveAllEvents(EventType* event_type)
|
||||||
{
|
{
|
||||||
MoveEvents();
|
MoveEvents();
|
||||||
RemoveEvent(event_type);
|
RemoveEvent(event_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForceExceptionCheck(s64 cycles)
|
void CoreTimingManager::ForceExceptionCheck(s64 cycles)
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
cycles = std::max<s64>(0, cycles);
|
cycles = std::max<s64>(0, cycles);
|
||||||
if (DowncountToCycles(g, PowerPC::ppcState.downcount) > cycles)
|
if (DowncountToCycles(PowerPC::ppcState.downcount) > cycles)
|
||||||
{
|
{
|
||||||
// downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int here.
|
// downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int here.
|
||||||
// Account for cycles already executed by adjusting the g.slice_length
|
// Account for cycles already executed by adjusting the m_globals.slice_length
|
||||||
g.slice_length -= DowncountToCycles(g, PowerPC::ppcState.downcount) - static_cast<int>(cycles);
|
m_globals.slice_length -=
|
||||||
PowerPC::ppcState.downcount = CyclesToDowncount(state, static_cast<int>(cycles));
|
DowncountToCycles(PowerPC::ppcState.downcount) - static_cast<int>(cycles);
|
||||||
|
PowerPC::ppcState.downcount = CyclesToDowncount(static_cast<int>(cycles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveEvents()
|
void CoreTimingManager::MoveEvents()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
for (Event ev; m_ts_queue.Pop(ev);)
|
||||||
for (Event ev; state.ts_queue.Pop(ev);)
|
|
||||||
{
|
{
|
||||||
ev.fifo_order = state.event_fifo_id++;
|
ev.fifo_order = m_event_fifo_id++;
|
||||||
state.event_queue.emplace_back(std::move(ev));
|
m_event_queue.emplace_back(std::move(ev));
|
||||||
std::push_heap(state.event_queue.begin(), state.event_queue.end(), std::greater<Event>());
|
std::push_heap(m_event_queue.begin(), m_event_queue.end(), std::greater<Event>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Advance()
|
void CoreTimingManager::Advance()
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
auto& system = Core::System::GetInstance();
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
MoveEvents();
|
MoveEvents();
|
||||||
|
|
||||||
int cyclesExecuted = g.slice_length - DowncountToCycles(g, PowerPC::ppcState.downcount);
|
int cyclesExecuted = m_globals.slice_length - DowncountToCycles(PowerPC::ppcState.downcount);
|
||||||
g.global_timer += cyclesExecuted;
|
m_globals.global_timer += cyclesExecuted;
|
||||||
state.last_oc_factor = state.config_oc_factor;
|
m_last_oc_factor = m_config_oc_factor;
|
||||||
g.last_OC_factor_inverted = state.config_oc_inv_factor;
|
m_globals.last_OC_factor_inverted = m_config_oc_inv_factor;
|
||||||
g.slice_length = MAX_SLICE_LENGTH;
|
m_globals.slice_length = MAX_SLICE_LENGTH;
|
||||||
|
|
||||||
state.is_global_timer_sane = true;
|
m_is_global_timer_sane = true;
|
||||||
|
|
||||||
while (!state.event_queue.empty() && state.event_queue.front().time <= g.global_timer)
|
while (!m_event_queue.empty() && m_event_queue.front().time <= m_globals.global_timer)
|
||||||
{
|
{
|
||||||
Event evt = std::move(state.event_queue.front());
|
Event evt = std::move(m_event_queue.front());
|
||||||
std::pop_heap(state.event_queue.begin(), state.event_queue.end(), std::greater<Event>());
|
std::pop_heap(m_event_queue.begin(), m_event_queue.end(), std::greater<Event>());
|
||||||
state.event_queue.pop_back();
|
m_event_queue.pop_back();
|
||||||
evt.type->callback(system, evt.userdata, g.global_timer - evt.time);
|
evt.type->callback(system, evt.userdata, m_globals.global_timer - evt.time);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.is_global_timer_sane = false;
|
m_is_global_timer_sane = false;
|
||||||
|
|
||||||
// Still events left (scheduled in the future)
|
// Still events left (scheduled in the future)
|
||||||
if (!state.event_queue.empty())
|
if (!m_event_queue.empty())
|
||||||
{
|
{
|
||||||
g.slice_length = static_cast<int>(
|
m_globals.slice_length = static_cast<int>(
|
||||||
std::min<s64>(state.event_queue.front().time - g.global_timer, MAX_SLICE_LENGTH));
|
std::min<s64>(m_event_queue.front().time - m_globals.global_timer, MAX_SLICE_LENGTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerPC::ppcState.downcount = CyclesToDowncount(state, g.slice_length);
|
PowerPC::ppcState.downcount = CyclesToDowncount(m_globals.slice_length);
|
||||||
|
|
||||||
// Check for any external exceptions.
|
// Check for any external exceptions.
|
||||||
// It's important to do this after processing events otherwise any exceptions will be delayed
|
// It's important to do this after processing events otherwise any exceptions will be delayed
|
||||||
|
@ -410,42 +326,30 @@ void Advance()
|
||||||
PowerPC::CheckExternalExceptions();
|
PowerPC::CheckExternalExceptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogPendingEvents()
|
void CoreTimingManager::LogPendingEvents() const
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
auto clone = m_event_queue;
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
auto clone = state.event_queue;
|
|
||||||
std::sort(clone.begin(), clone.end());
|
std::sort(clone.begin(), clone.end());
|
||||||
for (const Event& ev : clone)
|
for (const Event& ev : clone)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(POWERPC, "PENDING: Now: {} Pending: {} Type: {}", g.global_timer, ev.time,
|
INFO_LOG_FMT(POWERPC, "PENDING: Now: {} Pending: {} Type: {}", m_globals.global_timer, ev.time,
|
||||||
*ev.type->name);
|
*ev.type->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should only be called from the CPU thread after the PPC clock has changed
|
// Should only be called from the CPU thread after the PPC clock has changed
|
||||||
void AdjustEventQueueTimes(u32 new_ppc_clock, u32 old_ppc_clock)
|
void CoreTimingManager::AdjustEventQueueTimes(u32 new_ppc_clock, u32 old_ppc_clock)
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
for (Event& ev : m_event_queue)
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
for (Event& ev : state.event_queue)
|
|
||||||
{
|
{
|
||||||
const s64 ticks = (ev.time - g.global_timer) * new_ppc_clock / old_ppc_clock;
|
const s64 ticks = (ev.time - m_globals.global_timer) * new_ppc_clock / old_ppc_clock;
|
||||||
ev.time = g.global_timer + ticks;
|
ev.time = m_globals.global_timer + ticks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Idle()
|
void CoreTimingManager::Idle()
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
if (m_config_sync_on_skip_idle)
|
||||||
auto& state = system.GetCoreTimingState().GetData();
|
|
||||||
auto& g = system.GetCoreTimingGlobals();
|
|
||||||
|
|
||||||
if (state.config_sync_on_skip_idle)
|
|
||||||
{
|
{
|
||||||
// When the FIFO is processing data we must not advance because in this way
|
// When the FIFO is processing data we must not advance because in this way
|
||||||
// the VI will be desynchronized. So, We are waiting until the FIFO finish and
|
// the VI will be desynchronized. So, We are waiting until the FIFO finish and
|
||||||
|
@ -454,18 +358,16 @@ void Idle()
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerPC::UpdatePerformanceMonitor(PowerPC::ppcState.downcount, 0, 0);
|
PowerPC::UpdatePerformanceMonitor(PowerPC::ppcState.downcount, 0, 0);
|
||||||
state.idled_cycles += DowncountToCycles(g, PowerPC::ppcState.downcount);
|
m_idled_cycles += DowncountToCycles(PowerPC::ppcState.downcount);
|
||||||
PowerPC::ppcState.downcount = 0;
|
PowerPC::ppcState.downcount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetScheduledEventsSummary()
|
std::string CoreTimingManager::GetScheduledEventsSummary() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
|
||||||
|
|
||||||
std::string text = "Scheduled events\n";
|
std::string text = "Scheduled events\n";
|
||||||
text.reserve(1000);
|
text.reserve(1000);
|
||||||
|
|
||||||
auto clone = state.event_queue;
|
auto clone = m_event_queue;
|
||||||
std::sort(clone.begin(), clone.end());
|
std::sort(clone.begin(), clone.end());
|
||||||
for (const Event& ev : clone)
|
for (const Event& ev : clone)
|
||||||
{
|
{
|
||||||
|
@ -474,52 +376,54 @@ std::string GetScheduledEventsSummary()
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetFakeDecStartValue()
|
u32 CoreTimingManager::GetFakeDecStartValue() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
return m_fake_dec_start_value;
|
||||||
return state.fake_dec_start_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFakeDecStartValue(u32 val)
|
void CoreTimingManager::SetFakeDecStartValue(u32 val)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
m_fake_dec_start_value = val;
|
||||||
state.fake_dec_start_value = val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetFakeDecStartTicks()
|
u64 CoreTimingManager::GetFakeDecStartTicks() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
return m_fake_dec_start_ticks;
|
||||||
return state.fake_dec_start_ticks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFakeDecStartTicks(u64 val)
|
void CoreTimingManager::SetFakeDecStartTicks(u64 val)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetCoreTimingState().GetData();
|
m_fake_dec_start_ticks = val;
|
||||||
state.fake_dec_start_ticks = val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetFakeTBStartValue()
|
u64 CoreTimingManager::GetFakeTBStartValue() const
|
||||||
{
|
{
|
||||||
auto& g = Core::System::GetInstance().GetCoreTimingGlobals();
|
return m_globals.fake_TB_start_value;
|
||||||
return g.fake_TB_start_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFakeTBStartValue(u64 val)
|
void CoreTimingManager::SetFakeTBStartValue(u64 val)
|
||||||
{
|
{
|
||||||
auto& g = Core::System::GetInstance().GetCoreTimingGlobals();
|
m_globals.fake_TB_start_value = val;
|
||||||
g.fake_TB_start_value = val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetFakeTBStartTicks()
|
u64 CoreTimingManager::GetFakeTBStartTicks() const
|
||||||
{
|
{
|
||||||
auto& g = Core::System::GetInstance().GetCoreTimingGlobals();
|
return m_globals.fake_TB_start_ticks;
|
||||||
return g.fake_TB_start_ticks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFakeTBStartTicks(u64 val)
|
void CoreTimingManager::SetFakeTBStartTicks(u64 val)
|
||||||
{
|
{
|
||||||
auto& g = Core::System::GetInstance().GetCoreTimingGlobals();
|
m_globals.fake_TB_start_ticks = val;
|
||||||
g.fake_TB_start_ticks = val;
|
}
|
||||||
|
|
||||||
|
void GlobalAdvance()
|
||||||
|
{
|
||||||
|
Core::System::GetInstance().GetCoreTiming().Advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalIdle()
|
||||||
|
{
|
||||||
|
Core::System::GetInstance().GetCoreTiming().Idle();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CoreTiming
|
} // namespace CoreTiming
|
||||||
|
|
|
@ -16,10 +16,13 @@
|
||||||
// inside callback:
|
// inside callback:
|
||||||
// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
|
// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
|
||||||
|
|
||||||
#include <memory>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/SPSCQueue.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
|
@ -30,55 +33,31 @@ class System;
|
||||||
|
|
||||||
namespace CoreTiming
|
namespace CoreTiming
|
||||||
{
|
{
|
||||||
class CoreTimingState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CoreTimingState();
|
|
||||||
CoreTimingState(const CoreTimingState&) = delete;
|
|
||||||
CoreTimingState(CoreTimingState&&) = delete;
|
|
||||||
CoreTimingState& operator=(const CoreTimingState&) = delete;
|
|
||||||
CoreTimingState& operator=(CoreTimingState&&) = delete;
|
|
||||||
~CoreTimingState();
|
|
||||||
|
|
||||||
struct Data;
|
|
||||||
Data& GetData() { return *m_data; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Data> m_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
// These really shouldn't be global, but jit64 accesses them directly
|
// These really shouldn't be global, but jit64 accesses them directly
|
||||||
struct Globals
|
struct Globals
|
||||||
{
|
{
|
||||||
s64 global_timer;
|
s64 global_timer = 0;
|
||||||
int slice_length;
|
int slice_length = 0;
|
||||||
u64 fake_TB_start_value;
|
u64 fake_TB_start_value = 0;
|
||||||
u64 fake_TB_start_ticks;
|
u64 fake_TB_start_ticks = 0;
|
||||||
float last_OC_factor_inverted;
|
float last_OC_factor_inverted = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
|
|
||||||
// required to end slice -1 and start slice 0 before the first cycle of code is executed.
|
|
||||||
void Init();
|
|
||||||
void Shutdown();
|
|
||||||
|
|
||||||
typedef void (*TimedCallback)(Core::System& system, u64 userdata, s64 cyclesLate);
|
typedef void (*TimedCallback)(Core::System& system, u64 userdata, s64 cyclesLate);
|
||||||
|
|
||||||
// This should only be called from the CPU thread, if you are calling it any other thread, you are
|
struct EventType
|
||||||
// doing something evil
|
{
|
||||||
u64 GetTicks();
|
TimedCallback callback;
|
||||||
u64 GetIdleTicks();
|
const std::string* name;
|
||||||
|
};
|
||||||
|
|
||||||
void RefreshConfig();
|
struct Event
|
||||||
|
{
|
||||||
void DoState(PointerWrap& p);
|
s64 time;
|
||||||
|
u64 fifo_order;
|
||||||
struct EventType;
|
u64 userdata;
|
||||||
|
EventType* type;
|
||||||
// Returns the event_type identifier. if name is not unique, an existing event_type will be
|
};
|
||||||
// discarded.
|
|
||||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
|
||||||
void UnregisterAllEvents();
|
|
||||||
|
|
||||||
enum class FromThread
|
enum class FromThread
|
||||||
{
|
{
|
||||||
|
@ -89,6 +68,32 @@ enum class FromThread
|
||||||
ANY
|
ANY
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// helpers until the JIT is updated to use the instance
|
||||||
|
void GlobalAdvance();
|
||||||
|
void GlobalIdle();
|
||||||
|
|
||||||
|
class CoreTimingManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
|
||||||
|
// required to end slice -1 and start slice 0 before the first cycle of code is executed.
|
||||||
|
void Init();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
// This should only be called from the CPU thread, if you are calling it any other thread, you are
|
||||||
|
// doing something evil
|
||||||
|
u64 GetTicks() const;
|
||||||
|
u64 GetIdleTicks() const;
|
||||||
|
|
||||||
|
void RefreshConfig();
|
||||||
|
|
||||||
|
void DoState(PointerWrap& p);
|
||||||
|
|
||||||
|
// Returns the event_type identifier. if name is not unique, an existing event_type will be
|
||||||
|
// discarded.
|
||||||
|
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
||||||
|
void UnregisterAllEvents();
|
||||||
|
|
||||||
// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from savestates.
|
// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from savestates.
|
||||||
// After the first Advance, the slice lengths and the downcount will be reduced whenever an event
|
// After the first Advance, the slice lengths and the downcount will be reduced whenever an event
|
||||||
// is scheduled earlier than the current values (when scheduled from the CPU Thread only).
|
// is scheduled earlier than the current values (when scheduled from the CPU Thread only).
|
||||||
|
@ -115,21 +120,61 @@ void Idle();
|
||||||
// Clear all pending events. This should ONLY be done on exit or state load.
|
// Clear all pending events. This should ONLY be done on exit or state load.
|
||||||
void ClearPendingEvents();
|
void ClearPendingEvents();
|
||||||
|
|
||||||
void LogPendingEvents();
|
void LogPendingEvents() const;
|
||||||
|
|
||||||
std::string GetScheduledEventsSummary();
|
std::string GetScheduledEventsSummary() const;
|
||||||
|
|
||||||
void AdjustEventQueueTimes(u32 new_ppc_clock, u32 old_ppc_clock);
|
void AdjustEventQueueTimes(u32 new_ppc_clock, u32 old_ppc_clock);
|
||||||
|
|
||||||
u32 GetFakeDecStartValue();
|
u32 GetFakeDecStartValue() const;
|
||||||
void SetFakeDecStartValue(u32 val);
|
void SetFakeDecStartValue(u32 val);
|
||||||
u64 GetFakeDecStartTicks();
|
u64 GetFakeDecStartTicks() const;
|
||||||
void SetFakeDecStartTicks(u64 val);
|
void SetFakeDecStartTicks(u64 val);
|
||||||
u64 GetFakeTBStartValue();
|
u64 GetFakeTBStartValue() const;
|
||||||
void SetFakeTBStartValue(u64 val);
|
void SetFakeTBStartValue(u64 val);
|
||||||
u64 GetFakeTBStartTicks();
|
u64 GetFakeTBStartTicks() const;
|
||||||
void SetFakeTBStartTicks(u64 val);
|
void SetFakeTBStartTicks(u64 val);
|
||||||
|
|
||||||
void ForceExceptionCheck(s64 cycles);
|
void ForceExceptionCheck(s64 cycles);
|
||||||
|
|
||||||
|
// Directly accessed by the JIT.
|
||||||
|
Globals& GetGlobals() { return m_globals; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Globals m_globals;
|
||||||
|
|
||||||
|
// unordered_map stores each element separately as a linked list node so pointers to elements
|
||||||
|
// remain stable regardless of rehashes/resizing.
|
||||||
|
std::unordered_map<std::string, EventType> m_event_types;
|
||||||
|
|
||||||
|
// STATE_TO_SAVE
|
||||||
|
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
||||||
|
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
||||||
|
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated
|
||||||
|
// by the standard adaptor class.
|
||||||
|
std::vector<Event> m_event_queue;
|
||||||
|
u64 m_event_fifo_id = 0;
|
||||||
|
std::mutex m_ts_write_lock;
|
||||||
|
Common::SPSCQueue<Event, false> m_ts_queue;
|
||||||
|
|
||||||
|
float m_last_oc_factor = 0.0f;
|
||||||
|
|
||||||
|
s64 m_idled_cycles = 0;
|
||||||
|
u32 m_fake_dec_start_value = 0;
|
||||||
|
u64 m_fake_dec_start_ticks = 0;
|
||||||
|
|
||||||
|
// Are we in a function that has been called from Advance()
|
||||||
|
bool m_is_global_timer_sane = false;
|
||||||
|
|
||||||
|
EventType* m_ev_lost = nullptr;
|
||||||
|
|
||||||
|
size_t m_registered_config_callback_id = 0;
|
||||||
|
float m_config_oc_factor = 0.0f;
|
||||||
|
float m_config_oc_inv_factor = 0.0f;
|
||||||
|
bool m_config_sync_on_skip_idle = false;
|
||||||
|
|
||||||
|
int DowncountToCycles(int downcount) const;
|
||||||
|
int CyclesToDowncount(int cycles) const;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace CoreTiming
|
} // namespace CoreTiming
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "Core/DSP/Interpreter/DSPIntTables.h"
|
#include "Core/DSP/Interpreter/DSPIntTables.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace DSP::Interpreter
|
namespace DSP::Interpreter
|
||||||
{
|
{
|
||||||
|
@ -271,7 +272,7 @@ u16 Interpreter::ReadControlRegister()
|
||||||
if (SystemTimers::GetFakeTimeBase() >= state.control_reg_init_code_clear_time)
|
if (SystemTimers::GetFakeTimeBase() >= state.control_reg_init_code_clear_time)
|
||||||
state.control_reg &= ~CR_INIT_CODE;
|
state.control_reg &= ~CR_INIT_CODE;
|
||||||
else
|
else
|
||||||
CoreTiming::ForceExceptionCheck(50); // Keep checking
|
Core::System::GetInstance().GetCoreTiming().ForceExceptionCheck(50); // Keep checking
|
||||||
}
|
}
|
||||||
return state.control_reg;
|
return state.control_reg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Core/Host.h"
|
#include "Core/Host.h"
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "VideoCommon/BPMemory.h"
|
#include "VideoCommon/BPMemory.h"
|
||||||
#include "VideoCommon/CommandProcessor.h"
|
#include "VideoCommon/CommandProcessor.h"
|
||||||
#include "VideoCommon/VideoCommon.h"
|
#include "VideoCommon/VideoCommon.h"
|
||||||
|
@ -508,6 +509,8 @@ void FifoPlayer::WriteFifo(const u8* data, u32 start, u32 end)
|
||||||
u32 written = start;
|
u32 written = start;
|
||||||
u32 lastBurstEnd = end - 1;
|
u32 lastBurstEnd = end - 1;
|
||||||
|
|
||||||
|
auto& core_timing = Core::System::GetInstance().GetCoreTiming();
|
||||||
|
|
||||||
// Write up to 256 bytes at a time
|
// Write up to 256 bytes at a time
|
||||||
while (written < end)
|
while (written < end)
|
||||||
{
|
{
|
||||||
|
@ -515,8 +518,8 @@ void FifoPlayer::WriteFifo(const u8* data, u32 start, u32 end)
|
||||||
{
|
{
|
||||||
if (CPU::GetState() != CPU::State::Running)
|
if (CPU::GetState() != CPU::State::Running)
|
||||||
break;
|
break;
|
||||||
CoreTiming::Idle();
|
core_timing.Idle();
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 burstEnd = std::min(written + 255, lastBurstEnd);
|
u32 burstEnd = std::min(written + 255, lastBurstEnd);
|
||||||
|
@ -533,7 +536,7 @@ void FifoPlayer::WriteFifo(const u8* data, u32 start, u32 end)
|
||||||
m_ElapsedCycles = elapsedCycles;
|
m_ElapsedCycles = elapsedCycles;
|
||||||
|
|
||||||
PowerPC::ppcState.downcount -= cyclesUsed;
|
PowerPC::ppcState.downcount -= cyclesUsed;
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,11 +715,13 @@ void FifoPlayer::FlushWGP()
|
||||||
|
|
||||||
void FifoPlayer::WaitForGPUInactive()
|
void FifoPlayer::WaitForGPUInactive()
|
||||||
{
|
{
|
||||||
|
auto& core_timing = Core::System::GetInstance().GetCoreTiming();
|
||||||
|
|
||||||
// Sleep while the GPU is active
|
// Sleep while the GPU is active
|
||||||
while (!IsIdleSet() && CPU::GetState() != CPU::State::PowerDown)
|
while (!IsIdleSet() && CPU::GetState() != CPU::State::PowerDown)
|
||||||
{
|
{
|
||||||
CoreTiming::Idle();
|
core_timing.Idle();
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,15 +204,16 @@ static void Update(Core::System& system, u64 userdata, s64 cycles_late)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
auto& state = system.GetAudioInterfaceState().GetData();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
const u64 diff = CoreTiming::GetTicks() - state.last_cpu_time;
|
const u64 diff = core_timing.GetTicks() - state.last_cpu_time;
|
||||||
if (diff > state.cpu_cycles_per_sample)
|
if (diff > state.cpu_cycles_per_sample)
|
||||||
{
|
{
|
||||||
const u32 samples = static_cast<u32>(diff / state.cpu_cycles_per_sample);
|
const u32 samples = static_cast<u32>(diff / state.cpu_cycles_per_sample);
|
||||||
state.last_cpu_time += samples * state.cpu_cycles_per_sample;
|
state.last_cpu_time += samples * state.cpu_cycles_per_sample;
|
||||||
IncreaseSampleCount(samples);
|
IncreaseSampleCount(samples);
|
||||||
}
|
}
|
||||||
CoreTiming::ScheduleEvent(GetAIPeriod() - cycles_late, state.event_type_ai);
|
core_timing.ScheduleEvent(GetAIPeriod() - cycles_late, state.event_type_ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetAIDSampleRate(SampleRate sample_rate)
|
void SetAIDSampleRate(SampleRate sample_rate)
|
||||||
|
@ -258,7 +259,9 @@ void SetAISSampleRate(SampleRate sample_rate)
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetAudioInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetAudioInterfaceState().GetData();
|
||||||
|
|
||||||
state.control.hex = 0;
|
state.control.hex = 0;
|
||||||
SetAISSampleRate(SampleRate::AI48KHz);
|
SetAISSampleRate(SampleRate::AI48KHz);
|
||||||
|
@ -269,7 +272,7 @@ void Init()
|
||||||
|
|
||||||
state.last_cpu_time = 0;
|
state.last_cpu_time = 0;
|
||||||
|
|
||||||
state.event_type_ai = CoreTiming::RegisterEvent("AICallback", Update);
|
state.event_type_ai = core_timing.RegisterEvent("AICallback", Update);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
|
@ -285,6 +288,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
const AICR tmp_ai_ctrl(val);
|
const AICR tmp_ai_ctrl(val);
|
||||||
|
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
auto& state = system.GetAudioInterfaceState().GetData();
|
||||||
if (state.control.AIINTMSK != tmp_ai_ctrl.AIINTMSK)
|
if (state.control.AIINTMSK != tmp_ai_ctrl.AIINTMSK)
|
||||||
{
|
{
|
||||||
|
@ -321,10 +325,10 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "{} streaming audio",
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "{} streaming audio",
|
||||||
tmp_ai_ctrl.PSTAT ? "start" : "stop");
|
tmp_ai_ctrl.PSTAT ? "start" : "stop");
|
||||||
state.control.PSTAT = tmp_ai_ctrl.PSTAT;
|
state.control.PSTAT = tmp_ai_ctrl.PSTAT;
|
||||||
state.last_cpu_time = CoreTiming::GetTicks();
|
state.last_cpu_time = core_timing.GetTicks();
|
||||||
|
|
||||||
CoreTiming::RemoveEvent(state.event_type_ai);
|
core_timing.RemoveEvent(state.event_type_ai);
|
||||||
CoreTiming::ScheduleEvent(GetAIPeriod(), state.event_type_ai);
|
core_timing.ScheduleEvent(GetAIPeriod(), state.event_type_ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI Interrupt
|
// AI Interrupt
|
||||||
|
@ -340,7 +344,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Reset AIS sample counter");
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "Reset AIS sample counter");
|
||||||
state.sample_counter = 0;
|
state.sample_counter = 0;
|
||||||
|
|
||||||
state.last_cpu_time = CoreTiming::GetTicks();
|
state.last_cpu_time = core_timing.GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
|
@ -357,28 +361,30 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
|
|
||||||
mmio->Register(base | AI_SAMPLE_COUNTER, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
mmio->Register(base | AI_SAMPLE_COUNTER, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
auto& state = system.GetAudioInterfaceState().GetData();
|
||||||
const u64 cycles_streamed = IsPlaying() ?
|
const u64 cycles_streamed =
|
||||||
(CoreTiming::GetTicks() - state.last_cpu_time) :
|
IsPlaying() ? (system.GetCoreTiming().GetTicks() - state.last_cpu_time) :
|
||||||
state.last_cpu_time;
|
state.last_cpu_time;
|
||||||
return state.sample_counter +
|
return state.sample_counter +
|
||||||
static_cast<u32>(cycles_streamed / state.cpu_cycles_per_sample);
|
static_cast<u32>(cycles_streamed / state.cpu_cycles_per_sample);
|
||||||
}),
|
}),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
auto& state = system.GetAudioInterfaceState().GetData();
|
||||||
state.sample_counter = val;
|
state.sample_counter = val;
|
||||||
state.last_cpu_time = CoreTiming::GetTicks();
|
state.last_cpu_time = core_timing.GetTicks();
|
||||||
CoreTiming::RemoveEvent(state.event_type_ai);
|
core_timing.RemoveEvent(state.event_type_ai);
|
||||||
CoreTiming::ScheduleEvent(GetAIPeriod(), state.event_type_ai);
|
core_timing.ScheduleEvent(GetAIPeriod(), state.event_type_ai);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | AI_INTERRUPT_TIMING, MMIO::DirectRead<u32>(&state.interrupt_timing),
|
mmio->Register(base | AI_INTERRUPT_TIMING, MMIO::DirectRead<u32>(&state.interrupt_timing),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
auto& state = system.GetAudioInterfaceState().GetData();
|
auto& state = system.GetAudioInterfaceState().GetData();
|
||||||
DEBUG_LOG_FMT(AUDIO_INTERFACE, "AI_INTERRUPT_TIMING={:08x} at PC: {:08x}", val,
|
DEBUG_LOG_FMT(AUDIO_INTERFACE, "AI_INTERRUPT_TIMING={:08x} at PC: {:08x}", val,
|
||||||
PowerPC::ppcState.pc);
|
PowerPC::ppcState.pc);
|
||||||
state.interrupt_timing = val;
|
state.interrupt_timing = val;
|
||||||
CoreTiming::RemoveEvent(state.event_type_ai);
|
core_timing.RemoveEvent(state.event_type_ai);
|
||||||
CoreTiming::ScheduleEvent(GetAIPeriod(), state.event_type_ai);
|
core_timing.ScheduleEvent(GetAIPeriod(), state.event_type_ai);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -194,11 +194,13 @@ DSPEmulator* GetDSPEmulator()
|
||||||
|
|
||||||
void Init(bool hle)
|
void Init(bool hle)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDSPState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetDSPState().GetData();
|
||||||
Reinit(hle);
|
Reinit(hle);
|
||||||
state.event_type_generate_dsp_interrupt =
|
state.event_type_generate_dsp_interrupt =
|
||||||
CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt);
|
core_timing.RegisterEvent("DSPint", GenerateDSPInterrupt);
|
||||||
state.event_type_complete_aram = CoreTiming::RegisterEvent("ARAMint", CompleteARAM);
|
state.event_type_complete_aram = core_timing.RegisterEvent("ARAMint", CompleteARAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reinit(bool hle)
|
void Reinit(bool hle)
|
||||||
|
@ -432,7 +434,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
// TODO: need hardware tests for the timing of this interrupt.
|
// TODO: need hardware tests for the timing of this interrupt.
|
||||||
// Sky Crawlers crashes at boot if this is scheduled less than 87 cycles in the future.
|
// Sky Crawlers crashes at boot if this is scheduled less than 87 cycles in the future.
|
||||||
// Other Namco games crash too, see issue 9509. For now we will just push it to 200 cycles
|
// Other Namco games crash too, see issue 9509. For now we will just push it to 200 cycles
|
||||||
CoreTiming::ScheduleEvent(200, state.event_type_generate_dsp_interrupt, INT_AID);
|
system.GetCoreTiming().ScheduleEvent(200, state.event_type_generate_dsp_interrupt,
|
||||||
|
INT_AID);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -486,8 +489,10 @@ static void GenerateDSPInterrupt(Core::System& system, u64 DSPIntType, s64 cycle
|
||||||
// CALLED FROM DSP EMULATOR, POSSIBLY THREADED
|
// CALLED FROM DSP EMULATOR, POSSIBLY THREADED
|
||||||
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType type, int cycles_into_future)
|
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType type, int cycles_into_future)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDSPState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(cycles_into_future, state.event_type_generate_dsp_interrupt, type,
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetDSPState().GetData();
|
||||||
|
core_timing.ScheduleEvent(cycles_into_future, state.event_type_generate_dsp_interrupt, type,
|
||||||
CoreTiming::FromThread::ANY);
|
CoreTiming::FromThread::ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,13 +552,15 @@ void UpdateAudioDMA()
|
||||||
|
|
||||||
static void Do_ARAM_DMA()
|
static void Do_ARAM_DMA()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDSPState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetDSPState().GetData();
|
||||||
|
|
||||||
state.dsp_control.DMAState = 1;
|
state.dsp_control.DMAState = 1;
|
||||||
|
|
||||||
// ARAM DMA transfer rate has been measured on real hw
|
// ARAM DMA transfer rate has been measured on real hw
|
||||||
int ticksToTransfer = (state.aram_dma.Cnt.count / 32) * 246;
|
int ticksToTransfer = (state.aram_dma.Cnt.count / 32) * 246;
|
||||||
CoreTiming::ScheduleEvent(ticksToTransfer, state.event_type_complete_aram);
|
core_timing.ScheduleEvent(ticksToTransfer, state.event_type_complete_aram);
|
||||||
|
|
||||||
// Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks
|
// Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks
|
||||||
if (state.aram_dma.Cnt.dir)
|
if (state.aram_dma.Cnt.dir)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/HW/DSPHLE/UCodes/UCodes.h"
|
#include "Core/HW/DSPHLE/UCodes/UCodes.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace DSP::HLE
|
namespace DSP::HLE
|
||||||
{
|
{
|
||||||
|
@ -234,7 +235,7 @@ u16 DSPHLE::DSP_ReadControlRegister()
|
||||||
if (SystemTimers::GetFakeTimeBase() >= m_control_reg_init_code_clear_time)
|
if (SystemTimers::GetFakeTimeBase() >= m_control_reg_init_code_clear_time)
|
||||||
m_dsp_control.DSPInitCode = 0;
|
m_dsp_control.DSPInitCode = 0;
|
||||||
else
|
else
|
||||||
CoreTiming::ForceExceptionCheck(50); // Keep checking
|
Core::System::GetInstance().GetCoreTiming().ForceExceptionCheck(50); // Keep checking
|
||||||
}
|
}
|
||||||
return m_dsp_control.Hex;
|
return m_dsp_control.Hex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,7 +309,8 @@ static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process)
|
||||||
static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
|
static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
|
||||||
s64 cycles_late)
|
s64 cycles_late)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
|
|
||||||
// Actual games always set this to 48 KHz
|
// Actual games always set this to 48 KHz
|
||||||
// but let's make sure to use GetAISSampleRateDivisor()
|
// but let's make sure to use GetAISSampleRateDivisor()
|
||||||
|
@ -330,7 +331,6 @@ static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vect
|
||||||
std::vector<s16> temp_pcm(state.pending_samples * 2, 0);
|
std::vector<s16> temp_pcm(state.pending_samples * 2, 0);
|
||||||
ProcessDTKSamples(&temp_pcm, audio_data);
|
ProcessDTKSamples(&temp_pcm, audio_data);
|
||||||
|
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
SoundStream* sound_stream = system.GetSoundStream();
|
SoundStream* sound_stream = system.GetSoundStream();
|
||||||
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), state.pending_samples);
|
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), state.pending_samples);
|
||||||
|
|
||||||
|
@ -364,7 +364,7 @@ static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vect
|
||||||
{
|
{
|
||||||
// There's nothing to read, so using DVDThread is unnecessary.
|
// There's nothing to read, so using DVDThread is unnecessary.
|
||||||
u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
|
u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
|
||||||
CoreTiming::ScheduleEvent(ticks_to_dtk, state.finish_executing_command, userdata);
|
system.GetCoreTiming().ScheduleEvent(ticks_to_dtk, state.finish_executing_command, userdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +374,10 @@ void Init()
|
||||||
|
|
||||||
DVDThread::Start();
|
DVDThread::Start();
|
||||||
|
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
|
|
||||||
state.DISR.Hex = 0;
|
state.DISR.Hex = 0;
|
||||||
state.DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted
|
state.DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted
|
||||||
state.DICMDBUF[0] = 0;
|
state.DICMDBUF[0] = 0;
|
||||||
|
@ -389,15 +392,15 @@ void Init()
|
||||||
|
|
||||||
ResetDrive(false);
|
ResetDrive(false);
|
||||||
|
|
||||||
state.auto_change_disc = CoreTiming::RegisterEvent("AutoChangeDisc", AutoChangeDiscCallback);
|
state.auto_change_disc = core_timing.RegisterEvent("AutoChangeDisc", AutoChangeDiscCallback);
|
||||||
state.eject_disc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
|
state.eject_disc = core_timing.RegisterEvent("EjectDisc", EjectDiscCallback);
|
||||||
state.insert_disc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
|
state.insert_disc = core_timing.RegisterEvent("InsertDisc", InsertDiscCallback);
|
||||||
|
|
||||||
state.finish_executing_command =
|
state.finish_executing_command =
|
||||||
CoreTiming::RegisterEvent("FinishExecutingCommand", FinishExecutingCommandCallback);
|
core_timing.RegisterEvent("FinishExecutingCommand", FinishExecutingCommandCallback);
|
||||||
|
|
||||||
u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
|
u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
|
||||||
CoreTiming::ScheduleEvent(0, state.finish_executing_command, userdata);
|
core_timing.ScheduleEvent(0, state.finish_executing_command, userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the
|
// Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the
|
||||||
|
@ -557,8 +560,10 @@ static void InsertDiscCallback(Core::System& system, u64 userdata, s64 cyclesLat
|
||||||
// Must only be called on the CPU thread
|
// Must only be called on the CPU thread
|
||||||
void EjectDisc(EjectCause cause)
|
void EjectDisc(EjectCause cause)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(0, state.eject_disc);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
|
core_timing.ScheduleEvent(0, state.eject_disc);
|
||||||
if (cause == EjectCause::User)
|
if (cause == EjectCause::User)
|
||||||
ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::EjectButton] = true;
|
ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::EjectButton] = true;
|
||||||
}
|
}
|
||||||
|
@ -581,7 +586,8 @@ void ChangeDisc(const std::vector<std::string>& paths)
|
||||||
// Must only be called on the CPU thread
|
// Must only be called on the CPU thread
|
||||||
void ChangeDisc(const std::string& new_path)
|
void ChangeDisc(const std::string& new_path)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
if (!state.disc_path_to_insert.empty())
|
if (!state.disc_path_to_insert.empty())
|
||||||
{
|
{
|
||||||
PanicAlertFmtT("A disc is already about to be inserted.");
|
PanicAlertFmtT("A disc is already about to be inserted.");
|
||||||
|
@ -591,7 +597,7 @@ void ChangeDisc(const std::string& new_path)
|
||||||
EjectDisc(EjectCause::User);
|
EjectDisc(EjectCause::User);
|
||||||
|
|
||||||
state.disc_path_to_insert = new_path;
|
state.disc_path_to_insert = new_path;
|
||||||
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), state.insert_disc);
|
system.GetCoreTiming().ScheduleEvent(SystemTimers::GetTicksPerSecond(), state.insert_disc);
|
||||||
Movie::SignalDiscChange(new_path);
|
Movie::SignalDiscChange(new_path);
|
||||||
|
|
||||||
for (size_t i = 0; i < state.auto_disc_change_paths.size(); ++i)
|
for (size_t i = 0; i < state.auto_disc_change_paths.size(); ++i)
|
||||||
|
@ -724,7 +730,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base, bool is_wii)
|
||||||
|
|
||||||
static void UpdateInterrupts()
|
static void UpdateInterrupts()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
const bool set_mask = (state.DISR.DEINT & state.DISR.DEINTMASK) != 0 ||
|
const bool set_mask = (state.DISR.DEINT & state.DISR.DEINTMASK) != 0 ||
|
||||||
(state.DISR.TCINT & state.DISR.TCINTMASK) != 0 ||
|
(state.DISR.TCINT & state.DISR.TCINTMASK) != 0 ||
|
||||||
(state.DISR.BRKINT & state.DISR.BRKINTMASK) != 0 ||
|
(state.DISR.BRKINT & state.DISR.BRKINTMASK) != 0 ||
|
||||||
|
@ -733,7 +740,7 @@ static void UpdateInterrupts()
|
||||||
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DI, set_mask);
|
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DI, set_mask);
|
||||||
|
|
||||||
// Required for Summoner: A Goddess Reborn
|
// Required for Summoner: A Goddess Reborn
|
||||||
CoreTiming::ForceExceptionCheck(50);
|
system.GetCoreTiming().ForceExceptionCheck(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GenerateDIInterrupt(DIInterruptType dvd_interrupt)
|
static void GenerateDIInterrupt(DIInterruptType dvd_interrupt)
|
||||||
|
@ -876,7 +883,8 @@ static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_lengt
|
||||||
// with the userdata set to the interrupt type.
|
// with the userdata set to the interrupt type.
|
||||||
void ExecuteCommand(ReplyType reply_type)
|
void ExecuteCommand(ReplyType reply_type)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
||||||
bool command_handled_by_thread = false;
|
bool command_handled_by_thread = false;
|
||||||
|
|
||||||
|
@ -1214,7 +1222,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() &&
|
if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() &&
|
||||||
DVDThread::IsInsertedDiscRunning() && !state.auto_disc_change_paths.empty())
|
DVDThread::IsInsertedDiscRunning() && !state.auto_disc_change_paths.empty())
|
||||||
{
|
{
|
||||||
CoreTiming::ScheduleEvent(force_eject ? 0 : SystemTimers::GetTicksPerSecond() / 2,
|
system.GetCoreTiming().ScheduleEvent(force_eject ? 0 : SystemTimers::GetTicksPerSecond() / 2,
|
||||||
state.auto_change_disc);
|
state.auto_change_disc);
|
||||||
OSD::AddMessage("Changing discs automatically...", OSD::Duration::NORMAL);
|
OSD::AddMessage("Changing discs automatically...", OSD::Duration::NORMAL);
|
||||||
}
|
}
|
||||||
|
@ -1306,8 +1314,8 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
if (!command_handled_by_thread)
|
if (!command_handled_by_thread)
|
||||||
{
|
{
|
||||||
// TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
|
// TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
|
||||||
CoreTiming::ScheduleEvent(MINIMUM_COMMAND_LATENCY_US *
|
system.GetCoreTiming().ScheduleEvent(
|
||||||
(SystemTimers::GetTicksPerSecond() / 1000000),
|
MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000),
|
||||||
state.finish_executing_command,
|
state.finish_executing_command,
|
||||||
PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
|
PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
|
||||||
}
|
}
|
||||||
|
@ -1316,7 +1324,8 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
||||||
const DiscIO::Partition& partition, ReplyType reply_type)
|
const DiscIO::Partition& partition, ReplyType reply_type)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
||||||
|
|
||||||
if (state.drive_state == DriveState::ReadyNoReadsMade)
|
if (state.drive_state == DriveState::ReadyNoReadsMade)
|
||||||
|
@ -1329,8 +1338,8 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
||||||
if (!command_handled_by_thread)
|
if (!command_handled_by_thread)
|
||||||
{
|
{
|
||||||
// TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
|
// TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
|
||||||
CoreTiming::ScheduleEvent(MINIMUM_COMMAND_LATENCY_US *
|
system.GetCoreTiming().ScheduleEvent(
|
||||||
(SystemTimers::GetTicksPerSecond() / 1000000),
|
MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000),
|
||||||
state.finish_executing_command,
|
state.finish_executing_command,
|
||||||
PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
|
PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
|
||||||
}
|
}
|
||||||
|
@ -1338,7 +1347,8 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
||||||
|
|
||||||
void ForceOutOfBoundsRead(ReplyType reply_type)
|
void ForceOutOfBoundsRead(ReplyType reply_type)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetDVDInterfaceState().GetData();
|
||||||
INFO_LOG_FMT(DVDINTERFACE, "Forcing an out-of-bounds disc read.");
|
INFO_LOG_FMT(DVDINTERFACE, "Forcing an out-of-bounds disc read.");
|
||||||
|
|
||||||
if (state.drive_state == DriveState::ReadyNoReadsMade)
|
if (state.drive_state == DriveState::ReadyNoReadsMade)
|
||||||
|
@ -1348,8 +1358,8 @@ void ForceOutOfBoundsRead(ReplyType reply_type)
|
||||||
|
|
||||||
// TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
|
// TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
|
||||||
const DIInterruptType interrupt_type = DIInterruptType::DEINT;
|
const DIInterruptType interrupt_type = DIInterruptType::DEINT;
|
||||||
CoreTiming::ScheduleEvent(MINIMUM_COMMAND_LATENCY_US *
|
system.GetCoreTiming().ScheduleEvent(
|
||||||
(SystemTimers::GetTicksPerSecond() / 1000000),
|
MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000),
|
||||||
state.finish_executing_command,
|
state.finish_executing_command,
|
||||||
PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
|
PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
|
||||||
}
|
}
|
||||||
|
@ -1445,6 +1455,8 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type
|
||||||
static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition,
|
static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition,
|
||||||
u32 output_address, ReplyType reply_type)
|
u32 output_address, ReplyType reply_type)
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
auto& state = Core::System::GetInstance().GetDVDInterfaceState().GetData();
|
||||||
|
|
||||||
// The drive continues to read 1 MiB beyond the last read position when idle.
|
// The drive continues to read 1 MiB beyond the last read position when idle.
|
||||||
|
@ -1456,7 +1468,7 @@ static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& parti
|
||||||
// faster than on real hardware, and if there's too much latency in the wrong
|
// faster than on real hardware, and if there's too much latency in the wrong
|
||||||
// places, the video before the save-file select screen lags.
|
// places, the video before the save-file select screen lags.
|
||||||
|
|
||||||
const u64 current_time = CoreTiming::GetTicks();
|
const u64 current_time = core_timing.GetTicks();
|
||||||
const u32 ticks_per_second = SystemTimers::GetTicksPerSecond();
|
const u32 ticks_per_second = SystemTimers::GetTicksPerSecond();
|
||||||
const bool wii_disc = DVDThread::GetDiscType() == DiscIO::Platform::WiiDisc;
|
const bool wii_disc = DVDThread::GetDiscType() == DiscIO::Platform::WiiDisc;
|
||||||
|
|
||||||
|
@ -1581,7 +1593,7 @@ static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& parti
|
||||||
// should actually happen before reading data from the disc.
|
// should actually happen before reading data from the disc.
|
||||||
|
|
||||||
const double time_after_seek =
|
const double time_after_seek =
|
||||||
(CoreTiming::GetTicks() + ticks_until_completion) / ticks_per_second;
|
(core_timing.GetTicks() + ticks_until_completion) / ticks_per_second;
|
||||||
ticks_until_completion += ticks_per_second * DVDMath::CalculateRotationalLatency(
|
ticks_until_completion += ticks_per_second * DVDMath::CalculateRotationalLatency(
|
||||||
dvd_offset, time_after_seek, wii_disc);
|
dvd_offset, time_after_seek, wii_disc);
|
||||||
|
|
||||||
|
|
|
@ -102,9 +102,10 @@ DVDThreadState::~DVDThreadState() = default;
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetDVDThreadState().GetData();
|
||||||
|
|
||||||
state.finish_read = CoreTiming::RegisterEvent("FinishReadDVDThread", FinishRead);
|
state.finish_read = system.GetCoreTiming().RegisterEvent("FinishReadDVDThread", FinishRead);
|
||||||
|
|
||||||
state.request_queue_expanded.Reset();
|
state.request_queue_expanded.Reset();
|
||||||
state.result_queue_expanded.Reset();
|
state.result_queue_expanded.Reset();
|
||||||
|
@ -305,7 +306,9 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs
|
||||||
{
|
{
|
||||||
ASSERT(Core::IsCPUThread());
|
ASSERT(Core::IsCPUThread());
|
||||||
|
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetDVDThreadState().GetData();
|
||||||
|
|
||||||
ReadRequest request;
|
ReadRequest request;
|
||||||
|
|
||||||
|
@ -319,13 +322,13 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs
|
||||||
u64 id = state.next_id++;
|
u64 id = state.next_id++;
|
||||||
request.id = id;
|
request.id = id;
|
||||||
|
|
||||||
request.time_started_ticks = CoreTiming::GetTicks();
|
request.time_started_ticks = core_timing.GetTicks();
|
||||||
request.realtime_started_us = Common::Timer::NowUs();
|
request.realtime_started_us = Common::Timer::NowUs();
|
||||||
|
|
||||||
state.request_queue.Push(std::move(request));
|
state.request_queue.Push(std::move(request));
|
||||||
state.request_queue_expanded.Set();
|
state.request_queue_expanded.Set();
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(ticks_until_completion, state.finish_read, id);
|
core_timing.ScheduleEvent(ticks_until_completion, state.finish_read, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FinishRead(Core::System& system, u64 id, s64 cycles_late)
|
static void FinishRead(Core::System& system, u64 id, s64 cycles_late)
|
||||||
|
@ -373,7 +376,7 @@ static void FinishRead(Core::System& system, u64 id, s64 cycles_late)
|
||||||
"Emulated time including delay: {} us.",
|
"Emulated time including delay: {} us.",
|
||||||
request.realtime_done_us - request.realtime_started_us,
|
request.realtime_done_us - request.realtime_started_us,
|
||||||
Common::Timer::NowUs() - request.realtime_started_us,
|
Common::Timer::NowUs() - request.realtime_started_us,
|
||||||
(CoreTiming::GetTicks() - request.time_started_ticks) /
|
(system.GetCoreTiming().GetTicks() - request.time_started_ticks) /
|
||||||
(SystemTimers::GetTicksPerSecond() / 1000000));
|
(SystemTimers::GetTicksPerSecond() / 1000000));
|
||||||
|
|
||||||
DVDInterface::DIInterruptType interrupt;
|
DVDInterface::DIInterruptType interrupt;
|
||||||
|
|
|
@ -161,10 +161,11 @@ void Init(const Sram* override_sram)
|
||||||
SlotToEXIDevice(Slot::SP1));
|
SlotToEXIDevice(Slot::SP1));
|
||||||
state.channels[2]->AddDevice(EXIDeviceType::AD16, 0);
|
state.channels[2]->AddDevice(EXIDeviceType::AD16, 0);
|
||||||
|
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
state.event_type_change_device =
|
state.event_type_change_device =
|
||||||
CoreTiming::RegisterEvent("ChangeEXIDevice", ChangeDeviceCallback);
|
core_timing.RegisterEvent("ChangeEXIDevice", ChangeDeviceCallback);
|
||||||
state.event_type_update_interrupts =
|
state.event_type_update_interrupts =
|
||||||
CoreTiming::RegisterEvent("EXIUpdateInterrupts", UpdateInterruptsCallback);
|
core_timing.RegisterEvent("EXIUpdateInterrupts", UpdateInterruptsCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
|
@ -233,11 +234,13 @@ void ChangeDevice(u8 channel, u8 device_num, EXIDeviceType device_type,
|
||||||
CoreTiming::FromThread from_thread)
|
CoreTiming::FromThread from_thread)
|
||||||
{
|
{
|
||||||
// Let the hardware see no device for 1 second
|
// Let the hardware see no device for 1 second
|
||||||
auto& state = Core::System::GetInstance().GetExpansionInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(0, state.event_type_change_device,
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetExpansionInterfaceState().GetData();
|
||||||
|
core_timing.ScheduleEvent(0, state.event_type_change_device,
|
||||||
((u64)channel << 32) | ((u64)EXIDeviceType::None << 16) | device_num,
|
((u64)channel << 32) | ((u64)EXIDeviceType::None << 16) | device_num,
|
||||||
from_thread);
|
from_thread);
|
||||||
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), state.event_type_change_device,
|
core_timing.ScheduleEvent(SystemTimers::GetTicksPerSecond(), state.event_type_change_device,
|
||||||
((u64)channel << 32) | ((u64)device_type << 16) | device_num,
|
((u64)channel << 32) | ((u64)device_type << 16) | device_num,
|
||||||
from_thread);
|
from_thread);
|
||||||
}
|
}
|
||||||
|
@ -277,8 +280,9 @@ static void UpdateInterruptsCallback(Core::System& system, u64 userdata, s64 cyc
|
||||||
|
|
||||||
void ScheduleUpdateInterrupts(CoreTiming::FromThread from, int cycles_late)
|
void ScheduleUpdateInterrupts(CoreTiming::FromThread from, int cycles_late)
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
auto& state = Core::System::GetInstance().GetExpansionInterfaceState().GetData();
|
auto& state = Core::System::GetInstance().GetExpansionInterfaceState().GetData();
|
||||||
CoreTiming::ScheduleEvent(cycles_late, state.event_type_update_interrupts, 0, from);
|
system.GetCoreTiming().ScheduleEvent(cycles_late, state.event_type_update_interrupts, 0, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ExpansionInterface
|
} // namespace ExpansionInterface
|
||||||
|
|
|
@ -405,14 +405,16 @@ u32 CEXIIPL::GetEmulatedTime(u32 epoch)
|
||||||
ltime = Movie::GetRecordingStartTime();
|
ltime = Movie::GetRecordingStartTime();
|
||||||
|
|
||||||
// let's keep time moving forward, regardless of what it starts at
|
// let's keep time moving forward, regardless of what it starts at
|
||||||
ltime += CoreTiming::GetTicks() / SystemTimers::GetTicksPerSecond();
|
ltime +=
|
||||||
|
Core::System::GetInstance().GetCoreTiming().GetTicks() / SystemTimers::GetTicksPerSecond();
|
||||||
}
|
}
|
||||||
else if (NetPlay::IsNetPlayRunning())
|
else if (NetPlay::IsNetPlayRunning())
|
||||||
{
|
{
|
||||||
ltime = NetPlay_GetEmulatedTime();
|
ltime = NetPlay_GetEmulatedTime();
|
||||||
|
|
||||||
// let's keep time moving forward, regardless of what it starts at
|
// let's keep time moving forward, regardless of what it starts at
|
||||||
ltime += CoreTiming::GetTicks() / SystemTimers::GetTicksPerSecond();
|
ltime +=
|
||||||
|
Core::System::GetInstance().GetCoreTiming().GetTicks() / SystemTimers::GetTicksPerSecond();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -86,11 +86,13 @@ void CEXIMemoryCard::Init()
|
||||||
{
|
{
|
||||||
static_assert(s_et_cmd_done.size() == s_et_transfer_complete.size(), "Event array size differs");
|
static_assert(s_et_cmd_done.size() == s_et_transfer_complete.size(), "Event array size differs");
|
||||||
static_assert(s_et_cmd_done.size() == MEMCARD_SLOTS.size(), "Event array size differs");
|
static_assert(s_et_cmd_done.size() == MEMCARD_SLOTS.size(), "Event array size differs");
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
for (Slot slot : MEMCARD_SLOTS)
|
for (Slot slot : MEMCARD_SLOTS)
|
||||||
{
|
{
|
||||||
s_et_cmd_done[slot] = CoreTiming::RegisterEvent(
|
s_et_cmd_done[slot] = core_timing.RegisterEvent(
|
||||||
fmt::format("memcardDone{}", s_card_short_names[slot]), CmdDoneCallback);
|
fmt::format("memcardDone{}", s_card_short_names[slot]), CmdDoneCallback);
|
||||||
s_et_transfer_complete[slot] = CoreTiming::RegisterEvent(
|
s_et_transfer_complete[slot] = core_timing.RegisterEvent(
|
||||||
fmt::format("memcardTransferComplete{}", s_card_short_names[slot]),
|
fmt::format("memcardTransferComplete{}", s_card_short_names[slot]),
|
||||||
TransferCompleteCallback);
|
TransferCompleteCallback);
|
||||||
}
|
}
|
||||||
|
@ -233,8 +235,10 @@ void CEXIMemoryCard::SetupRawMemcard(u16 size_mb)
|
||||||
|
|
||||||
CEXIMemoryCard::~CEXIMemoryCard()
|
CEXIMemoryCard::~CEXIMemoryCard()
|
||||||
{
|
{
|
||||||
CoreTiming::RemoveEvent(s_et_cmd_done[m_card_slot]);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::RemoveEvent(s_et_transfer_complete[m_card_slot]);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.RemoveEvent(s_et_cmd_done[m_card_slot]);
|
||||||
|
core_timing.RemoveEvent(s_et_transfer_complete[m_card_slot]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIMemoryCard::UseDelayedTransferCompletion() const
|
bool CEXIMemoryCard::UseDelayedTransferCompletion() const
|
||||||
|
@ -265,8 +269,10 @@ void CEXIMemoryCard::TransferComplete()
|
||||||
|
|
||||||
void CEXIMemoryCard::CmdDoneLater(u64 cycles)
|
void CEXIMemoryCard::CmdDoneLater(u64 cycles)
|
||||||
{
|
{
|
||||||
CoreTiming::RemoveEvent(s_et_cmd_done[m_card_slot]);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(cycles, s_et_cmd_done[m_card_slot], static_cast<u64>(m_card_slot));
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.RemoveEvent(s_et_cmd_done[m_card_slot]);
|
||||||
|
core_timing.ScheduleEvent(cycles, s_et_cmd_done[m_card_slot], static_cast<u64>(m_card_slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIMemoryCard::SetCS(int cs)
|
void CEXIMemoryCard::SetCS(int cs)
|
||||||
|
@ -525,7 +531,8 @@ void CEXIMemoryCard::DMARead(u32 addr, u32 size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule transfer complete later based on read speed
|
// Schedule transfer complete later based on read speed
|
||||||
CoreTiming::ScheduleEvent(size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ),
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(
|
||||||
|
size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ),
|
||||||
s_et_transfer_complete[m_card_slot], static_cast<u64>(m_card_slot));
|
s_et_transfer_complete[m_card_slot], static_cast<u64>(m_card_slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,7 +548,8 @@ void CEXIMemoryCard::DMAWrite(u32 addr, u32 size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule transfer complete later based on write speed
|
// Schedule transfer complete later based on write speed
|
||||||
CoreTiming::ScheduleEvent(size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE),
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(
|
||||||
|
size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE),
|
||||||
s_et_transfer_complete[m_card_slot], static_cast<u64>(m_card_slot));
|
s_et_transfer_complete[m_card_slot], static_cast<u64>(m_card_slot));
|
||||||
}
|
}
|
||||||
} // namespace ExpansionInterface
|
} // namespace ExpansionInterface
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "Core/HW/EXI/EXI.h"
|
#include "Core/HW/EXI/EXI.h"
|
||||||
#include "Core/HW/GCPad.h"
|
#include "Core/HW/GCPad.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Objbase.h>
|
#include <Objbase.h>
|
||||||
|
@ -264,13 +265,13 @@ void CEXIMic::SetCS(int cs)
|
||||||
void CEXIMic::UpdateNextInterruptTicks()
|
void CEXIMic::UpdateNextInterruptTicks()
|
||||||
{
|
{
|
||||||
int diff = (SystemTimers::GetTicksPerSecond() / sample_rate) * buff_size_samples;
|
int diff = (SystemTimers::GetTicksPerSecond() / sample_rate) * buff_size_samples;
|
||||||
next_int_ticks = CoreTiming::GetTicks() + diff;
|
next_int_ticks = Core::System::GetInstance().GetCoreTiming().GetTicks() + diff;
|
||||||
ExpansionInterface::ScheduleUpdateInterrupts(CoreTiming::FromThread::CPU, diff);
|
ExpansionInterface::ScheduleUpdateInterrupts(CoreTiming::FromThread::CPU, diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIMic::IsInterruptSet()
|
bool CEXIMic::IsInterruptSet()
|
||||||
{
|
{
|
||||||
if (next_int_ticks && CoreTiming::GetTicks() >= next_int_ticks)
|
if (next_int_ticks && Core::System::GetInstance().GetCoreTiming().GetTicks() >= next_int_ticks)
|
||||||
{
|
{
|
||||||
if (status.is_active)
|
if (status.is_active)
|
||||||
UpdateNextInterruptTicks();
|
UpdateNextInterruptTicks();
|
||||||
|
|
|
@ -27,12 +27,13 @@
|
||||||
#include "Core/HW/WII_IPC.h"
|
#include "Core/HW/WII_IPC.h"
|
||||||
#include "Core/IOS/IOS.h"
|
#include "Core/IOS/IOS.h"
|
||||||
#include "Core/State.h"
|
#include "Core/State.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace HW
|
namespace HW
|
||||||
{
|
{
|
||||||
void Init(const Sram* override_sram)
|
void Init(const Sram* override_sram)
|
||||||
{
|
{
|
||||||
CoreTiming::Init();
|
Core::System::GetInstance().GetCoreTiming().Init();
|
||||||
SystemTimers::PreInit();
|
SystemTimers::PreInit();
|
||||||
|
|
||||||
State::Init();
|
State::Init();
|
||||||
|
@ -79,7 +80,7 @@ void Shutdown()
|
||||||
AudioInterface::Shutdown();
|
AudioInterface::Shutdown();
|
||||||
|
|
||||||
State::Shutdown();
|
State::Shutdown();
|
||||||
CoreTiming::Shutdown();
|
Core::System::GetInstance().GetCoreTiming().Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoState(PointerWrap& p)
|
void DoState(PointerWrap& p)
|
||||||
|
|
|
@ -72,11 +72,13 @@ void Init()
|
||||||
m_ResetCode = 0; // Cold reset
|
m_ResetCode = 0; // Cold reset
|
||||||
m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI;
|
m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI;
|
||||||
|
|
||||||
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
toggleResetButton = core_timing.RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
|
||||||
iosNotifyResetButton =
|
iosNotifyResetButton =
|
||||||
CoreTiming::RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback);
|
core_timing.RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback);
|
||||||
iosNotifyPowerButton =
|
iosNotifyPowerButton =
|
||||||
CoreTiming::RegisterEvent("IOSNotifyPowerButton", IOSNotifyPowerButtonCallback);
|
core_timing.RegisterEvent("IOSNotifyPowerButton", IOSNotifyPowerButtonCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
|
@ -261,9 +263,12 @@ void ResetButton_Tap()
|
||||||
{
|
{
|
||||||
if (!Core::IsRunning())
|
if (!Core::IsRunning())
|
||||||
return;
|
return;
|
||||||
CoreTiming::ScheduleEvent(0, toggleResetButton, true, CoreTiming::FromThread::ANY);
|
|
||||||
CoreTiming::ScheduleEvent(0, iosNotifyResetButton, 0, CoreTiming::FromThread::ANY);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond() / 2, toggleResetButton, false,
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.ScheduleEvent(0, toggleResetButton, true, CoreTiming::FromThread::ANY);
|
||||||
|
core_timing.ScheduleEvent(0, iosNotifyResetButton, 0, CoreTiming::FromThread::ANY);
|
||||||
|
core_timing.ScheduleEvent(SystemTimers::GetTicksPerSecond() / 2, toggleResetButton, false,
|
||||||
CoreTiming::FromThread::ANY);
|
CoreTiming::FromThread::ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +276,10 @@ void PowerButton_Tap()
|
||||||
{
|
{
|
||||||
if (!Core::IsRunning())
|
if (!Core::IsRunning())
|
||||||
return;
|
return;
|
||||||
CoreTiming::ScheduleEvent(0, iosNotifyPowerButton, 0, CoreTiming::FromThread::ANY);
|
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.ScheduleEvent(0, iosNotifyPowerButton, 0, CoreTiming::FromThread::ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ProcessorInterface
|
} // namespace ProcessorInterface
|
||||||
|
|
|
@ -344,7 +344,7 @@ static void RunSIBuffer(Core::System& system, u64 user_data, s64 cycles_late)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CoreTiming::ScheduleEvent(device->TransferInterval() - cycles_late,
|
system.GetCoreTiming().ScheduleEvent(device->TransferInterval() - cycles_late,
|
||||||
state.event_type_tranfer_pending);
|
state.event_type_tranfer_pending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,10 +388,12 @@ static void DeviceEventCallback(Core::System& system, u64 userdata, s64 cyclesLa
|
||||||
|
|
||||||
static void RegisterEvents()
|
static void RegisterEvents()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetSerialInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetSerialInterfaceState().GetData();
|
||||||
state.event_type_change_device =
|
state.event_type_change_device =
|
||||||
CoreTiming::RegisterEvent("ChangeSIDevice", ChangeDeviceCallback);
|
core_timing.RegisterEvent("ChangeSIDevice", ChangeDeviceCallback);
|
||||||
state.event_type_tranfer_pending = CoreTiming::RegisterEvent("SITransferPending", RunSIBuffer);
|
state.event_type_tranfer_pending = core_timing.RegisterEvent("SITransferPending", RunSIBuffer);
|
||||||
|
|
||||||
constexpr std::array<CoreTiming::TimedCallback, MAX_SI_CHANNELS> event_callbacks = {
|
constexpr std::array<CoreTiming::TimedCallback, MAX_SI_CHANNELS> event_callbacks = {
|
||||||
DeviceEventCallback<0>,
|
DeviceEventCallback<0>,
|
||||||
|
@ -402,20 +404,24 @@ static void RegisterEvents()
|
||||||
for (int i = 0; i < MAX_SI_CHANNELS; ++i)
|
for (int i = 0; i < MAX_SI_CHANNELS; ++i)
|
||||||
{
|
{
|
||||||
state.event_types_device[i] =
|
state.event_types_device[i] =
|
||||||
CoreTiming::RegisterEvent(fmt::format("SIEventChannel{}", i), event_callbacks[i]);
|
core_timing.RegisterEvent(fmt::format("SIEventChannel{}", i), event_callbacks[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScheduleEvent(int device_number, s64 cycles_into_future, u64 userdata)
|
void ScheduleEvent(int device_number, s64 cycles_into_future, u64 userdata)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetSerialInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(cycles_into_future, state.event_types_device[device_number], userdata);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetSerialInterfaceState().GetData();
|
||||||
|
core_timing.ScheduleEvent(cycles_into_future, state.event_types_device[device_number], userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveEvent(int device_number)
|
void RemoveEvent(int device_number)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetSerialInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::RemoveEvent(state.event_types_device[device_number]);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
auto& state = system.GetSerialInterfaceState().GetData();
|
||||||
|
core_timing.RemoveEvent(state.event_types_device[device_number]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
|
@ -573,7 +579,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
if (tmp_com_csr.TSTART)
|
if (tmp_com_csr.TSTART)
|
||||||
{
|
{
|
||||||
if (state.com_csr.TSTART)
|
if (state.com_csr.TSTART)
|
||||||
CoreTiming::RemoveEvent(state.event_type_tranfer_pending);
|
system.GetCoreTiming().RemoveEvent(state.event_type_tranfer_pending);
|
||||||
state.com_csr.TSTART = 1;
|
state.com_csr.TSTART = 1;
|
||||||
RunSIBuffer(system, 0, 0);
|
RunSIBuffer(system, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -676,7 +682,8 @@ void ChangeDevice(SIDevices device, int channel)
|
||||||
|
|
||||||
static void ChangeDeviceDeterministic(SIDevices device, int channel)
|
static void ChangeDeviceDeterministic(SIDevices device, int channel)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetSerialInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetSerialInterfaceState().GetData();
|
||||||
if (state.channel[channel].has_recent_device_change)
|
if (state.channel[channel].has_recent_device_change)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -696,8 +703,8 @@ static void ChangeDeviceDeterministic(SIDevices device, int channel)
|
||||||
|
|
||||||
// Prevent additional device changes on this channel for one second.
|
// Prevent additional device changes on this channel for one second.
|
||||||
state.channel[channel].has_recent_device_change = true;
|
state.channel[channel].has_recent_device_change = true;
|
||||||
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), state.event_type_change_device,
|
system.GetCoreTiming().ScheduleEvent(SystemTimers::GetTicksPerSecond(),
|
||||||
channel);
|
state.event_type_change_device, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateDevices()
|
void UpdateDevices()
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/HW/SI/SI_Device.h"
|
#include "Core/HW/SI/SI_Device.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace SerialInterface
|
namespace SerialInterface
|
||||||
{
|
{
|
||||||
|
@ -145,21 +146,24 @@ void GBASockServer::ClockSync()
|
||||||
if (!(m_clock_sync = GetNextClock()))
|
if (!(m_clock_sync = GetNextClock()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
u32 time_slice = 0;
|
u32 time_slice = 0;
|
||||||
|
|
||||||
if (m_last_time_slice == 0)
|
if (m_last_time_slice == 0)
|
||||||
{
|
{
|
||||||
s_num_connected++;
|
s_num_connected++;
|
||||||
m_last_time_slice = CoreTiming::GetTicks();
|
m_last_time_slice = core_timing.GetTicks();
|
||||||
time_slice = (u32)(SystemTimers::GetTicksPerSecond() / 60);
|
time_slice = (u32)(SystemTimers::GetTicksPerSecond() / 60);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
time_slice = (u32)(CoreTiming::GetTicks() - m_last_time_slice);
|
time_slice = (u32)(core_timing.GetTicks() - m_last_time_slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
time_slice = (u32)((u64)time_slice * 16777216 / SystemTimers::GetTicksPerSecond());
|
time_slice = (u32)((u64)time_slice * 16777216 / SystemTimers::GetTicksPerSecond());
|
||||||
m_last_time_slice = CoreTiming::GetTicks();
|
m_last_time_slice = core_timing.GetTicks();
|
||||||
char bytes[4] = {0, 0, 0, 0};
|
char bytes[4] = {0, 0, 0, 0};
|
||||||
bytes[0] = (time_slice >> 24) & 0xff;
|
bytes[0] = (time_slice >> 24) & 0xff;
|
||||||
bytes[1] = (time_slice >> 16) & 0xff;
|
bytes[1] = (time_slice >> 16) & 0xff;
|
||||||
|
@ -285,14 +289,15 @@ int CSIDevice_GBA::RunBuffer(u8* buffer, int request_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
m_last_cmd = static_cast<EBufferCommands>(buffer[0]);
|
m_last_cmd = static_cast<EBufferCommands>(buffer[0]);
|
||||||
m_timestamp_sent = CoreTiming::GetTicks();
|
m_timestamp_sent = Core::System::GetInstance().GetCoreTiming().GetTicks();
|
||||||
m_next_action = NextAction::WaitTransferTime;
|
m_next_action = NextAction::WaitTransferTime;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case NextAction::WaitTransferTime:
|
case NextAction::WaitTransferTime:
|
||||||
{
|
{
|
||||||
int elapsed_time = static_cast<int>(CoreTiming::GetTicks() - m_timestamp_sent);
|
int elapsed_time =
|
||||||
|
static_cast<int>(Core::System::GetInstance().GetCoreTiming().GetTicks() - m_timestamp_sent);
|
||||||
// Tell SI to ask again after TransferInterval() cycles
|
// Tell SI to ask again after TransferInterval() cycles
|
||||||
if (SIDevice_GetGBATransferTime(m_last_cmd) > elapsed_time)
|
if (SIDevice_GetGBATransferTime(m_last_cmd) > elapsed_time)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/Host.h"
|
#include "Core/Host.h"
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace SerialInterface
|
namespace SerialInterface
|
||||||
{
|
{
|
||||||
|
@ -30,7 +31,7 @@ CSIDevice_GBAEmu::CSIDevice_GBAEmu(SIDevices device, int device_number)
|
||||||
: ISIDevice(device, device_number)
|
: ISIDevice(device, device_number)
|
||||||
{
|
{
|
||||||
m_core = std::make_shared<HW::GBA::Core>(m_device_number);
|
m_core = std::make_shared<HW::GBA::Core>(m_device_number);
|
||||||
m_core->Start(CoreTiming::GetTicks());
|
m_core->Start(Core::System::GetInstance().GetCoreTiming().GetTicks());
|
||||||
m_gbahost = Host_CreateGBAHost(m_core);
|
m_gbahost = Host_CreateGBAHost(m_core);
|
||||||
m_core->SetHost(m_gbahost);
|
m_core->SetHost(m_gbahost);
|
||||||
ScheduleEvent(m_device_number, GetSyncInterval());
|
ScheduleEvent(m_device_number, GetSyncInterval());
|
||||||
|
@ -55,7 +56,7 @@ int CSIDevice_GBAEmu::RunBuffer(u8* buffer, int request_length)
|
||||||
buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
|
buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
|
||||||
#endif
|
#endif
|
||||||
m_last_cmd = static_cast<EBufferCommands>(buffer[0]);
|
m_last_cmd = static_cast<EBufferCommands>(buffer[0]);
|
||||||
m_timestamp_sent = CoreTiming::GetTicks();
|
m_timestamp_sent = Core::System::GetInstance().GetCoreTiming().GetTicks();
|
||||||
m_core->SendJoybusCommand(m_timestamp_sent, TransferInterval(), buffer, m_keys);
|
m_core->SendJoybusCommand(m_timestamp_sent, TransferInterval(), buffer, m_keys);
|
||||||
|
|
||||||
RemoveEvent(m_device_number);
|
RemoveEvent(m_device_number);
|
||||||
|
@ -74,7 +75,8 @@ int CSIDevice_GBAEmu::RunBuffer(u8* buffer, int request_length)
|
||||||
|
|
||||||
case NextAction::WaitTransferTime:
|
case NextAction::WaitTransferTime:
|
||||||
{
|
{
|
||||||
int elapsed_time = static_cast<int>(CoreTiming::GetTicks() - m_timestamp_sent);
|
int elapsed_time =
|
||||||
|
static_cast<int>(Core::System::GetInstance().GetCoreTiming().GetTicks() - m_timestamp_sent);
|
||||||
// Tell SI to ask again after TransferInterval() cycles
|
// Tell SI to ask again after TransferInterval() cycles
|
||||||
if (TransferInterval() > elapsed_time)
|
if (TransferInterval() > elapsed_time)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -162,7 +164,8 @@ void CSIDevice_GBAEmu::DoState(PointerWrap& p)
|
||||||
|
|
||||||
void CSIDevice_GBAEmu::OnEvent(u64 userdata, s64 cycles_late)
|
void CSIDevice_GBAEmu::OnEvent(u64 userdata, s64 cycles_late)
|
||||||
{
|
{
|
||||||
m_core->SendJoybusCommand(CoreTiming::GetTicks() + userdata, 0, nullptr, m_keys);
|
m_core->SendJoybusCommand(Core::System::GetInstance().GetCoreTiming().GetTicks() + userdata, 0,
|
||||||
|
nullptr, m_keys);
|
||||||
ScheduleEvent(m_device_number, userdata + GetSyncInterval());
|
ScheduleEvent(m_device_number, userdata + GetSyncInterval());
|
||||||
}
|
}
|
||||||
} // namespace SerialInterface
|
} // namespace SerialInterface
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "InputCommon/GCPadStatus.h"
|
#include "InputCommon/GCPadStatus.h"
|
||||||
|
|
||||||
namespace SerialInterface
|
namespace SerialInterface
|
||||||
|
@ -263,12 +264,12 @@ CSIDevice_GCController::HandleButtonCombos(const GCPadStatus& pad_status)
|
||||||
{
|
{
|
||||||
m_last_button_combo = temp_combo;
|
m_last_button_combo = temp_combo;
|
||||||
if (m_last_button_combo != COMBO_NONE)
|
if (m_last_button_combo != COMBO_NONE)
|
||||||
m_timer_button_combo_start = CoreTiming::GetTicks();
|
m_timer_button_combo_start = Core::System::GetInstance().GetCoreTiming().GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_last_button_combo != COMBO_NONE)
|
if (m_last_button_combo != COMBO_NONE)
|
||||||
{
|
{
|
||||||
const u64 current_time = CoreTiming::GetTicks();
|
const u64 current_time = Core::System::GetInstance().GetCoreTiming().GetTicks();
|
||||||
if (u32(current_time - m_timer_button_combo_start) > SystemTimers::GetTicksPerSecond() * 3)
|
if (u32(current_time - m_timer_button_combo_start) > SystemTimers::GetTicksPerSecond() * 3)
|
||||||
{
|
{
|
||||||
if (m_last_button_combo == COMBO_RESET)
|
if (m_last_button_combo == COMBO_RESET)
|
||||||
|
|
|
@ -65,6 +65,7 @@ IPC_HLE_PERIOD: For the Wii Remote this is the call schedule:
|
||||||
#include "Core/IOS/IOS.h"
|
#include "Core/IOS/IOS.h"
|
||||||
#include "Core/PatchEngine.h"
|
#include "Core/PatchEngine.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "VideoCommon/Fifo.h"
|
#include "VideoCommon/Fifo.h"
|
||||||
|
|
||||||
namespace SystemTimers
|
namespace SystemTimers
|
||||||
|
@ -105,7 +106,8 @@ void DSPCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
// splits up the cycle budget in case lle is used
|
// splits up the cycle budget in case lle is used
|
||||||
// for hle, just gives all of the slice to hle
|
// for hle, just gives all of the slice to hle
|
||||||
DSP::UpdateDSPSlice(static_cast<int>(DSP::GetDSPEmulator()->DSP_UpdateRate() - cyclesLate));
|
DSP::UpdateDSPSlice(static_cast<int>(DSP::GetDSPEmulator()->DSP_UpdateRate() - cyclesLate));
|
||||||
CoreTiming::ScheduleEvent(DSP::GetDSPEmulator()->DSP_UpdateRate() - cyclesLate, et_DSP);
|
system.GetCoreTiming().ScheduleEvent(DSP::GetDSPEmulator()->DSP_UpdateRate() - cyclesLate,
|
||||||
|
et_DSP);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetAudioDMACallbackPeriod()
|
int GetAudioDMACallbackPeriod()
|
||||||
|
@ -118,7 +120,7 @@ int GetAudioDMACallbackPeriod()
|
||||||
void AudioDMACallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
void AudioDMACallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
{
|
{
|
||||||
DSP::UpdateAudioDMA(); // Push audio to speakers.
|
DSP::UpdateAudioDMA(); // Push audio to speakers.
|
||||||
CoreTiming::ScheduleEvent(GetAudioDMACallbackPeriod() - cyclesLate, et_AudioDMA);
|
system.GetCoreTiming().ScheduleEvent(GetAudioDMACallbackPeriod() - cyclesLate, et_AudioDMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPC_HLE_UpdateCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
void IPC_HLE_UpdateCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
|
@ -126,14 +128,15 @@ void IPC_HLE_UpdateCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
if (SConfig::GetInstance().bWii)
|
if (SConfig::GetInstance().bWii)
|
||||||
{
|
{
|
||||||
IOS::HLE::GetIOS()->UpdateDevices();
|
IOS::HLE::GetIOS()->UpdateDevices();
|
||||||
CoreTiming::ScheduleEvent(s_ipc_hle_period - cyclesLate, et_IPC_HLE);
|
system.GetCoreTiming().ScheduleEvent(s_ipc_hle_period - cyclesLate, et_IPC_HLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VICallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
void VICallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
{
|
{
|
||||||
VideoInterface::Update(CoreTiming::GetTicks() - cyclesLate);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine() - cyclesLate, et_VI);
|
VideoInterface::Update(core_timing.GetTicks() - cyclesLate);
|
||||||
|
core_timing.ScheduleEvent(VideoInterface::GetTicksPerHalfLine() - cyclesLate, et_VI);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecrementerCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
void DecrementerCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||||
|
@ -164,7 +167,7 @@ void PatchEngineCallback(Core::System& system, u64 userdata, s64 cycles_late)
|
||||||
cycles_pruned += next_schedule;
|
cycles_pruned += next_schedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned);
|
system.GetCoreTiming().ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThrottleCallback(Core::System& system, u64 deadline, s64 cyclesLate)
|
void ThrottleCallback(Core::System& system, u64 deadline, s64 cyclesLate)
|
||||||
|
@ -208,7 +211,7 @@ void ThrottleCallback(Core::System& system, u64 deadline, s64 cyclesLate)
|
||||||
}
|
}
|
||||||
// reschedule 1ms (possibly scaled by emulation_speed) into future on ppc
|
// reschedule 1ms (possibly scaled by emulation_speed) into future on ppc
|
||||||
// add 1ms to the deadline
|
// add 1ms to the deadline
|
||||||
CoreTiming::ScheduleEvent(next_event - cyclesLate, et_Throttle, deadline + 1000);
|
system.GetCoreTiming().ScheduleEvent(next_event - cyclesLate, et_Throttle, deadline + 1000);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -219,34 +222,43 @@ u32 GetTicksPerSecond()
|
||||||
|
|
||||||
void DecrementerSet()
|
void DecrementerSet()
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
u32 decValue = PowerPC::ppcState.spr[SPR_DEC];
|
u32 decValue = PowerPC::ppcState.spr[SPR_DEC];
|
||||||
|
|
||||||
CoreTiming::RemoveEvent(et_Dec);
|
core_timing.RemoveEvent(et_Dec);
|
||||||
if ((decValue & 0x80000000) == 0)
|
if ((decValue & 0x80000000) == 0)
|
||||||
{
|
{
|
||||||
CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks());
|
core_timing.SetFakeDecStartTicks(core_timing.GetTicks());
|
||||||
CoreTiming::SetFakeDecStartValue(decValue);
|
core_timing.SetFakeDecStartValue(decValue);
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(decValue * TIMER_RATIO, et_Dec);
|
core_timing.ScheduleEvent(decValue * TIMER_RATIO, et_Dec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetFakeDecrementer()
|
u32 GetFakeDecrementer()
|
||||||
{
|
{
|
||||||
return (CoreTiming::GetFakeDecStartValue() -
|
auto& system = Core::System::GetInstance();
|
||||||
(u32)((CoreTiming::GetTicks() - CoreTiming::GetFakeDecStartTicks()) / TIMER_RATIO));
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
return (core_timing.GetFakeDecStartValue() -
|
||||||
|
(u32)((core_timing.GetTicks() - core_timing.GetFakeDecStartTicks()) / TIMER_RATIO));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeBaseSet()
|
void TimeBaseSet()
|
||||||
{
|
{
|
||||||
CoreTiming::SetFakeTBStartTicks(CoreTiming::GetTicks());
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::SetFakeTBStartValue(PowerPC::ReadFullTimeBaseValue());
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.SetFakeTBStartTicks(core_timing.GetTicks());
|
||||||
|
core_timing.SetFakeTBStartValue(PowerPC::ReadFullTimeBaseValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetFakeTimeBase()
|
u64 GetFakeTimeBase()
|
||||||
{
|
{
|
||||||
return CoreTiming::GetFakeTBStartValue() +
|
auto& system = Core::System::GetInstance();
|
||||||
((CoreTiming::GetTicks() - CoreTiming::GetFakeTBStartTicks()) / TIMER_RATIO);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
return core_timing.GetFakeTBStartValue() +
|
||||||
|
((core_timing.GetTicks() - core_timing.GetFakeTBStartTicks()) / TIMER_RATIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 GetLocalTimeRTCOffset()
|
s64 GetLocalTimeRTCOffset()
|
||||||
|
@ -293,7 +305,8 @@ void ChangePPCClock(Mode mode)
|
||||||
s_cpu_core_clock = 729000000u;
|
s_cpu_core_clock = 729000000u;
|
||||||
else
|
else
|
||||||
s_cpu_core_clock = 486000000u;
|
s_cpu_core_clock = 486000000u;
|
||||||
CoreTiming::AdjustEventQueueTimes(s_cpu_core_clock, previous_clock);
|
Core::System::GetInstance().GetCoreTiming().AdjustEventQueueTimes(s_cpu_core_clock,
|
||||||
|
previous_clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
|
@ -315,32 +328,35 @@ void Init()
|
||||||
Common::Timer::GetLocalTimeSinceJan1970() - Config::Get(Config::MAIN_CUSTOM_RTC_VALUE);
|
Common::Timer::GetLocalTimeSinceJan1970() - Config::Get(Config::MAIN_CUSTOM_RTC_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreTiming::SetFakeTBStartValue(static_cast<u64>(s_cpu_core_clock / TIMER_RATIO) *
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
|
core_timing.SetFakeTBStartValue(static_cast<u64>(s_cpu_core_clock / TIMER_RATIO) *
|
||||||
static_cast<u64>(ExpansionInterface::CEXIIPL::GetEmulatedTime(
|
static_cast<u64>(ExpansionInterface::CEXIIPL::GetEmulatedTime(
|
||||||
ExpansionInterface::CEXIIPL::GC_EPOCH)));
|
ExpansionInterface::CEXIIPL::GC_EPOCH)));
|
||||||
|
|
||||||
CoreTiming::SetFakeTBStartTicks(CoreTiming::GetTicks());
|
core_timing.SetFakeTBStartTicks(core_timing.GetTicks());
|
||||||
|
|
||||||
CoreTiming::SetFakeDecStartValue(0xFFFFFFFF);
|
core_timing.SetFakeDecStartValue(0xFFFFFFFF);
|
||||||
CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks());
|
core_timing.SetFakeDecStartTicks(core_timing.GetTicks());
|
||||||
|
|
||||||
et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback);
|
et_Dec = core_timing.RegisterEvent("DecCallback", DecrementerCallback);
|
||||||
et_VI = CoreTiming::RegisterEvent("VICallback", VICallback);
|
et_VI = core_timing.RegisterEvent("VICallback", VICallback);
|
||||||
et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback);
|
et_DSP = core_timing.RegisterEvent("DSPCallback", DSPCallback);
|
||||||
et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback);
|
et_AudioDMA = core_timing.RegisterEvent("AudioDMACallback", AudioDMACallback);
|
||||||
et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
|
et_IPC_HLE = core_timing.RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
|
||||||
et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback);
|
et_PatchEngine = core_timing.RegisterEvent("PatchEngine", PatchEngineCallback);
|
||||||
et_Throttle = CoreTiming::RegisterEvent("Throttle", ThrottleCallback);
|
et_Throttle = core_timing.RegisterEvent("Throttle", ThrottleCallback);
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine(), et_VI);
|
core_timing.ScheduleEvent(VideoInterface::GetTicksPerHalfLine(), et_VI);
|
||||||
CoreTiming::ScheduleEvent(0, et_DSP);
|
core_timing.ScheduleEvent(0, et_DSP);
|
||||||
CoreTiming::ScheduleEvent(GetAudioDMACallbackPeriod(), et_AudioDMA);
|
core_timing.ScheduleEvent(GetAudioDMACallbackPeriod(), et_AudioDMA);
|
||||||
CoreTiming::ScheduleEvent(0, et_Throttle, 0);
|
core_timing.ScheduleEvent(0, et_Throttle, 0);
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField(), et_PatchEngine);
|
core_timing.ScheduleEvent(VideoInterface::GetTicksPerField(), et_PatchEngine);
|
||||||
|
|
||||||
if (SConfig::GetInstance().bWii)
|
if (SConfig::GetInstance().bWii)
|
||||||
CoreTiming::ScheduleEvent(s_ipc_hle_period, et_IPC_HLE);
|
core_timing.ScheduleEvent(s_ipc_hle_period, et_IPC_HLE);
|
||||||
|
|
||||||
s_emu_to_real_time_ring_buffer.fill(0);
|
s_emu_to_real_time_ring_buffer.fill(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,7 +317,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
base | VI_HORIZONTAL_BEAM_POSITION, MMIO::ComplexRead<u16>([](Core::System& system, u32) {
|
base | VI_HORIZONTAL_BEAM_POSITION, MMIO::ComplexRead<u16>([](Core::System& system, u32) {
|
||||||
auto& state = system.GetVideoInterfaceState().GetData();
|
auto& state = system.GetVideoInterfaceState().GetData();
|
||||||
u16 value = static_cast<u16>(
|
u16 value = static_cast<u16>(
|
||||||
1 + state.h_timing_0.HLW * (CoreTiming::GetTicks() - state.ticks_last_line_start) /
|
1 + state.h_timing_0.HLW *
|
||||||
|
(system.GetCoreTiming().GetTicks() - state.ticks_last_line_start) /
|
||||||
(GetTicksPerHalfLine()));
|
(GetTicksPerHalfLine()));
|
||||||
return std::clamp<u16>(value, 1, state.h_timing_0.HLW * 2);
|
return std::clamp<u16>(value, 1, state.h_timing_0.HLW * 2);
|
||||||
}),
|
}),
|
||||||
|
@ -878,7 +879,8 @@ static void EndField(FieldType field, u64 ticks)
|
||||||
// Run when: When a frame is scanned (progressive/interlace)
|
// Run when: When a frame is scanned (progressive/interlace)
|
||||||
void Update(u64 ticks)
|
void Update(u64 ticks)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetVideoInterfaceState().GetData();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& state = system.GetVideoInterfaceState().GetData();
|
||||||
|
|
||||||
// Movie's frame counter should be updated before actually rendering the frame,
|
// Movie's frame counter should be updated before actually rendering the frame,
|
||||||
// in case frame counter display is enabled
|
// in case frame counter display is enabled
|
||||||
|
@ -946,7 +948,7 @@ void Update(u64 ticks)
|
||||||
|
|
||||||
if (!(state.half_line_count & 1))
|
if (!(state.half_line_count & 1))
|
||||||
{
|
{
|
||||||
state.ticks_last_line_start = CoreTiming::GetTicks();
|
state.ticks_last_line_start = system.GetCoreTiming().GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to assert IR_INT. Note that the granularity of our current horizontal
|
// Check if we need to assert IR_INT. Note that the granularity of our current horizontal
|
||||||
|
|
|
@ -157,7 +157,8 @@ static void InitState()
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
InitState();
|
InitState();
|
||||||
updateInterrupts = CoreTiming::RegisterEvent("IPCInterrupt", UpdateInterrupts);
|
updateInterrupts =
|
||||||
|
Core::System::GetInstance().GetCoreTiming().RegisterEvent("IPCInterrupt", UpdateInterrupts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
|
@ -176,7 +177,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
|
|
||||||
mmio->Register(base | IPC_PPCCTRL,
|
mmio->Register(base | IPC_PPCCTRL,
|
||||||
MMIO::ComplexRead<u32>([](Core::System&, u32) { return ctrl.ppc(); }),
|
MMIO::ComplexRead<u32>([](Core::System&, u32) { return ctrl.ppc(); }),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
ctrl.ppc(val);
|
ctrl.ppc(val);
|
||||||
// The IPC interrupt is triggered when IY1/IY2 is set and
|
// The IPC interrupt is triggered when IY1/IY2 is set and
|
||||||
// Y1/Y2 is written to -- even when this results in clearing the bit.
|
// Y1/Y2 is written to -- even when this results in clearing the bit.
|
||||||
|
@ -185,25 +186,25 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
if (ctrl.X1)
|
if (ctrl.X1)
|
||||||
HLE::GetIOS()->EnqueueIPCRequest(ppc_msg);
|
HLE::GetIOS()->EnqueueIPCRequest(ppc_msg);
|
||||||
HLE::GetIOS()->UpdateIPC();
|
HLE::GetIOS()->UpdateIPC();
|
||||||
CoreTiming::ScheduleEvent(0, updateInterrupts, 0);
|
system.GetCoreTiming().ScheduleEvent(0, updateInterrupts, 0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | IPC_ARMMSG, MMIO::DirectRead<u32>(&arm_msg), MMIO::InvalidWrite<u32>());
|
mmio->Register(base | IPC_ARMMSG, MMIO::DirectRead<u32>(&arm_msg), MMIO::InvalidWrite<u32>());
|
||||||
|
|
||||||
mmio->Register(base | PPC_IRQFLAG, MMIO::InvalidRead<u32>(),
|
mmio->Register(base | PPC_IRQFLAG, MMIO::InvalidRead<u32>(),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
ppc_irq_flags &= ~val;
|
ppc_irq_flags &= ~val;
|
||||||
HLE::GetIOS()->UpdateIPC();
|
HLE::GetIOS()->UpdateIPC();
|
||||||
CoreTiming::ScheduleEvent(0, updateInterrupts, 0);
|
system.GetCoreTiming().ScheduleEvent(0, updateInterrupts, 0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | PPC_IRQMASK, MMIO::InvalidRead<u32>(),
|
mmio->Register(base | PPC_IRQMASK, MMIO::InvalidRead<u32>(),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
ppc_irq_masks = val;
|
ppc_irq_masks = val;
|
||||||
if (ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf?
|
if (ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf?
|
||||||
Reset();
|
Reset();
|
||||||
HLE::GetIOS()->UpdateIPC();
|
HLE::GetIOS()->UpdateIPC();
|
||||||
CoreTiming::ScheduleEvent(0, updateInterrupts, 0);
|
system.GetCoreTiming().ScheduleEvent(0, updateInterrupts, 0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
mmio->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
||||||
|
@ -313,7 +314,8 @@ void GenerateAck(u32 address)
|
||||||
ctrl.Y2, ctrl.X1);
|
ctrl.Y2, ctrl.X1);
|
||||||
// Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
|
// Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
|
||||||
// after Y2 is seen in the control register.
|
// after Y2 is seen in the control register.
|
||||||
CoreTiming::ScheduleEvent(100 * SystemTimers::TIMER_RATIO, updateInterrupts);
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(100 * SystemTimers::TIMER_RATIO,
|
||||||
|
updateInterrupts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateReply(u32 address)
|
void GenerateReply(u32 address)
|
||||||
|
@ -324,7 +326,8 @@ void GenerateReply(u32 address)
|
||||||
ctrl.Y1, ctrl.Y2, ctrl.X1);
|
ctrl.Y1, ctrl.Y2, ctrl.X1);
|
||||||
// Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
|
// Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
|
||||||
// after Y1 is seen in the control register.
|
// after Y1 is seen in the control register.
|
||||||
CoreTiming::ScheduleEvent(100 * SystemTimers::TIMER_RATIO, updateInterrupts);
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(100 * SystemTimers::TIMER_RATIO,
|
||||||
|
updateInterrupts);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsReady()
|
bool IsReady()
|
||||||
|
|
|
@ -113,8 +113,8 @@ void DIDevice::ProcessQueuedIOCtl()
|
||||||
auto finished = StartIOCtl(request);
|
auto finished = StartIOCtl(request);
|
||||||
if (finished)
|
if (finished)
|
||||||
{
|
{
|
||||||
CoreTiming::ScheduleEvent(IPC_OVERHEAD_TICKS, s_finish_executing_di_command,
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(
|
||||||
static_cast<u64>(finished.value()));
|
IPC_OVERHEAD_TICKS, s_finish_executing_di_command, static_cast<u64>(finished.value()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "Core/IOS/IOSC.h"
|
#include "Core/IOS/IOSC.h"
|
||||||
#include "Core/IOS/Uids.h"
|
#include "Core/IOS/Uids.h"
|
||||||
#include "Core/IOS/VersionInfo.h"
|
#include "Core/IOS/VersionInfo.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
|
|
||||||
namespace IOS::HLE
|
namespace IOS::HLE
|
||||||
|
@ -103,8 +104,10 @@ ESDevice::ESDevice(Kernel& ios, const std::string& device_name) : Device(ios, de
|
||||||
|
|
||||||
if (Core::IsRunningAndStarted())
|
if (Core::IsRunningAndStarted())
|
||||||
{
|
{
|
||||||
CoreTiming::RemoveEvent(s_finish_init_event);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(GetESBootTicks(m_ios.GetVersion()), s_finish_init_event);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.RemoveEvent(s_finish_init_event);
|
||||||
|
core_timing.ScheduleEvent(GetESBootTicks(m_ios.GetVersion()), s_finish_init_event);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -114,14 +117,16 @@ ESDevice::ESDevice(Kernel& ios, const std::string& device_name) : Device(ios, de
|
||||||
|
|
||||||
void ESDevice::InitializeEmulationState()
|
void ESDevice::InitializeEmulationState()
|
||||||
{
|
{
|
||||||
s_finish_init_event = CoreTiming::RegisterEvent(
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
s_finish_init_event = core_timing.RegisterEvent(
|
||||||
"IOS-ESFinishInit", [](Core::System& system, u64, s64) { GetIOS()->GetES()->FinishInit(); });
|
"IOS-ESFinishInit", [](Core::System& system, u64, s64) { GetIOS()->GetES()->FinishInit(); });
|
||||||
s_reload_ios_for_ppc_launch_event = CoreTiming::RegisterEvent(
|
s_reload_ios_for_ppc_launch_event = core_timing.RegisterEvent(
|
||||||
"IOS-ESReloadIOSForPPCLaunch", [](Core::System& system, u64 ios_id, s64) {
|
"IOS-ESReloadIOSForPPCLaunch", [](Core::System& system, u64 ios_id, s64) {
|
||||||
GetIOS()->GetES()->LaunchTitle(ios_id, HangPPC::Yes);
|
GetIOS()->GetES()->LaunchTitle(ios_id, HangPPC::Yes);
|
||||||
});
|
});
|
||||||
s_bootstrap_ppc_for_launch_event =
|
s_bootstrap_ppc_for_launch_event =
|
||||||
CoreTiming::RegisterEvent("IOS-ESBootstrapPPCForLaunch", [](Core::System& system, u64, s64) {
|
core_timing.RegisterEvent("IOS-ESBootstrapPPCForLaunch", [](Core::System& system, u64, s64) {
|
||||||
GetIOS()->GetES()->BootstrapPPC();
|
GetIOS()->GetES()->BootstrapPPC();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -397,6 +402,9 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
// Before launching a title, IOS first reads the TMD and reloads into the specified IOS version,
|
// Before launching a title, IOS first reads the TMD and reloads into the specified IOS version,
|
||||||
// even when that version is already running. After it has reloaded, ES_Launch will be called
|
// even when that version is already running. After it has reloaded, ES_Launch will be called
|
||||||
// again and the PPC will be bootstrapped then.
|
// again and the PPC will be bootstrapped then.
|
||||||
|
@ -417,8 +425,8 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
|
||||||
const u64 required_ios = tmd.GetIOSId();
|
const u64 required_ios = tmd.GetIOSId();
|
||||||
if (!Core::IsRunningAndStarted())
|
if (!Core::IsRunningAndStarted())
|
||||||
return LaunchTitle(required_ios, HangPPC::Yes);
|
return LaunchTitle(required_ios, HangPPC::Yes);
|
||||||
CoreTiming::RemoveEvent(s_reload_ios_for_ppc_launch_event);
|
core_timing.RemoveEvent(s_reload_ios_for_ppc_launch_event);
|
||||||
CoreTiming::ScheduleEvent(ticks, s_reload_ios_for_ppc_launch_event, required_ios);
|
core_timing.ScheduleEvent(ticks, s_reload_ios_for_ppc_launch_event, required_ios);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,8 +454,9 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
|
||||||
m_pending_ppc_boot_content_path = GetContentPath(tmd.GetTitleId(), content);
|
m_pending_ppc_boot_content_path = GetContentPath(tmd.GetTitleId(), content);
|
||||||
if (!Core::IsRunningAndStarted())
|
if (!Core::IsRunningAndStarted())
|
||||||
return BootstrapPPC();
|
return BootstrapPPC();
|
||||||
CoreTiming::RemoveEvent(s_bootstrap_ppc_for_launch_event);
|
|
||||||
CoreTiming::ScheduleEvent(ticks, s_bootstrap_ppc_for_launch_event);
|
core_timing.RemoveEvent(s_bootstrap_ppc_for_launch_event);
|
||||||
|
core_timing.ScheduleEvent(ticks, s_bootstrap_ppc_for_launch_event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
#include "Core/IOS/WFS/WFSI.h"
|
#include "Core/IOS/WFS/WFSI.h"
|
||||||
#include "Core/IOS/WFS/WFSSRV.h"
|
#include "Core/IOS/WFS/WFSSRV.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "Core/WiiRoot.h"
|
#include "Core/WiiRoot.h"
|
||||||
|
|
||||||
namespace IOS::HLE
|
namespace IOS::HLE
|
||||||
|
@ -320,7 +321,7 @@ EmulationKernel::EmulationKernel(u64 title_id) : Kernel(title_id)
|
||||||
|
|
||||||
EmulationKernel::~EmulationKernel()
|
EmulationKernel::~EmulationKernel()
|
||||||
{
|
{
|
||||||
CoreTiming::RemoveAllEvents(s_event_enqueue);
|
Core::System::GetInstance().GetCoreTiming().RemoveAllEvents(s_event_enqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The title ID is a u64 where the first 32 bits are used for the title type.
|
// The title ID is a u64 where the first 32 bits are used for the title type.
|
||||||
|
@ -410,7 +411,8 @@ bool Kernel::BootstrapPPC(const std::string& boot_content_path)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
INFO_LOG_FMT(IOS, "BootstrapPPC: {}", boot_content_path);
|
INFO_LOG_FMT(IOS, "BootstrapPPC: {}", boot_content_path);
|
||||||
CoreTiming::ScheduleEvent(ticks, s_event_finish_ppc_bootstrap, dol.IsAncast());
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(ticks, s_event_finish_ppc_bootstrap,
|
||||||
|
dol.IsAncast());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,9 +487,14 @@ bool Kernel::BootIOS(const u64 ios_title_id, HangPPC hang_ppc, const std::string
|
||||||
ResetAndPausePPC();
|
ResetAndPausePPC();
|
||||||
|
|
||||||
if (Core::IsRunningAndStarted())
|
if (Core::IsRunningAndStarted())
|
||||||
CoreTiming::ScheduleEvent(GetIOSBootTicks(GetVersion()), s_event_finish_ios_boot, ios_title_id);
|
{
|
||||||
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(
|
||||||
|
GetIOSBootTicks(GetVersion()), s_event_finish_ios_boot, ios_title_id);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
FinishIOSBoot(ios_title_id);
|
FinishIOSBoot(ios_title_id);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -720,10 +727,12 @@ void Kernel::ExecuteIPCCommand(const u32 address)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Ensure replies happen in order
|
// Ensure replies happen in order
|
||||||
const s64 ticks_until_last_reply = m_last_reply_time - CoreTiming::GetTicks();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
const s64 ticks_until_last_reply = m_last_reply_time - core_timing.GetTicks();
|
||||||
if (ticks_until_last_reply > 0)
|
if (ticks_until_last_reply > 0)
|
||||||
result->reply_delay_ticks += ticks_until_last_reply;
|
result->reply_delay_ticks += ticks_until_last_reply;
|
||||||
m_last_reply_time = CoreTiming::GetTicks() + result->reply_delay_ticks;
|
m_last_reply_time = core_timing.GetTicks() + result->reply_delay_ticks;
|
||||||
|
|
||||||
EnqueueIPCReply(request, result->return_value, result->reply_delay_ticks);
|
EnqueueIPCReply(request, result->return_value, result->reply_delay_ticks);
|
||||||
}
|
}
|
||||||
|
@ -734,7 +743,8 @@ void Kernel::EnqueueIPCRequest(u32 address)
|
||||||
// Based on hardware tests, IOS takes between 5µs and 10µs to acknowledge an IPC request.
|
// Based on hardware tests, IOS takes between 5µs and 10µs to acknowledge an IPC request.
|
||||||
// Console 1: 456 TB ticks before ACK
|
// Console 1: 456 TB ticks before ACK
|
||||||
// Console 2: 658 TB ticks before ACK
|
// Console 2: 658 TB ticks before ACK
|
||||||
CoreTiming::ScheduleEvent(500_tbticks, s_event_enqueue, address | ENQUEUE_REQUEST_FLAG);
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(500_tbticks, s_event_enqueue,
|
||||||
|
address | ENQUEUE_REQUEST_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called to send a reply to an IOS syscall
|
// Called to send a reply to an IOS syscall
|
||||||
|
@ -746,7 +756,8 @@ void Kernel::EnqueueIPCReply(const Request& request, const s32 return_value, s64
|
||||||
Memory::Write_U32(request.command, request.address + 8);
|
Memory::Write_U32(request.command, request.address + 8);
|
||||||
// IOS also overwrites the command type with the reply type.
|
// IOS also overwrites the command type with the reply type.
|
||||||
Memory::Write_U32(IPC_REPLY, request.address);
|
Memory::Write_U32(IPC_REPLY, request.address);
|
||||||
CoreTiming::ScheduleEvent(cycles_in_future, s_event_enqueue, request.address, from);
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(cycles_in_future, s_event_enqueue,
|
||||||
|
request.address, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kernel::HandleIPCEvent(u64 userdata)
|
void Kernel::HandleIPCEvent(u64 userdata)
|
||||||
|
@ -892,8 +903,11 @@ static void FinishPPCBootstrap(Core::System& system, u64 userdata, s64 cycles_la
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
s_event_enqueue =
|
s_event_enqueue =
|
||||||
CoreTiming::RegisterEvent("IPCEvent", [](Core::System& system, u64 userdata, s64) {
|
core_timing.RegisterEvent("IPCEvent", [](Core::System& system, u64 userdata, s64) {
|
||||||
if (s_ios)
|
if (s_ios)
|
||||||
s_ios->HandleIPCEvent(userdata);
|
s_ios->HandleIPCEvent(userdata);
|
||||||
});
|
});
|
||||||
|
@ -901,14 +915,14 @@ void Init()
|
||||||
ESDevice::InitializeEmulationState();
|
ESDevice::InitializeEmulationState();
|
||||||
|
|
||||||
s_event_finish_ppc_bootstrap =
|
s_event_finish_ppc_bootstrap =
|
||||||
CoreTiming::RegisterEvent("IOSFinishPPCBootstrap", FinishPPCBootstrap);
|
core_timing.RegisterEvent("IOSFinishPPCBootstrap", FinishPPCBootstrap);
|
||||||
|
|
||||||
s_event_finish_ios_boot =
|
s_event_finish_ios_boot =
|
||||||
CoreTiming::RegisterEvent("IOSFinishIOSBoot", [](Core::System& system, u64 ios_title_id,
|
core_timing.RegisterEvent("IOSFinishIOSBoot", [](Core::System& system, u64 ios_title_id,
|
||||||
s64) { FinishIOSBoot(ios_title_id); });
|
s64) { FinishIOSBoot(ios_title_id); });
|
||||||
|
|
||||||
DIDevice::s_finish_executing_di_command =
|
DIDevice::s_finish_executing_di_command =
|
||||||
CoreTiming::RegisterEvent("FinishDICommand", DIDevice::FinishDICommandCallback);
|
core_timing.RegisterEvent("FinishDICommand", DIDevice::FinishDICommandCallback);
|
||||||
|
|
||||||
// Start with IOS80 to simulate part of the Wii boot process.
|
// Start with IOS80 to simulate part of the Wii boot process.
|
||||||
s_ios = std::make_unique<EmulationKernel>(Titles::SYSTEM_MENU_IOS);
|
s_ios = std::make_unique<EmulationKernel>(Titles::SYSTEM_MENU_IOS);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "Core/NetPlayClient.h"
|
#include "Core/NetPlayClient.h"
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
#include "Core/SysConf.h"
|
#include "Core/SysConf.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||||
|
|
||||||
namespace IOS::HLE
|
namespace IOS::HLE
|
||||||
|
@ -339,7 +340,7 @@ void BluetoothEmuDevice::Update()
|
||||||
wiimote->Update();
|
wiimote->Update();
|
||||||
|
|
||||||
const u64 interval = SystemTimers::GetTicksPerSecond() / Wiimote::UPDATE_FREQ;
|
const u64 interval = SystemTimers::GetTicksPerSecond() / Wiimote::UPDATE_FREQ;
|
||||||
const u64 now = CoreTiming::GetTicks();
|
const u64 now = Core::System::GetInstance().GetCoreTiming().GetTicks();
|
||||||
|
|
||||||
if (now - m_last_ticks > interval)
|
if (now - m_last_ticks > interval)
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
#include "Core/State.h"
|
#include "Core/State.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "Core/WiiUtils.h"
|
#include "Core/WiiUtils.h"
|
||||||
|
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
|
@ -290,9 +291,12 @@ void InputUpdate()
|
||||||
s_currentInputCount++;
|
s_currentInputCount++;
|
||||||
if (IsRecordingInput())
|
if (IsRecordingInput())
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
s_totalInputCount = s_currentInputCount;
|
s_totalInputCount = s_currentInputCount;
|
||||||
s_totalTickCount += CoreTiming::GetTicks() - s_tickCountAtLastInput;
|
s_totalTickCount += core_timing.GetTicks() - s_tickCountAtLastInput;
|
||||||
s_tickCountAtLastInput = CoreTiming::GetTicks();
|
s_tickCountAtLastInput = core_timing.GetTicks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1181,7 +1185,8 @@ void LoadInput(const std::string& movie_path)
|
||||||
static void CheckInputEnd()
|
static void CheckInputEnd()
|
||||||
{
|
{
|
||||||
if (s_currentByte >= s_temp_input.size() ||
|
if (s_currentByte >= s_temp_input.size() ||
|
||||||
(CoreTiming::GetTicks() > s_totalTickCount && !IsRecordingInputFromSaveState()))
|
(Core::System::GetInstance().GetCoreTiming().GetTicks() > s_totalTickCount &&
|
||||||
|
!IsRecordingInputFromSaveState()))
|
||||||
{
|
{
|
||||||
EndPlayInput(!s_bReadOnly);
|
EndPlayInput(!s_bReadOnly);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "Core/PowerPC/Jit64Common/Jit64Constants.h"
|
#include "Core/PowerPC/Jit64Common/Jit64Constants.h"
|
||||||
#include "Core/PowerPC/PPCAnalyst.h"
|
#include "Core/PowerPC/PPCAnalyst.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
struct CachedInterpreter::Instruction
|
struct CachedInterpreter::Instruction
|
||||||
{
|
{
|
||||||
|
@ -109,12 +110,15 @@ void CachedInterpreter::ExecuteOneBlock()
|
||||||
|
|
||||||
void CachedInterpreter::Run()
|
void CachedInterpreter::Run()
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
const CPU::State* state_ptr = CPU::GetStatePtr();
|
const CPU::State* state_ptr = CPU::GetStatePtr();
|
||||||
while (CPU::GetState() == CPU::State::Running)
|
while (CPU::GetState() == CPU::State::Running)
|
||||||
{
|
{
|
||||||
// Start new timing slice
|
// Start new timing slice
|
||||||
// NOTE: Exceptions may change PC
|
// NOTE: Exceptions may change PC
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -126,7 +130,7 @@ void CachedInterpreter::Run()
|
||||||
void CachedInterpreter::SingleStep()
|
void CachedInterpreter::SingleStep()
|
||||||
{
|
{
|
||||||
// Enter new timing slice
|
// Enter new timing slice
|
||||||
CoreTiming::Advance();
|
Core::System::GetInstance().GetCoreTiming().Advance();
|
||||||
ExecuteOneBlock();
|
ExecuteOneBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +211,7 @@ static bool CheckIdle(u32 idle_pc)
|
||||||
{
|
{
|
||||||
if (PowerPC::ppcState.npc == idle_pc)
|
if (PowerPC::ppcState.npc == idle_pc)
|
||||||
{
|
{
|
||||||
CoreTiming::Idle();
|
Core::System::GetInstance().GetCoreTiming().Idle();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ typedef SSIZE_T ssize_t;
|
||||||
#include "Core/PowerPC/Gekko.h"
|
#include "Core/PowerPC/Gekko.h"
|
||||||
#include "Core/PowerPC/PPCCache.h"
|
#include "Core/PowerPC/PPCCache.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace GDBStub
|
namespace GDBStub
|
||||||
{
|
{
|
||||||
|
@ -128,7 +129,7 @@ static void UpdateCallback(Core::System& system, u64 userdata, s64 cycles_late)
|
||||||
{
|
{
|
||||||
ProcessCommands(false);
|
ProcessCommands(false);
|
||||||
if (IsActive())
|
if (IsActive())
|
||||||
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 ReadByte()
|
static u8 ReadByte()
|
||||||
|
@ -1068,8 +1069,10 @@ static void InitGeneric(int domain, const sockaddr* server_addr, socklen_t serve
|
||||||
#endif
|
#endif
|
||||||
s_tmpsock = -1;
|
s_tmpsock = -1;
|
||||||
|
|
||||||
s_update_event = CoreTiming::RegisterEvent("GDBStubUpdate", UpdateCallback);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
s_update_event = core_timing.RegisterEvent("GDBStubUpdate", UpdateCallback);
|
||||||
|
core_timing.ScheduleEvent(GDB_UPDATE_CYCLES, s_update_event);
|
||||||
s_has_control = true;
|
s_has_control = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,10 +212,11 @@ int Interpreter::SingleStepInner()
|
||||||
|
|
||||||
void Interpreter::SingleStep()
|
void Interpreter::SingleStep()
|
||||||
{
|
{
|
||||||
auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals();
|
auto& core_timing = Core::System::GetInstance().GetCoreTiming();
|
||||||
|
auto& core_timing_globals = core_timing.GetGlobals();
|
||||||
|
|
||||||
// Declare start of new slice
|
// Declare start of new slice
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
SingleStepInner();
|
SingleStepInner();
|
||||||
|
|
||||||
|
@ -241,12 +242,14 @@ constexpr u32 s_show_steps = 300;
|
||||||
// FastRun - inspired by GCemu (to imitate the JIT so that they can be compared).
|
// FastRun - inspired by GCemu (to imitate the JIT so that they can be compared).
|
||||||
void Interpreter::Run()
|
void Interpreter::Run()
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
while (CPU::GetState() == CPU::State::Running)
|
while (CPU::GetState() == CPU::State::Running)
|
||||||
{
|
{
|
||||||
// CoreTiming Advance() ends the previous slice and declares the start of the next
|
// CoreTiming Advance() ends the previous slice and declares the start of the next
|
||||||
// one so it must always be called at the start. At boot, we are in slice -1 and must
|
// one so it must always be called at the start. At boot, we are in slice -1 and must
|
||||||
// advance into slice 0 to get a correct slice length before executing any cycles.
|
// advance into slice 0 to get a correct slice length before executing any cycles.
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
// we have to check exceptions at branches apparently (or maybe just rfi?)
|
// we have to check exceptions at branches apparently (or maybe just rfi?)
|
||||||
if (Config::Get(Config::MAIN_ENABLE_DEBUGGING))
|
if (Config::Get(Config::MAIN_ENABLE_DEBUGGING))
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "Core/PowerPC/PPCAnalyst.h"
|
#include "Core/PowerPC/PPCAnalyst.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "Core/PowerPC/Profiler.h"
|
#include "Core/PowerPC/Profiler.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
using namespace PowerPC;
|
using namespace PowerPC;
|
||||||
|
@ -205,7 +206,7 @@ bool Jit64::HandleStackFault()
|
||||||
// to reset the guard page.
|
// to reset the guard page.
|
||||||
// Yeah, it's kind of gross.
|
// Yeah, it's kind of gross.
|
||||||
GetBlockCache()->InvalidateICache(0, 0xffffffff, true);
|
GetBlockCache()->InvalidateICache(0, 0xffffffff, true);
|
||||||
CoreTiming::ForceExceptionCheck(0);
|
Core::System::GetInstance().GetCoreTiming().ForceExceptionCheck(0);
|
||||||
m_cleanup_after_stackfault = true;
|
m_cleanup_after_stackfault = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -685,7 +686,7 @@ void Jit64::WriteRfiExitDestInRSCRATCH()
|
||||||
void Jit64::WriteIdleExit(u32 destination)
|
void Jit64::WriteIdleExit(u32 destination)
|
||||||
{
|
{
|
||||||
ABI_PushRegistersAndAdjustStack({}, 0);
|
ABI_PushRegistersAndAdjustStack({}, 0);
|
||||||
ABI_CallFunction(CoreTiming::Idle);
|
ABI_CallFunction(CoreTiming::GlobalIdle);
|
||||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||||
MOV(32, PPCSTATE(pc), Imm32(destination));
|
MOV(32, PPCSTATE(pc), Imm32(destination));
|
||||||
WriteExceptionExit();
|
WriteExceptionExit();
|
||||||
|
|
|
@ -65,7 +65,7 @@ void Jit64AsmRoutineManager::Generate()
|
||||||
|
|
||||||
const u8* outerLoop = GetCodePtr();
|
const u8* outerLoop = GetCodePtr();
|
||||||
ABI_PushRegistersAndAdjustStack({}, 0);
|
ABI_PushRegistersAndAdjustStack({}, 0);
|
||||||
ABI_CallFunction(CoreTiming::Advance);
|
ABI_CallFunction(CoreTiming::GlobalAdvance);
|
||||||
ABI_PopRegistersAndAdjustStack({}, 0);
|
ABI_PopRegistersAndAdjustStack({}, 0);
|
||||||
FixupBranch skipToRealDispatch = J(enable_debugging); // skip the sync and compare first time
|
FixupBranch skipToRealDispatch = J(enable_debugging); // skip the sync and compare first time
|
||||||
dispatcher_mispredicted_blr = GetCodePtr();
|
dispatcher_mispredicted_blr = GetCodePtr();
|
||||||
|
|
|
@ -323,7 +323,7 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
||||||
RCX64Reg rax = gpr.Scratch(RAX);
|
RCX64Reg rax = gpr.Scratch(RAX);
|
||||||
RCX64Reg rcx = gpr.Scratch(RCX);
|
RCX64Reg rcx = gpr.Scratch(RCX);
|
||||||
|
|
||||||
auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals();
|
auto& core_timing_globals = Core::System::GetInstance().GetCoreTiming().GetGlobals();
|
||||||
MOV(64, rcx, ImmPtr(&core_timing_globals));
|
MOV(64, rcx, ImmPtr(&core_timing_globals));
|
||||||
|
|
||||||
// An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the
|
// An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||||
#include "Core/PowerPC/JitInterface.h"
|
#include "Core/PowerPC/JitInterface.h"
|
||||||
#include "Core/PowerPC/Profiler.h"
|
#include "Core/PowerPC/Profiler.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
using namespace Arm64Gen;
|
using namespace Arm64Gen;
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ bool JitArm64::HandleStackFault()
|
||||||
Common::UnWriteProtectMemory(m_stack_base + GUARD_OFFSET, GUARD_SIZE);
|
Common::UnWriteProtectMemory(m_stack_base + GUARD_OFFSET, GUARD_SIZE);
|
||||||
#endif
|
#endif
|
||||||
GetBlockCache()->InvalidateICache(0, 0xffffffff, true);
|
GetBlockCache()->InvalidateICache(0, 0xffffffff, true);
|
||||||
CoreTiming::ForceExceptionCheck(0);
|
Core::System::GetInstance().GetCoreTiming().ForceExceptionCheck(0);
|
||||||
m_cleanup_after_stackfault = true;
|
m_cleanup_after_stackfault = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -105,7 +105,7 @@ void JitArm64::bx(UGeckoInstruction inst)
|
||||||
ARM64Reg WA = gpr.GetReg();
|
ARM64Reg WA = gpr.GetReg();
|
||||||
ARM64Reg XA = EncodeRegTo64(WA);
|
ARM64Reg XA = EncodeRegTo64(WA);
|
||||||
|
|
||||||
MOVP2R(XA, &CoreTiming::Idle);
|
MOVP2R(XA, &CoreTiming::GlobalIdle);
|
||||||
BLR(XA);
|
BLR(XA);
|
||||||
gpr.Unlock(WA);
|
gpr.Unlock(WA);
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ void JitArm64::bcx(UGeckoInstruction inst)
|
||||||
// make idle loops go faster
|
// make idle loops go faster
|
||||||
ARM64Reg XA = EncodeRegTo64(WA);
|
ARM64Reg XA = EncodeRegTo64(WA);
|
||||||
|
|
||||||
MOVP2R(XA, &CoreTiming::Idle);
|
MOVP2R(XA, &CoreTiming::GlobalIdle);
|
||||||
BLR(XA);
|
BLR(XA);
|
||||||
|
|
||||||
WriteExceptionExit(js.op->branchTo);
|
WriteExceptionExit(js.op->branchTo);
|
||||||
|
@ -281,7 +281,7 @@ void JitArm64::bclrx(UGeckoInstruction inst)
|
||||||
// make idle loops go faster
|
// make idle loops go faster
|
||||||
ARM64Reg XA = EncodeRegTo64(WA);
|
ARM64Reg XA = EncodeRegTo64(WA);
|
||||||
|
|
||||||
MOVP2R(XA, &CoreTiming::Idle);
|
MOVP2R(XA, &CoreTiming::GlobalIdle);
|
||||||
BLR(XA);
|
BLR(XA);
|
||||||
|
|
||||||
WriteExceptionExit(js.op->branchTo);
|
WriteExceptionExit(js.op->branchTo);
|
||||||
|
|
|
@ -307,7 +307,7 @@ void JitArm64::mfspr(UGeckoInstruction inst)
|
||||||
// An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the
|
// An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the
|
||||||
// cost of calling out to C for this is actually significant.
|
// cost of calling out to C for this is actually significant.
|
||||||
|
|
||||||
auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals();
|
auto& core_timing_globals = Core::System::GetInstance().GetCoreTiming().GetGlobals();
|
||||||
MOVP2R(Xg, &core_timing_globals);
|
MOVP2R(Xg, &core_timing_globals);
|
||||||
|
|
||||||
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(downcount));
|
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(downcount));
|
||||||
|
|
|
@ -172,7 +172,7 @@ void JitArm64::GenerateAsm()
|
||||||
FixupBranch Exit = B(CC_NEQ);
|
FixupBranch Exit = B(CC_NEQ);
|
||||||
|
|
||||||
SetJumpTarget(to_start_of_timing_slice);
|
SetJumpTarget(to_start_of_timing_slice);
|
||||||
MOVP2R(ARM64Reg::X8, &CoreTiming::Advance);
|
MOVP2R(ARM64Reg::X8, &CoreTiming::GlobalAdvance);
|
||||||
BLR(ARM64Reg::X8);
|
BLR(ARM64Reg::X8);
|
||||||
|
|
||||||
// Load the PC back into DISPATCHER_PC (the exception handler might have changed it)
|
// Load the PC back into DISPATCHER_PC (the exception handler might have changed it)
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "Core/PowerPC/JitInterface.h"
|
#include "Core/PowerPC/JitInterface.h"
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
#include "Core/PowerPC/PPCSymbolDB.h"
|
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace PowerPC
|
namespace PowerPC
|
||||||
{
|
{
|
||||||
|
@ -258,8 +259,8 @@ CPUCore DefaultCPUCore()
|
||||||
|
|
||||||
void Init(CPUCore cpu_core)
|
void Init(CPUCore cpu_core)
|
||||||
{
|
{
|
||||||
s_invalidate_cache_thread_safe =
|
s_invalidate_cache_thread_safe = Core::System::GetInstance().GetCoreTiming().RegisterEvent(
|
||||||
CoreTiming::RegisterEvent("invalidateEmulatedCache", InvalidateCacheThreadSafe);
|
"invalidateEmulatedCache", InvalidateCacheThreadSafe);
|
||||||
|
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
|
@ -284,8 +285,8 @@ void ScheduleInvalidateCacheThreadSafe(u32 address)
|
||||||
{
|
{
|
||||||
if (CPU::GetState() == CPU::State::Running)
|
if (CPU::GetState() == CPU::State::Running)
|
||||||
{
|
{
|
||||||
CoreTiming::ScheduleEvent(0, s_invalidate_cache_thread_safe, address,
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(
|
||||||
CoreTiming::FromThread::NON_CPU);
|
0, s_invalidate_cache_thread_safe, address, CoreTiming::FromThread::NON_CPU);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
#include "Core/NetPlayClient.h"
|
#include "Core/NetPlayClient.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
#include "VideoCommon/FrameDump.h"
|
#include "VideoCommon/FrameDump.h"
|
||||||
#include "VideoCommon/OnScreenDisplay.h"
|
#include "VideoCommon/OnScreenDisplay.h"
|
||||||
|
@ -224,7 +225,7 @@ static void DoState(PointerWrap& p)
|
||||||
p.DoMarker("PowerPC");
|
p.DoMarker("PowerPC");
|
||||||
// CoreTiming needs to be restored before restoring Hardware because
|
// CoreTiming needs to be restored before restoring Hardware because
|
||||||
// the controller code might need to schedule an event if the controller has changed.
|
// the controller code might need to schedule an event if the controller has changed.
|
||||||
CoreTiming::DoState(p);
|
Core::System::GetInstance().GetCoreTiming().DoState(p);
|
||||||
p.DoMarker("CoreTiming");
|
p.DoMarker("CoreTiming");
|
||||||
HW::DoState(p);
|
HW::DoState(p);
|
||||||
p.DoMarker("HW");
|
p.DoMarker("HW");
|
||||||
|
|
|
@ -27,8 +27,7 @@ struct System::Impl
|
||||||
bool m_audio_dump_started = false;
|
bool m_audio_dump_started = false;
|
||||||
|
|
||||||
AudioInterface::AudioInterfaceState m_audio_interface_state;
|
AudioInterface::AudioInterfaceState m_audio_interface_state;
|
||||||
CoreTiming::CoreTimingState m_core_timing_state;
|
CoreTiming::CoreTimingManager m_core_timing;
|
||||||
CoreTiming::Globals m_core_timing_globals;
|
|
||||||
DSP::DSPState m_dsp_state;
|
DSP::DSPState m_dsp_state;
|
||||||
DVDInterface::DVDInterfaceState m_dvd_interface_state;
|
DVDInterface::DVDInterfaceState m_dvd_interface_state;
|
||||||
DVDThread::DVDThreadState m_dvd_thread_state;
|
DVDThread::DVDThreadState m_dvd_thread_state;
|
||||||
|
@ -87,14 +86,9 @@ AudioInterface::AudioInterfaceState& System::GetAudioInterfaceState() const
|
||||||
return m_impl->m_audio_interface_state;
|
return m_impl->m_audio_interface_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreTiming::CoreTimingState& System::GetCoreTimingState() const
|
CoreTiming::CoreTimingManager& System::GetCoreTiming() const
|
||||||
{
|
{
|
||||||
return m_impl->m_core_timing_state;
|
return m_impl->m_core_timing;
|
||||||
}
|
|
||||||
|
|
||||||
CoreTiming::Globals& System::GetCoreTimingGlobals() const
|
|
||||||
{
|
|
||||||
return m_impl->m_core_timing_globals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DSP::DSPState& System::GetDSPState() const
|
DSP::DSPState& System::GetDSPState() const
|
||||||
|
|
|
@ -14,9 +14,8 @@ class AudioInterfaceState;
|
||||||
};
|
};
|
||||||
namespace CoreTiming
|
namespace CoreTiming
|
||||||
{
|
{
|
||||||
class CoreTimingState;
|
class CoreTimingManager;
|
||||||
struct Globals;
|
}
|
||||||
} // namespace CoreTiming
|
|
||||||
namespace DSP
|
namespace DSP
|
||||||
{
|
{
|
||||||
class DSPState;
|
class DSPState;
|
||||||
|
@ -81,8 +80,7 @@ public:
|
||||||
void SetAudioDumpStarted(bool started);
|
void SetAudioDumpStarted(bool started);
|
||||||
|
|
||||||
AudioInterface::AudioInterfaceState& GetAudioInterfaceState() const;
|
AudioInterface::AudioInterfaceState& GetAudioInterfaceState() const;
|
||||||
CoreTiming::CoreTimingState& GetCoreTimingState() const;
|
CoreTiming::CoreTimingManager& GetCoreTiming() const;
|
||||||
CoreTiming::Globals& GetCoreTimingGlobals() const;
|
|
||||||
DSP::DSPState& GetDSPState() const;
|
DSP::DSPState& GetDSPState() const;
|
||||||
DVDInterface::DVDInterfaceState& GetDVDInterfaceState() const;
|
DVDInterface::DVDInterfaceState& GetDVDInterfaceState() const;
|
||||||
DVDThread::DVDThreadState& GetDVDThreadState() const;
|
DVDThread::DVDThreadState& GetDVDThreadState() const;
|
||||||
|
|
|
@ -42,10 +42,12 @@ static void RestartCore(const std::weak_ptr<HW::GBA::Core>& core, std::string_vi
|
||||||
auto& info = Config::MAIN_GBA_ROM_PATHS[core_ptr->GetCoreInfo().device_number];
|
auto& info = Config::MAIN_GBA_ROM_PATHS[core_ptr->GetCoreInfo().device_number];
|
||||||
core_ptr->Stop();
|
core_ptr->Stop();
|
||||||
Config::SetCurrent(info, rom_path);
|
Config::SetCurrent(info, rom_path);
|
||||||
if (core_ptr->Start(CoreTiming::GetTicks()))
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
if (core_ptr->Start(core_timing.GetTicks()))
|
||||||
return;
|
return;
|
||||||
Config::SetCurrent(info, Config::GetBase(info));
|
Config::SetCurrent(info, Config::GetBase(info));
|
||||||
core_ptr->Start(CoreTiming::GetTicks());
|
core_ptr->Start(core_timing.GetTicks());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "Core/HW/SI/SI.h"
|
#include "Core/HW/SI/SI.h"
|
||||||
#include "Core/HW/SI/SI_Device.h"
|
#include "Core/HW/SI/SI_Device.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "InputCommon/GCPadStatus.h"
|
#include "InputCommon/GCPadStatus.h"
|
||||||
|
|
||||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||||
|
@ -419,10 +420,12 @@ void Init()
|
||||||
|
|
||||||
if (Core::GetState() != Core::State::Uninitialized && Core::GetState() != Core::State::Starting)
|
if (Core::GetState() != Core::State::Uninitialized && Core::GetState() != Core::State::Starting)
|
||||||
{
|
{
|
||||||
if ((CoreTiming::GetTicks() - s_last_init) < SystemTimers::GetTicksPerSecond())
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
if ((core_timing.GetTicks() - s_last_init) < SystemTimers::GetTicksPerSecond())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
s_last_init = CoreTiming::GetTicks();
|
s_last_init = core_timing.GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "Core/FifoPlayer/FifoRecorder.h"
|
#include "Core/FifoPlayer/FifoRecorder.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/HW/VideoInterface.h"
|
#include "Core/HW/VideoInterface.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
#include "VideoCommon/BPFunctions.h"
|
#include "VideoCommon/BPFunctions.h"
|
||||||
#include "VideoCommon/BPMemory.h"
|
#include "VideoCommon/BPMemory.h"
|
||||||
|
@ -324,7 +325,8 @@ static void BPWritten(const BPCmd& bp, int cycles_into_future)
|
||||||
if (g_ActiveConfig.bImmediateXFB)
|
if (g_ActiveConfig.bImmediateXFB)
|
||||||
{
|
{
|
||||||
// below div two to convert from bytes to pixels - it expects width, not stride
|
// below div two to convert from bytes to pixels - it expects width, not stride
|
||||||
g_renderer->Swap(destAddr, destStride / 2, destStride, height, CoreTiming::GetTicks());
|
g_renderer->Swap(destAddr, destStride / 2, destStride, height,
|
||||||
|
Core::System::GetInstance().GetCoreTiming().GetTicks());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -149,7 +149,8 @@ void Init()
|
||||||
s_interrupt_set.Clear();
|
s_interrupt_set.Clear();
|
||||||
s_interrupt_waiting.Clear();
|
s_interrupt_waiting.Clear();
|
||||||
|
|
||||||
et_UpdateInterrupts = CoreTiming::RegisterEvent("CPInterrupt", UpdateInterrupts_Wrapper);
|
et_UpdateInterrupts = Core::System::GetInstance().GetCoreTiming().RegisterEvent(
|
||||||
|
"CPInterrupt", UpdateInterrupts_Wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetPhysicalAddressMask()
|
u32 GetPhysicalAddressMask()
|
||||||
|
@ -406,7 +407,7 @@ void GatherPipeBursted()
|
||||||
|
|
||||||
// If the game is running close to overflowing, make the exception checking more frequent.
|
// If the game is running close to overflowing, make the exception checking more frequent.
|
||||||
if (fifo.bFF_HiWatermark.load(std::memory_order_relaxed) != 0)
|
if (fifo.bFF_HiWatermark.load(std::memory_order_relaxed) != 0)
|
||||||
CoreTiming::ForceExceptionCheck(0);
|
Core::System::GetInstance().GetCoreTiming().ForceExceptionCheck(0);
|
||||||
|
|
||||||
fifo.CPReadWriteDistance.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst);
|
fifo.CPReadWriteDistance.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst);
|
||||||
|
|
||||||
|
@ -445,7 +446,7 @@ void UpdateInterrupts(u64 userdata)
|
||||||
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Interrupt cleared");
|
DEBUG_LOG_FMT(COMMANDPROCESSOR, "Interrupt cleared");
|
||||||
ProcessorInterface::SetInterrupt(INT_CAUSE_CP, false);
|
ProcessorInterface::SetInterrupt(INT_CAUSE_CP, false);
|
||||||
}
|
}
|
||||||
CoreTiming::ForceExceptionCheck(0);
|
Core::System::GetInstance().GetCoreTiming().ForceExceptionCheck(0);
|
||||||
s_interrupt_waiting.Clear();
|
s_interrupt_waiting.Clear();
|
||||||
Fifo::RunGpu();
|
Fifo::RunGpu();
|
||||||
}
|
}
|
||||||
|
@ -453,7 +454,10 @@ void UpdateInterrupts(u64 userdata)
|
||||||
void UpdateInterruptsFromVideoBackend(u64 userdata)
|
void UpdateInterruptsFromVideoBackend(u64 userdata)
|
||||||
{
|
{
|
||||||
if (!Fifo::UseDeterministicGPUThread())
|
if (!Fifo::UseDeterministicGPUThread())
|
||||||
CoreTiming::ScheduleEvent(0, et_UpdateInterrupts, userdata, CoreTiming::FromThread::NON_CPU);
|
{
|
||||||
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(0, et_UpdateInterrupts, userdata,
|
||||||
|
CoreTiming::FromThread::NON_CPU);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInterruptWaiting()
|
bool IsInterruptWaiting()
|
||||||
|
|
|
@ -448,7 +448,8 @@ bool AtBreakpoint()
|
||||||
|
|
||||||
void RunGpu()
|
void RunGpu()
|
||||||
{
|
{
|
||||||
const bool is_dual_core = Core::System::GetInstance().IsDualCoreMode();
|
auto& system = Core::System::GetInstance();
|
||||||
|
const bool is_dual_core = system.IsDualCoreMode();
|
||||||
|
|
||||||
// wake up GPU thread
|
// wake up GPU thread
|
||||||
if (is_dual_core && !s_use_deterministic_gpu_thread)
|
if (is_dual_core && !s_use_deterministic_gpu_thread)
|
||||||
|
@ -462,7 +463,8 @@ void RunGpu()
|
||||||
if (s_syncing_suspended)
|
if (s_syncing_suspended)
|
||||||
{
|
{
|
||||||
s_syncing_suspended = false;
|
s_syncing_suspended = false;
|
||||||
CoreTiming::ScheduleEvent(GPU_TIME_SLOT_SIZE, s_event_sync_gpu, GPU_TIME_SLOT_SIZE);
|
system.GetCoreTiming().ScheduleEvent(GPU_TIME_SLOT_SIZE, s_event_sync_gpu,
|
||||||
|
GPU_TIME_SLOT_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,7 +613,7 @@ static void SyncGPUCallback(Core::System& system, u64 ticks, s64 cyclesLate)
|
||||||
|
|
||||||
s_syncing_suspended = next < 0;
|
s_syncing_suspended = next < 0;
|
||||||
if (!s_syncing_suspended)
|
if (!s_syncing_suspended)
|
||||||
CoreTiming::ScheduleEvent(next, s_event_sync_gpu, next);
|
system.GetCoreTiming().ScheduleEvent(next, s_event_sync_gpu, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncGPUForRegisterAccess()
|
void SyncGPUForRegisterAccess()
|
||||||
|
@ -627,7 +629,8 @@ void SyncGPUForRegisterAccess()
|
||||||
// Initialize GPU - CPU thread syncing, this gives us a deterministic way to start the GPU thread.
|
// Initialize GPU - CPU thread syncing, this gives us a deterministic way to start the GPU thread.
|
||||||
void Prepare()
|
void Prepare()
|
||||||
{
|
{
|
||||||
s_event_sync_gpu = CoreTiming::RegisterEvent("SyncGPUCallback", SyncGPUCallback);
|
s_event_sync_gpu =
|
||||||
|
Core::System::GetInstance().GetCoreTiming().RegisterEvent("SyncGPUCallback", SyncGPUCallback);
|
||||||
s_syncing_suspended = true;
|
s_syncing_suspended = true;
|
||||||
}
|
}
|
||||||
} // namespace Fifo
|
} // namespace Fifo
|
||||||
|
|
|
@ -202,8 +202,8 @@ void Init()
|
||||||
s_signal_token_interrupt = false;
|
s_signal_token_interrupt = false;
|
||||||
s_signal_finish_interrupt = false;
|
s_signal_finish_interrupt = false;
|
||||||
|
|
||||||
et_SetTokenFinishOnMainThread =
|
et_SetTokenFinishOnMainThread = Core::System::GetInstance().GetCoreTiming().RegisterEvent(
|
||||||
CoreTiming::RegisterEvent("SetTokenFinish", SetTokenFinish_OnMainThread);
|
"SetTokenFinish", SetTokenFinish_OnMainThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
|
@ -341,7 +341,8 @@ static void RaiseEvent(int cycles_into_future)
|
||||||
// games time to setup any interrupt state
|
// games time to setup any interrupt state
|
||||||
cycles = std::max(500, cycles_into_future);
|
cycles = std::max(500, cycles_into_future);
|
||||||
}
|
}
|
||||||
CoreTiming::ScheduleEvent(cycles, et_SetTokenFinishOnMainThread, 0, from);
|
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(cycles, et_SetTokenFinishOnMainThread,
|
||||||
|
0, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetToken
|
// SetToken
|
||||||
|
|
|
@ -49,7 +49,9 @@ public:
|
||||||
Config::Init();
|
Config::Init();
|
||||||
SConfig::Init();
|
SConfig::Init();
|
||||||
PowerPC::Init(PowerPC::CPUCore::Interpreter);
|
PowerPC::Init(PowerPC::CPUCore::Interpreter);
|
||||||
CoreTiming::Init();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.Init();
|
||||||
}
|
}
|
||||||
~ScopeInit()
|
~ScopeInit()
|
||||||
{
|
{
|
||||||
|
@ -57,7 +59,9 @@ public:
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CoreTiming::Shutdown();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.Shutdown();
|
||||||
PowerPC::Shutdown();
|
PowerPC::Shutdown();
|
||||||
SConfig::Shutdown();
|
SConfig::Shutdown();
|
||||||
Config::Shutdown();
|
Config::Shutdown();
|
||||||
|
@ -78,7 +82,9 @@ static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0,
|
||||||
s_lateness = expected_lateness;
|
s_lateness = expected_lateness;
|
||||||
|
|
||||||
PowerPC::ppcState.downcount = cpu_downcount; // Pretend we executed X cycles of instructions.
|
PowerPC::ppcState.downcount = cpu_downcount; // Pretend we executed X cycles of instructions.
|
||||||
CoreTiming::Advance();
|
auto& system = Core::System::GetInstance();
|
||||||
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
core_timing.Advance();
|
||||||
|
|
||||||
EXPECT_EQ(decltype(s_callbacks_ran_flags)().set(idx), s_callbacks_ran_flags);
|
EXPECT_EQ(decltype(s_callbacks_ran_flags)().set(idx), s_callbacks_ran_flags);
|
||||||
EXPECT_EQ(downcount, PowerPC::ppcState.downcount);
|
EXPECT_EQ(downcount, PowerPC::ppcState.downcount);
|
||||||
|
@ -89,25 +95,28 @@ TEST(CoreTiming, BasicOrder)
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
ASSERT_TRUE(guard.UserDirectoryExists());
|
ASSERT_TRUE(guard.UserDirectoryExists());
|
||||||
|
|
||||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
|
||||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
|
CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
|
CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
|
CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||||
|
CoreTiming::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||||
|
CoreTiming::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
// D -> B -> C -> A -> E
|
// D -> B -> C -> A -> E
|
||||||
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||||
EXPECT_EQ(1000, PowerPC::ppcState.downcount);
|
EXPECT_EQ(1000, PowerPC::ppcState.downcount);
|
||||||
CoreTiming::ScheduleEvent(500, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
|
||||||
EXPECT_EQ(500, PowerPC::ppcState.downcount);
|
EXPECT_EQ(500, PowerPC::ppcState.downcount);
|
||||||
CoreTiming::ScheduleEvent(800, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
|
||||||
EXPECT_EQ(500, PowerPC::ppcState.downcount);
|
EXPECT_EQ(500, PowerPC::ppcState.downcount);
|
||||||
CoreTiming::ScheduleEvent(100, cb_d, CB_IDS[3]);
|
core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
|
||||||
EXPECT_EQ(100, PowerPC::ppcState.downcount);
|
EXPECT_EQ(100, PowerPC::ppcState.downcount);
|
||||||
CoreTiming::ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
|
||||||
EXPECT_EQ(100, PowerPC::ppcState.downcount);
|
EXPECT_EQ(100, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
AdvanceAndCheck(3, 400);
|
AdvanceAndCheck(3, 400);
|
||||||
|
@ -140,27 +149,30 @@ TEST(CoreTiming, SharedSlot)
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
ASSERT_TRUE(guard.UserDirectoryExists());
|
ASSERT_TRUE(guard.UserDirectoryExists());
|
||||||
|
|
||||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", FifoCallback<0>);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", FifoCallback<1>);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", FifoCallback<2>);
|
|
||||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", FifoCallback<3>);
|
|
||||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", FifoCallback<4>);
|
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", FifoCallback<0>);
|
||||||
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", FifoCallback<1>);
|
||||||
CoreTiming::ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", FifoCallback<2>);
|
||||||
CoreTiming::ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
CoreTiming::EventType* cb_d = core_timing.RegisterEvent("callbackD", FifoCallback<3>);
|
||||||
CoreTiming::ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
CoreTiming::EventType* cb_e = core_timing.RegisterEvent("callbackE", FifoCallback<4>);
|
||||||
|
|
||||||
|
core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
|
||||||
|
core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||||
|
core_timing.ScheduleEvent(1000, cb_c, CB_IDS[2]);
|
||||||
|
core_timing.ScheduleEvent(1000, cb_d, CB_IDS[3]);
|
||||||
|
core_timing.ScheduleEvent(1000, cb_e, CB_IDS[4]);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
EXPECT_EQ(1000, PowerPC::ppcState.downcount);
|
EXPECT_EQ(1000, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
s_callbacks_ran_flags = 0;
|
s_callbacks_ran_flags = 0;
|
||||||
s_counter = 0;
|
s_counter = 0;
|
||||||
s_lateness = 0;
|
s_lateness = 0;
|
||||||
PowerPC::ppcState.downcount = 0;
|
PowerPC::ppcState.downcount = 0;
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
EXPECT_EQ(MAX_SLICE_LENGTH, PowerPC::ppcState.downcount);
|
EXPECT_EQ(MAX_SLICE_LENGTH, PowerPC::ppcState.downcount);
|
||||||
EXPECT_EQ(0x1FULL, s_callbacks_ran_flags.to_ullong());
|
EXPECT_EQ(0x1FULL, s_callbacks_ran_flags.to_ullong());
|
||||||
}
|
}
|
||||||
|
@ -170,14 +182,17 @@ TEST(CoreTiming, PredictableLateness)
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
ASSERT_TRUE(guard.UserDirectoryExists());
|
ASSERT_TRUE(guard.UserDirectoryExists());
|
||||||
|
|
||||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
|
|
||||||
|
CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
|
CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||||
CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||||
|
|
||||||
AdvanceAndCheck(0, 90, 10, -10); // (100 - 10)
|
AdvanceAndCheck(0, 90, 10, -10); // (100 - 10)
|
||||||
AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50);
|
AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50);
|
||||||
|
@ -194,7 +209,10 @@ static void RescheduleCallback(Core::System& system, u64 userdata, s64 lateness)
|
||||||
EXPECT_EQ(s_lateness, lateness);
|
EXPECT_EQ(s_lateness, lateness);
|
||||||
|
|
||||||
if (s_reschedules > 0)
|
if (s_reschedules > 0)
|
||||||
CoreTiming::ScheduleEvent(1000, reinterpret_cast<CoreTiming::EventType*>(userdata), userdata);
|
{
|
||||||
|
system.GetCoreTiming().ScheduleEvent(1000, reinterpret_cast<CoreTiming::EventType*>(userdata),
|
||||||
|
userdata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // namespace ChainSchedulingTest
|
} // namespace ChainSchedulingTest
|
||||||
|
|
||||||
|
@ -205,19 +223,22 @@ TEST(CoreTiming, ChainScheduling)
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
ASSERT_TRUE(guard.UserDirectoryExists());
|
ASSERT_TRUE(guard.UserDirectoryExists());
|
||||||
|
|
||||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
|
||||||
|
CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
|
CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
|
CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||||
CoreTiming::EventType* cb_rs =
|
CoreTiming::EventType* cb_rs =
|
||||||
CoreTiming::RegisterEvent("callbackReschedule", RescheduleCallback);
|
core_timing.RegisterEvent("callbackReschedule", RescheduleCallback);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(800, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]);
|
||||||
CoreTiming::ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
|
||||||
CoreTiming::ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]);
|
||||||
CoreTiming::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
|
||||||
EXPECT_EQ(800, PowerPC::ppcState.downcount);
|
EXPECT_EQ(800, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
s_reschedules = 3;
|
s_reschedules = 3;
|
||||||
|
@ -226,14 +247,14 @@ TEST(CoreTiming, ChainScheduling)
|
||||||
EXPECT_EQ(2, s_reschedules);
|
EXPECT_EQ(2, s_reschedules);
|
||||||
|
|
||||||
PowerPC::ppcState.downcount = 0;
|
PowerPC::ppcState.downcount = 0;
|
||||||
CoreTiming::Advance(); // cb_rs
|
core_timing.Advance(); // cb_rs
|
||||||
EXPECT_EQ(1, s_reschedules);
|
EXPECT_EQ(1, s_reschedules);
|
||||||
EXPECT_EQ(200, PowerPC::ppcState.downcount);
|
EXPECT_EQ(200, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
AdvanceAndCheck(2, 800); // cb_c
|
AdvanceAndCheck(2, 800); // cb_c
|
||||||
|
|
||||||
PowerPC::ppcState.downcount = 0;
|
PowerPC::ppcState.downcount = 0;
|
||||||
CoreTiming::Advance(); // cb_rs
|
core_timing.Advance(); // cb_rs
|
||||||
EXPECT_EQ(0, s_reschedules);
|
EXPECT_EQ(0, s_reschedules);
|
||||||
EXPECT_EQ(MAX_SLICE_LENGTH, PowerPC::ppcState.downcount);
|
EXPECT_EQ(MAX_SLICE_LENGTH, PowerPC::ppcState.downcount);
|
||||||
}
|
}
|
||||||
|
@ -247,7 +268,7 @@ static void ChainCallback(Core::System& system, u64 userdata, s64 lateness)
|
||||||
EXPECT_EQ(CB_IDS[0] + 1, userdata);
|
EXPECT_EQ(CB_IDS[0] + 1, userdata);
|
||||||
EXPECT_EQ(0, lateness);
|
EXPECT_EQ(0, lateness);
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(-1000, s_cb_next, userdata - 1);
|
system.GetCoreTiming().ScheduleEvent(-1000, s_cb_next, userdata - 1);
|
||||||
}
|
}
|
||||||
} // namespace ScheduleIntoPastTest
|
} // namespace ScheduleIntoPastTest
|
||||||
|
|
||||||
|
@ -261,14 +282,17 @@ TEST(CoreTiming, ScheduleIntoPast)
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
ASSERT_TRUE(guard.UserDirectoryExists());
|
ASSERT_TRUE(guard.UserDirectoryExists());
|
||||||
|
|
||||||
s_cb_next = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
CoreTiming::EventType* cb_chain = CoreTiming::RegisterEvent("callbackChain", ChainCallback);
|
|
||||||
|
s_cb_next = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
|
CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
|
CoreTiming::EventType* cb_chain = core_timing.RegisterEvent("callbackChain", ChainCallback);
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(1000, cb_chain, CB_IDS[0] + 1);
|
core_timing.ScheduleEvent(1000, cb_chain, CB_IDS[0] + 1);
|
||||||
EXPECT_EQ(1000, PowerPC::ppcState.downcount);
|
EXPECT_EQ(1000, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
AdvanceAndCheck(0, MAX_SLICE_LENGTH, 1000); // Run cb_chain into late cb_a
|
AdvanceAndCheck(0, MAX_SLICE_LENGTH, 1000); // Run cb_chain into late cb_a
|
||||||
|
@ -280,9 +304,9 @@ TEST(CoreTiming, ScheduleIntoPast)
|
||||||
// the stale value, i.e. effectively half-way through the previous slice.
|
// the stale value, i.e. effectively half-way through the previous slice.
|
||||||
// NOTE: We're only testing that the scheduler doesn't break, not whether this makes sense.
|
// NOTE: We're only testing that the scheduler doesn't break, not whether this makes sense.
|
||||||
Core::UndeclareAsCPUThread();
|
Core::UndeclareAsCPUThread();
|
||||||
auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals();
|
auto& core_timing_globals = core_timing.GetGlobals();
|
||||||
core_timing_globals.global_timer -= 1000;
|
core_timing_globals.global_timer -= 1000;
|
||||||
CoreTiming::ScheduleEvent(0, cb_b, CB_IDS[1], CoreTiming::FromThread::NON_CPU);
|
core_timing.ScheduleEvent(0, cb_b, CB_IDS[1], CoreTiming::FromThread::NON_CPU);
|
||||||
core_timing_globals.global_timer += 1000;
|
core_timing_globals.global_timer += 1000;
|
||||||
Core::DeclareAsCPUThread();
|
Core::DeclareAsCPUThread();
|
||||||
AdvanceAndCheck(1, MAX_SLICE_LENGTH, MAX_SLICE_LENGTH + 1000);
|
AdvanceAndCheck(1, MAX_SLICE_LENGTH, MAX_SLICE_LENGTH + 1000);
|
||||||
|
@ -290,7 +314,7 @@ TEST(CoreTiming, ScheduleIntoPast)
|
||||||
// Schedule directly into the past from the CPU.
|
// Schedule directly into the past from the CPU.
|
||||||
// This shouldn't happen in practice, but it's best if we don't mess up the slice length and
|
// This shouldn't happen in practice, but it's best if we don't mess up the slice length and
|
||||||
// downcount if we do.
|
// downcount if we do.
|
||||||
CoreTiming::ScheduleEvent(-1000, s_cb_next, CB_IDS[0]);
|
core_timing.ScheduleEvent(-1000, s_cb_next, CB_IDS[0]);
|
||||||
EXPECT_EQ(0, PowerPC::ppcState.downcount);
|
EXPECT_EQ(0, PowerPC::ppcState.downcount);
|
||||||
AdvanceAndCheck(0, MAX_SLICE_LENGTH, 1000);
|
AdvanceAndCheck(0, MAX_SLICE_LENGTH, 1000);
|
||||||
}
|
}
|
||||||
|
@ -300,11 +324,14 @@ TEST(CoreTiming, Overclocking)
|
||||||
ScopeInit guard;
|
ScopeInit guard;
|
||||||
ASSERT_TRUE(guard.UserDirectoryExists());
|
ASSERT_TRUE(guard.UserDirectoryExists());
|
||||||
|
|
||||||
CoreTiming::EventType* cb_a = CoreTiming::RegisterEvent("callbackA", CallbackTemplate<0>);
|
auto& system = Core::System::GetInstance();
|
||||||
CoreTiming::EventType* cb_b = CoreTiming::RegisterEvent("callbackB", CallbackTemplate<1>);
|
auto& core_timing = system.GetCoreTiming();
|
||||||
CoreTiming::EventType* cb_c = CoreTiming::RegisterEvent("callbackC", CallbackTemplate<2>);
|
|
||||||
CoreTiming::EventType* cb_d = CoreTiming::RegisterEvent("callbackD", CallbackTemplate<3>);
|
CoreTiming::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
|
||||||
CoreTiming::EventType* cb_e = CoreTiming::RegisterEvent("callbackE", CallbackTemplate<4>);
|
CoreTiming::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
|
||||||
|
CoreTiming::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
|
||||||
|
CoreTiming::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
|
||||||
|
CoreTiming::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
|
||||||
|
|
||||||
// Overclock
|
// Overclock
|
||||||
Config::SetCurrent(Config::MAIN_OVERCLOCK_ENABLE, true);
|
Config::SetCurrent(Config::MAIN_OVERCLOCK_ENABLE, true);
|
||||||
|
@ -312,13 +339,13 @@ TEST(CoreTiming, Overclocking)
|
||||||
|
|
||||||
// Enter slice 0
|
// Enter slice 0
|
||||||
// Updates s_last_OC_factor.
|
// Updates s_last_OC_factor.
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||||
CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||||
CoreTiming::ScheduleEvent(400, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(400, cb_c, CB_IDS[2]);
|
||||||
CoreTiming::ScheduleEvent(800, cb_d, CB_IDS[3]);
|
core_timing.ScheduleEvent(800, cb_d, CB_IDS[3]);
|
||||||
CoreTiming::ScheduleEvent(1600, cb_e, CB_IDS[4]);
|
core_timing.ScheduleEvent(1600, cb_e, CB_IDS[4]);
|
||||||
EXPECT_EQ(200, PowerPC::ppcState.downcount);
|
EXPECT_EQ(200, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
AdvanceAndCheck(0, 200); // (200 - 100) * 2
|
AdvanceAndCheck(0, 200); // (200 - 100) * 2
|
||||||
|
@ -329,13 +356,13 @@ TEST(CoreTiming, Overclocking)
|
||||||
|
|
||||||
// Underclock
|
// Underclock
|
||||||
Config::SetCurrent(Config::MAIN_OVERCLOCK, 0.5f);
|
Config::SetCurrent(Config::MAIN_OVERCLOCK, 0.5f);
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||||
CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||||
CoreTiming::ScheduleEvent(400, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(400, cb_c, CB_IDS[2]);
|
||||||
CoreTiming::ScheduleEvent(800, cb_d, CB_IDS[3]);
|
core_timing.ScheduleEvent(800, cb_d, CB_IDS[3]);
|
||||||
CoreTiming::ScheduleEvent(1600, cb_e, CB_IDS[4]);
|
core_timing.ScheduleEvent(1600, cb_e, CB_IDS[4]);
|
||||||
EXPECT_EQ(50, PowerPC::ppcState.downcount);
|
EXPECT_EQ(50, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
AdvanceAndCheck(0, 50); // (200 - 100) / 2
|
AdvanceAndCheck(0, 50); // (200 - 100) / 2
|
||||||
|
@ -346,13 +373,13 @@ TEST(CoreTiming, Overclocking)
|
||||||
|
|
||||||
// Try switching the clock mid-emulation
|
// Try switching the clock mid-emulation
|
||||||
Config::SetCurrent(Config::MAIN_OVERCLOCK, 1.0f);
|
Config::SetCurrent(Config::MAIN_OVERCLOCK, 1.0f);
|
||||||
CoreTiming::Advance();
|
core_timing.Advance();
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(100, cb_a, CB_IDS[0]);
|
core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
|
||||||
CoreTiming::ScheduleEvent(200, cb_b, CB_IDS[1]);
|
core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
|
||||||
CoreTiming::ScheduleEvent(400, cb_c, CB_IDS[2]);
|
core_timing.ScheduleEvent(400, cb_c, CB_IDS[2]);
|
||||||
CoreTiming::ScheduleEvent(800, cb_d, CB_IDS[3]);
|
core_timing.ScheduleEvent(800, cb_d, CB_IDS[3]);
|
||||||
CoreTiming::ScheduleEvent(1600, cb_e, CB_IDS[4]);
|
core_timing.ScheduleEvent(1600, cb_e, CB_IDS[4]);
|
||||||
EXPECT_EQ(100, PowerPC::ppcState.downcount);
|
EXPECT_EQ(100, PowerPC::ppcState.downcount);
|
||||||
|
|
||||||
AdvanceAndCheck(0, 100); // (200 - 100)
|
AdvanceAndCheck(0, 100); // (200 - 100)
|
||||||
|
|
Loading…
Reference in New Issue