From 9d79fc7aa690d3cbfbbca7df8ea0411145ee240c Mon Sep 17 00:00:00 2001 From: ShizZy Date: Tue, 1 Oct 2013 19:10:47 -0400 Subject: [PATCH] added core_timing and system modules to core vcproj --- src/core/core.vcxproj | 3 + src/core/core.vcxproj.filters | 3 + src/core/src/core_timing.cpp | 639 ++++++++++++++++++++++++++++++++++ src/core/src/core_timing.h | 125 +++++++ src/core/src/system.cpp | 52 +++ src/core/src/system.h | 3 +- 6 files changed, 823 insertions(+), 2 deletions(-) create mode 100644 src/core/src/core_timing.cpp create mode 100644 src/core/src/core_timing.h create mode 100644 src/core/src/system.cpp diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index d707a566c..d3b26b661 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -141,11 +141,13 @@ + + @@ -162,6 +164,7 @@ + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 039373047..de3aab97f 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -20,6 +20,8 @@ file_sys + + @@ -88,6 +90,7 @@ file_sys + diff --git a/src/core/src/core_timing.cpp b/src/core/src/core_timing.cpp new file mode 100644 index 000000000..73dd9b549 --- /dev/null +++ b/src/core/src/core_timing.cpp @@ -0,0 +1,639 @@ +// Copyright (c) 2012- PPSSPP Project / Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + + +#include +#include + +#include "msg_handler.h" +#include "std_mutex.h" +#include "atomic.h" +#include "core_timing.h" +#include "core.h" +#include "chunk_file.h" + +//#include "HLE/sceKernelThread.h" + +int g_clock_rate_arm11 = 268123480; + +// is this really necessary? +#define INITIAL_SLICE_LENGTH 20000 +#define MAX_SLICE_LENGTH 100000000 + +namespace CoreTiming +{ + +struct EventType +{ + EventType() {} + + EventType(TimedCallback cb, const char *n) + : callback(cb), name(n) {} + + TimedCallback callback; + const char *name; +}; + +std::vector event_types; + +struct BaseEvent +{ + s64 time; + u64 userdata; + int type; +// Event *next; +}; + +typedef LinkedListItem Event; + +Event *first; +Event *tsFirst; +Event *tsLast; + +// event pools +Event *eventPool = 0; +Event *eventTsPool = 0; +int allocatedTsEvents = 0; +// Optimization to skip MoveEvents when possible. +volatile u32 hasTsEvents = false; + +// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block +// as we can already reach that structure through a register. +int slicelength; + +MEMORY_ALIGNED16(s64) globalTimer; +s64 idledCycles; + +static std::recursive_mutex externalEventSection; + +// Warning: not included in save state. +void (*advanceCallback)(int cyclesExecuted) = NULL; + +void SetClockFrequencyMHz(int cpuMhz) +{ + g_clock_rate_arm11 = cpuMhz * 1000000; + // TODO: Rescale times of scheduled events? +} + +int GetClockFrequencyMHz() +{ + return g_clock_rate_arm11 / 1000000; +} + + +Event* GetNewEvent() +{ + if(!eventPool) + return new Event; + + Event* ev = eventPool; + eventPool = ev->next; + return ev; +} + +Event* GetNewTsEvent() +{ + allocatedTsEvents++; + + if(!eventTsPool) + return new Event; + + Event* ev = eventTsPool; + eventTsPool = ev->next; + return ev; +} + +void FreeEvent(Event* ev) +{ + ev->next = eventPool; + eventPool = ev; +} + +void FreeTsEvent(Event* ev) +{ + ev->next = eventTsPool; + eventTsPool = ev; + allocatedTsEvents--; +} + +int RegisterEvent(const char *name, TimedCallback callback) +{ + event_types.push_back(EventType(callback, name)); + return (int)event_types.size() - 1; +} + +void AntiCrashCallback(u64 userdata, int cyclesLate) +{ + ERROR_LOG(TIME, "Savestate broken: an unregistered event was called."); + Core::Halt("invalid timing events"); +} + +void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) +{ + if (event_type >= (int) event_types.size()) + event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT")); + + event_types[event_type] = EventType(callback, name); +} + +void UnregisterAllEvents() +{ + if (first) + PanicAlert("Cannot unregister events with events pending"); + event_types.clear(); +} + +void Init() +{ + //currentMIPS->downcount = INITIAL_SLICE_LENGTH; + //slicelength = INITIAL_SLICE_LENGTH; + globalTimer = 0; + idledCycles = 0; + hasTsEvents = 0; +} + +void Shutdown() +{ + MoveEvents(); + ClearPendingEvents(); + UnregisterAllEvents(); + + while(eventPool) + { + Event *ev = eventPool; + eventPool = ev->next; + delete ev; + } + + std::lock_guard lk(externalEventSection); + while(eventTsPool) + { + Event *ev = eventTsPool; + eventTsPool = ev->next; + delete ev; + } +} + +u64 GetTicks() +{ + ERROR_LOG(TIME, "Unimplemented function!"); + return 0; + //return (u64)globalTimer + slicelength - currentMIPS->downcount; +} + +u64 GetIdleTicks() +{ + return (u64)idledCycles; +} + + +// This is to be called when outside threads, such as the graphics thread, wants to +// schedule things to be executed on the main thread. +void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata) +{ + std::lock_guard lk(externalEventSection); + Event *ne = GetNewTsEvent(); + ne->time = GetTicks() + cyclesIntoFuture; + ne->type = event_type; + ne->next = 0; + ne->userdata = userdata; + if(!tsFirst) + tsFirst = ne; + if(tsLast) + tsLast->next = ne; + tsLast = ne; + + Common::AtomicStoreRelease(hasTsEvents, 1); +} + +// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread +// in which case the event will get handled immediately, before returning. +void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) +{ + if(false) //Core::IsCPUThread()) + { + std::lock_guard lk(externalEventSection); + event_types[event_type].callback(userdata, 0); + } + else + ScheduleEvent_Threadsafe(0, event_type, userdata); +} + +void ClearPendingEvents() +{ + while (first) + { + Event *e = first->next; + FreeEvent(first); + first = e; + } +} + +void AddEventToQueue(Event* ne) +{ + Event* prev = NULL; + Event** pNext = &first; + for(;;) + { + Event*& next = *pNext; + if(!next || ne->time < next->time) + { + ne->next = next; + next = ne; + break; + } + prev = next; + pNext = &prev->next; + } +} + +// This must be run ONLY from within the cpu thread +// cyclesIntoFuture may be VERY inaccurate if called from anything else +// than Advance +void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) +{ + Event *ne = GetNewEvent(); + ne->userdata = userdata; + ne->type = event_type; + ne->time = GetTicks() + cyclesIntoFuture; + AddEventToQueue(ne); +} + +// Returns cycles left in timer. +s64 UnscheduleEvent(int event_type, u64 userdata) +{ + s64 result = 0; + if (!first) + return result; + while(first) + { + if (first->type == event_type && first->userdata == userdata) + { + result = first->time - globalTimer; + + Event *next = first->next; + FreeEvent(first); + first = next; + } + else + { + break; + } + } + if (!first) + return result; + Event *prev = first; + Event *ptr = prev->next; + while (ptr) + { + if (ptr->type == event_type && ptr->userdata == userdata) + { + result = ptr->time - globalTimer; + + prev->next = ptr->next; + FreeEvent(ptr); + ptr = prev->next; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } + + return result; +} + +s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) +{ + s64 result = 0; + std::lock_guard lk(externalEventSection); + if (!tsFirst) + return result; + while(tsFirst) + { + if (tsFirst->type == event_type && tsFirst->userdata == userdata) + { + result = tsFirst->time - globalTimer; + + Event *next = tsFirst->next; + FreeTsEvent(tsFirst); + tsFirst = next; + } + else + { + break; + } + } + if (!tsFirst) + { + tsLast = NULL; + return result; + } + + Event *prev = tsFirst; + Event *ptr = prev->next; + while (ptr) + { + if (ptr->type == event_type && ptr->userdata == userdata) + { + result = ptr->time - globalTimer; + + prev->next = ptr->next; + if (ptr == tsLast) + tsLast = prev; + FreeTsEvent(ptr); + ptr = prev->next; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } + + return result; +} + +// Warning: not included in save state. +void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)) +{ + advanceCallback = callback; +} + +bool IsScheduled(int event_type) +{ + if (!first) + return false; + Event *e = first; + while (e) { + if (e->type == event_type) + return true; + e = e->next; + } + return false; +} + +void RemoveEvent(int event_type) +{ + if (!first) + return; + while(first) + { + if (first->type == event_type) + { + Event *next = first->next; + FreeEvent(first); + first = next; + } + else + { + break; + } + } + if (!first) + return; + Event *prev = first; + Event *ptr = prev->next; + while (ptr) + { + if (ptr->type == event_type) + { + prev->next = ptr->next; + FreeEvent(ptr); + ptr = prev->next; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } +} + +void RemoveThreadsafeEvent(int event_type) +{ + std::lock_guard lk(externalEventSection); + if (!tsFirst) + { + return; + } + while(tsFirst) + { + if (tsFirst->type == event_type) + { + Event *next = tsFirst->next; + FreeTsEvent(tsFirst); + tsFirst = next; + } + else + { + break; + } + } + if (!tsFirst) + { + tsLast = NULL; + return; + } + Event *prev = tsFirst; + Event *ptr = prev->next; + while (ptr) + { + if (ptr->type == event_type) + { + prev->next = ptr->next; + if (ptr == tsLast) + tsLast = prev; + FreeTsEvent(ptr); + ptr = prev->next; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } +} + +void RemoveAllEvents(int event_type) +{ + RemoveThreadsafeEvent(event_type); + RemoveEvent(event_type); +} + +//This raise only the events required while the fifo is processing data +void ProcessFifoWaitEvents() +{ + while (first) + { + if (first->time <= globalTimer) + { +// LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", +// first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); + Event* evt = first; + first = first->next; + event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); + FreeEvent(evt); + } + else + { + break; + } + } +} + +void MoveEvents() +{ + Common::AtomicStoreRelease(hasTsEvents, 0); + + std::lock_guard lk(externalEventSection); + // Move events from async queue into main queue + while (tsFirst) + { + Event *next = tsFirst->next; + AddEventToQueue(tsFirst); + tsFirst = next; + } + tsLast = NULL; + + // Move free events to threadsafe pool + while(allocatedTsEvents > 0 && eventPool) + { + Event *ev = eventPool; + eventPool = ev->next; + ev->next = eventTsPool; + eventTsPool = ev; + allocatedTsEvents--; + } +} + +void Advance() +{ + ERROR_LOG(TIME, "Unimplemented function!"); + //int cyclesExecuted = slicelength - currentMIPS->downcount; + //globalTimer += cyclesExecuted; + //currentMIPS->downcount = slicelength; + + //if (Common::AtomicLoadAcquire(hasTsEvents)) + // MoveEvents(); + //ProcessFifoWaitEvents(); + + //if (!first) + //{ + // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); + // currentMIPS->downcount += 10000; + //} + //else + //{ + // slicelength = (int)(first->time - globalTimer); + // if (slicelength > MAX_SLICE_LENGTH) + // slicelength = MAX_SLICE_LENGTH; + // currentMIPS->downcount = slicelength; + //} + //if (advanceCallback) + // advanceCallback(cyclesExecuted); +} + +void LogPendingEvents() +{ + Event *ptr = first; + while (ptr) + { + //INFO_LOG(TIMER, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type); + ptr = ptr->next; + } +} + +void Idle(int maxIdle) +{ + ERROR_LOG(TIME, "Unimplemented function!"); + //int cyclesDown = currentMIPS->downcount; + //if (maxIdle != 0 && cyclesDown > maxIdle) + // cyclesDown = maxIdle; + + //if (first && cyclesDown > 0) + //{ + // int cyclesExecuted = slicelength - currentMIPS->downcount; + // int cyclesNextEvent = (int) (first->time - globalTimer); + + // if (cyclesNextEvent < cyclesExecuted + cyclesDown) + // { + // cyclesDown = cyclesNextEvent - cyclesExecuted; + // // Now, now... no time machines, please. + // if (cyclesDown < 0) + // cyclesDown = 0; + // } + //} + + //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f)); + + //idledCycles += cyclesDown; + //currentMIPS->downcount -= cyclesDown; + //if (currentMIPS->downcount == 0) + // currentMIPS->downcount = -1; +} + +std::string GetScheduledEventsSummary() +{ + Event *ptr = first; + std::string text = "Scheduled events\n"; + text.reserve(1000); + while (ptr) + { + unsigned int t = ptr->type; + if (t >= event_types.size()) + PanicAlert("Invalid event type"); // %i", t); + const char *name = event_types[ptr->type].name; + if (!name) + name = "[unknown]"; + char temp[512]; + sprintf(temp, "%s : %i %08x%08x\n", name, (int)ptr->time, (u32)(ptr->userdata >> 32), (u32)(ptr->userdata)); + text += temp; + ptr = ptr->next; + } + return text; +} + +void Event_DoState(PointerWrap &p, BaseEvent *ev) +{ + p.Do(*ev); +} + +void DoState(PointerWrap &p) +{ + std::lock_guard lk(externalEventSection); + + auto s = p.Section("CoreTiming", 1); + if (!s) + return; + + int n = (int) event_types.size(); + p.Do(n); + // These (should) be filled in later by the modules. + event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); + + p.DoLinkedList(first, (Event **) NULL); + p.DoLinkedList(tsFirst, &tsLast); + + p.Do(g_clock_rate_arm11); + p.Do(slicelength); + p.Do(globalTimer); + p.Do(idledCycles); +} + +} // namespace diff --git a/src/core/src/core_timing.h b/src/core/src/core_timing.h new file mode 100644 index 000000000..1ef17f6f8 --- /dev/null +++ b/src/core/src/core_timing.h @@ -0,0 +1,125 @@ +// Copyright (c) 2012- PPSSPP Project / Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#ifndef CORE_CORE_TIMING_H_ +#define CORE_CORE_TIMING_H_ + +// This is a system to schedule events into the emulated machine's future. Time is measured +// in main CPU clock cycles. + +// To schedule an event, you first have to register its type. This is where you pass in the +// callback. You then schedule events using the type id you get back. + +// See HW/SystemTimers.cpp for the main part of Dolphin's usage of this scheduler. + +// The int cyclesLate that the callbacks get is how many cycles late it was. +// So to schedule a new event on a regular basis: +// inside callback: +// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") + +#include "common.h" + +class PointerWrap; + +extern int g_clock_rate_arm11; + +inline s64 msToCycles(int ms) { + return g_clock_rate_arm11 / 1000 * ms; +} + +inline s64 msToCycles(float ms) { + return (s64)(g_clock_rate_arm11 * ms * (0.001f)); +} + +inline s64 msToCycles(double ms) { + return (s64)(g_clock_rate_arm11 * ms * (0.001)); +} + +inline s64 usToCycles(float us) { + return (s64)(g_clock_rate_arm11 * us * (0.000001f)); +} + +inline s64 usToCycles(int us) { + return (g_clock_rate_arm11 / 1000000 * (s64)us); +} + +inline s64 usToCycles(s64 us) { + return (g_clock_rate_arm11 / 1000000 * us); +} + +inline s64 usToCycles(u64 us) { + return (s64)(g_clock_rate_arm11 / 1000000 * us); +} + +inline s64 cyclesToUs(s64 cycles) { + return cycles / (g_clock_rate_arm11 / 1000000); +} + +namespace CoreTiming +{ + void Init(); + void Shutdown(); + + typedef void (*TimedCallback)(u64 userdata, int cyclesLate); + + u64 GetTicks(); + u64 GetIdleTicks(); + + // Returns the event_type identifier. + int RegisterEvent(const char *name, TimedCallback callback); + // For save states. + void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback); + void UnregisterAllEvents(); + + // userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, + // when we implement state saves. + void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata=0); + void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata=0); + void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata=0); + s64 UnscheduleEvent(int event_type, u64 userdata); + s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata); + + void RemoveEvent(int event_type); + void RemoveThreadsafeEvent(int event_type); + void RemoveAllEvents(int event_type); + bool IsScheduled(int event_type); + void Advance(); + void MoveEvents(); + void ProcessFifoWaitEvents(); + + // Pretend that the main CPU has executed enough cycles to reach the next event. + void Idle(int maxIdle = 0); + + // Clear all pending events. This should ONLY be done on exit or state load. + void ClearPendingEvents(); + + void LogPendingEvents(); + + // Warning: not included in save states. + void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)); + + std::string GetScheduledEventsSummary(); + + void DoState(PointerWrap &p); + + void SetClockFrequencyMHz(int cpuMhz); + int GetClockFrequencyMHz(); + extern int slicelength; + +}; // namespace + +#endif // CORE_CORE_TIMING_H_ diff --git a/src/core/src/system.cpp b/src/core/src/system.cpp new file mode 100644 index 000000000..36fdf028c --- /dev/null +++ b/src/core/src/system.cpp @@ -0,0 +1,52 @@ +/** +* Copyright (C) 2013 Citrus Emulator +* +* @file system.cpp +* @author ShizZy +* @date 2013-09-26 +* @brief Emulation of main system +* +* @section LICENSE +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details at +* http://www.gnu.org/copyleft/gpl.html +* +* Official project repository can be found at: +* http://code.google.com/p/gekko-gc-emu/ +*/ + +#include "core_timing.h" +#include "system.h" + +namespace System { + +extern volatile State g_state; +extern MetaFileSystem g_ctr_file_system; + +void UpdateState(State state) { +} + +void Init() { +} + +void RunLoopFor(int cycles) { + RunLoopUntil(CoreTiming::GetTicks() + cycles); +} + +void RunLoopUntil(u64 global_cycles) { +} + +void Shutdown() { + g_ctr_file_system.Shutdown(); +} + + + +} // namespace diff --git a/src/core/src/system.h b/src/core/src/system.h index d35515c66..b86a87164 100644 --- a/src/core/src/system.h +++ b/src/core/src/system.h @@ -31,8 +31,6 @@ namespace System { -extern MetaFileSystem g_ctr_file_system; - // State of the full emulator typedef enum { STATE_NULL = 0, ///< System is in null state, nothing initialized @@ -46,6 +44,7 @@ typedef enum { } State; extern volatile State g_state; +extern MetaFileSystem g_ctr_file_system; void UpdateState(State state); void Init();