aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--src/citra_qt/debugger/callstack.cpp3
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/thread_queue_list.h218
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/arm_interface.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp14
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h3
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp83
-rw-r--r--src/core/core.cpp19
-rw-r--r--src/core/core_timing.cpp709
-rw-r--r--src/core/core_timing.h77
-rw-r--r--src/core/hle/function_wrappers.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp6
-rw-r--r--src/core/hle/kernel/kernel.h1
-rw-r--r--src/core/hle/kernel/thread.cpp63
-rw-r--r--src/core/hle/kernel/thread.h19
-rw-r--r--src/core/hle/kernel/timer.cpp142
-rw-r--r--src/core/hle/kernel/timer.h47
-rw-r--r--src/core/hle/service/gsp_gpu.cpp5
-rw-r--r--src/core/hle/service/service.cpp14
-rw-r--r--src/core/hle/service/service.h15
-rw-r--r--src/core/hle/svc.cpp40
-rw-r--r--src/core/system.cpp4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp5
26 files changed, 868 insertions, 634 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 884520ce..2cba5e8a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,7 @@ else()
# As far as I can tell, there's no way to override the CMake defaults while leaving user
# changes intact, so we'll just clobber everything and say sorry.
message(STATUS "Cache compiler flags ignored, please edit CMakeFiles.txt to change the flags.")
+ # /MP - Multi-threaded compilation
# /MD - Multi-threaded runtime
# /Ox - Full optimization
# /Oi - Use intrinsic functions
@@ -24,7 +25,7 @@ else()
# /GR- - Disable RTTI
# /GS- - No stack buffer overflow checks
# /EHsc - C++-only exception handling semantics
- set(optimization_flags "/MD /Ox /Oi /Oy- /DNDEBUG /GR- /GS- /EHsc")
+ set(optimization_flags "/MP /MD /Ox /Oi /Oy- /DNDEBUG /GR- /GS- /EHsc")
# /Zi - Output debugging information
# /Zo - enahnced debug info for optimized builds
set(CMAKE_C_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE)
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index bcc5d214..274c5ccc 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -38,6 +38,9 @@ void CallstackWidget::OnCPUStepped()
{
ret_addr = Memory::Read32(addr);
call_addr = ret_addr - 4; //get call address???
+
+ if (Memory::GetPointer(call_addr) == nullptr)
+ break;
/* TODO (mattvail) clean me, move to debugger interface */
u32 insn = Memory::Read32(call_addr);
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 7ac30ad5..83ebb42d 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -22,6 +22,7 @@ static std::shared_ptr<Logger> global_logger;
SUB(Common, Memory) \
CLS(Core) \
SUB(Core, ARM11) \
+ SUB(Core, Timing) \
CLS(Config) \
CLS(Debug) \
SUB(Debug, Emulated) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 06b99b07..bda3d633 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -41,6 +41,7 @@ enum class Class : ClassType {
Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core
Core_ARM11, ///< ARM11 CPU core
+ Core_Timing, ///< CoreTiming functions
Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools
Debug_Emulated, ///< Debug messages from the emulated programs
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 4e1c0a21..444abf11 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -4,213 +4,143 @@
#pragma once
+#include <array>
+#include <deque>
+
+#include <boost/range/algorithm_ext/erase.hpp>
+
#include "common/common.h"
namespace Common {
-template<class IdType>
+template<class T, unsigned int N>
struct ThreadQueueList {
- // Number of queues (number of priority levels starting at 0.)
- static const int NUM_QUEUES = 128;
+ // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
+ // (dynamically resizable) circular buffers to remove their overhead when
+ // inserting and popping.
- // Initial number of threads a single queue can handle.
- static const int INITIAL_CAPACITY = 32;
+ typedef unsigned int Priority;
- struct Queue {
- // Next ever-been-used queue (worse priority.)
- Queue *next;
- // First valid item in data.
- int first;
- // One after last valid item in data.
- int end;
- // A too-large array with room on the front and end.
- IdType *data;
- // Size of data array.
- int capacity;
- };
+ // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
+ static const Priority NUM_QUEUES = N;
ThreadQueueList() {
- memset(queues, 0, sizeof(queues));
- first = invalid();
- }
-
- ~ThreadQueueList() {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data != nullptr)
- free(queues[i].data);
- }
+ first = nullptr;
}
// Only for debugging, returns priority level.
- int contains(const IdType uid) {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data == nullptr)
- continue;
-
- Queue *cur = &queues[i];
- for (int j = cur->first; j < cur->end; ++j)
- {
- if (cur->data[j] == uid)
- return i;
+ Priority contains(const T& uid) {
+ for (Priority i = 0; i < NUM_QUEUES; ++i) {
+ Queue& cur = queues[i];
+ if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
+ return i;
}
}
return -1;
}
- inline IdType pop_first() {
+ T pop_first() {
Queue *cur = first;
- while (cur != invalid())
- {
- if (cur->end - cur->first > 0)
- return cur->data[cur->first++];
- cur = cur->next;
+ while (cur != nullptr) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
}
- //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty.");
- return 0;
+ return T();
}
- inline IdType pop_first_better(u32 priority) {
+ T pop_first_better(Priority priority) {
Queue *cur = first;
Queue *stop = &queues[priority];
- while (cur < stop)
- {
- if (cur->end - cur->first > 0)
- return cur->data[cur->first++];
- cur = cur->next;
+ while (cur < stop) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
}
- return 0;
+ return T();
}
- inline void push_front(u32 priority, const IdType threadID) {
+ void push_front(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- cur->data[--cur->first] = threadID;
- if (cur->first == 0)
- rebalance(priority);
+ cur->data.push_front(thread_id);
}
- inline void push_back(u32 priority, const IdType threadID) {
+ void push_back(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- cur->data[cur->end++] = threadID;
- if (cur->end == cur->capacity)
- rebalance(priority);
+ cur->data.push_back(thread_id);
}
- inline void remove(u32 priority, const IdType threadID) {
+ void remove(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
-
- for (int i = cur->first; i < cur->end; ++i)
- {
- if (cur->data[i] == threadID)
- {
- int remaining = --cur->end - i;
- if (remaining > 0)
- memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType));
- return;
- }
- }
-
- // Wasn't there.
+ boost::remove_erase(cur->data, thread_id);
}
- inline void rotate(u32 priority) {
+ void rotate(Priority priority) {
Queue *cur = &queues[priority];
- //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
- if (cur->end - cur->first > 1)
- {
- cur->data[cur->end++] = cur->data[cur->first++];
- if (cur->end == cur->capacity)
- rebalance(priority);
+ if (cur->data.size() > 1) {
+ cur->data.push_back(std::move(cur->data.front()));
+ cur->data.pop_front();
}
}
- inline void clear() {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data != nullptr)
- free(queues[i].data);
- }
- memset(queues, 0, sizeof(queues));
- first = invalid();
+ void clear() {
+ queues.fill(Queue());
+ first = nullptr;
}
- inline bool empty(u32 priority) const {
+ bool empty(Priority priority) const {
const Queue *cur = &queues[priority];
- return cur->first == cur->end;
+ return cur->data.empty();
}
- inline void prepare(u32 priority) {
- Queue *cur = &queues[priority];
- if (cur->next == nullptr)
- link(priority, INITIAL_CAPACITY);
+ void prepare(Priority priority) {
+ Queue* cur = &queues[priority];
+ if (cur->next_nonempty == UnlinkedTag())
+ link(priority);
}
private:
- Queue *invalid() const {
- return (Queue *) -1;
+ struct Queue {
+ // Points to the next active priority, skipping over ones that have never been used.
+ Queue* next_nonempty = UnlinkedTag();
+ // Double-ended queue of threads in this priority level
+ std::deque<T> data;
+ };
+
+ /// Special tag used to mark priority levels that have never been used.
+ static Queue* UnlinkedTag() {
+ return reinterpret_cast<Queue*>(1);
}
- void link(u32 priority, int size) {
- //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once.");
-
- if (size <= INITIAL_CAPACITY)
- size = INITIAL_CAPACITY;
- else
- {
- int goal = size;
- size = INITIAL_CAPACITY;
- while (size < goal)
- size *= 2;
- }
+ void link(Priority priority) {
Queue *cur = &queues[priority];
- cur->data = (IdType *) malloc(sizeof(IdType) * size);
- cur->capacity = size;
- cur->first = size / 2;
- cur->end = size / 2;
-
- for (int i = (int) priority - 1; i >= 0; --i)
- {
- if (queues[i].next != nullptr)
- {
- cur->next = queues[i].next;
- queues[i].next = cur;
+
+ for (int i = priority - 1; i >= 0; --i) {
+ if (queues[i].next_nonempty != UnlinkedTag()) {
+ cur->next_nonempty = queues[i].next_nonempty;
+ queues[i].next_nonempty = cur;
return;
}
}
- cur->next = first;
+ cur->next_nonempty = first;
first = cur;
}
- void rebalance(u32 priority) {
- Queue *cur = &queues[priority];
- int size = cur->end - cur->first;
- if (size >= cur->capacity - 2) {
- IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
- if (new_data != nullptr) {
- cur->capacity *= 2;
- cur->data = new_data;
- }
- }
-
- int newFirst = (cur->capacity - size) / 2;
- if (newFirst != cur->first) {
- memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType));
- cur->first = newFirst;
- cur->end = newFirst + size;
- }
- }
-
// The first queue that's ever been used.
- Queue *first;
+ Queue* first;
// The priority level queues of thread ids.
- Queue queues[NUM_QUEUES];
+ std::array<Queue, NUM_QUEUES> queues;
};
} // namespace
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b67226d8..8723a471 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -31,6 +31,7 @@ set(SRCS
hle/kernel/mutex.cpp
hle/kernel/semaphore.cpp
hle/kernel/shared_memory.cpp
+ hle/kernel/timer.cpp
hle/kernel/thread.cpp
hle/service/ac_u.cpp
hle/service/act_u.cpp
@@ -123,6 +124,7 @@ set(HEADERS
hle/kernel/semaphore.h
hle/kernel/session.h
hle/kernel/shared_memory.h
+ hle/kernel/timer.h
hle/kernel/thread.h
hle/service/ac_u.h
hle/service/act_u.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 3b720941..d3bd4a9a 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -103,6 +103,8 @@ public:
return num_instructions;
}
+ s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
+
protected:
/**
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index a838fd25..31eb879a 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -9,11 +9,13 @@
#include "core/arm/dyncom/arm_dyncom.h"
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
+#include "core/core_timing.h"
+
const static cpu_config_t s_arm11_cpu_info = {
"armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
};
-ARM_DynCom::ARM_DynCom() : ticks(0) {
+ARM_DynCom::ARM_DynCom() {
state = std::unique_ptr<ARMul_State>(new ARMul_State);
ARMul_EmulateInit();
@@ -72,11 +74,14 @@ void ARM_DynCom::SetCPSR(u32 cpsr) {
}
u64 ARM_DynCom::GetTicks() const {
- return ticks;
+ // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone
+ return CoreTiming::GetTicks();
}
void ARM_DynCom::AddTicks(u64 ticks) {
- this->ticks += ticks;
+ down_count -= ticks;
+ if (down_count < 0)
+ CoreTiming::Advance();
}
void ARM_DynCom::ExecuteInstructions(int num_instructions) {
@@ -85,7 +90,8 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
// Dyncom only breaks on instruction dispatch. This only happens on every instruction when
// executing one instruction at a time. Otherwise, if a block is being executed, more
// instructions may actually be executed than specified.
- ticks += InterpreterMainLoop(state.get());
+ unsigned ticks_executed = InterpreterMainLoop(state.get());
+ AddTicks(ticks_executed);
}
void ARM_DynCom::SaveContext(ThreadContext& ctx) {
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 7284dcd0..9e102a46 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -89,8 +89,5 @@ public:
void ExecuteInstructions(int num_instructions) override;
private:
-
std::unique_ptr<ARMul_State> state;
- u64 ticks;
-
};
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 9b291862..e3ca02e9 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -947,6 +947,15 @@ typedef struct _smla_inst {
unsigned int Rn;
} smla_inst;
+typedef struct smlalxy_inst {
+ unsigned int x;
+ unsigned int y;
+ unsigned int RdLo;
+ unsigned int RdHi;
+ unsigned int Rm;
+ unsigned int Rn;
+} smlalxy_inst;
+
typedef struct ssat_inst {
unsigned int Rn;
unsigned int Rd;
@@ -2403,7 +2412,25 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlalxy_inst));
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->x = BIT(inst, 5);
+ inst_cream->y = BIT(inst, 6);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->Rn = BITS(inst, 0, 4);
+ inst_cream->Rm = BITS(inst, 8, 11);
+
+ return inst_base;
+}
ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index)
{
@@ -5686,6 +5713,34 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
SMLALXY_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ u64 operand1 = RN;
+ u64 operand2 = RM;
+
+ if (inst_cream->x != 0)
+ operand1 >>= 16;
+ if (inst_cream->y != 0)
+ operand2 >>= 16;
+ operand1 &= 0xFFFF;
+ if (operand1 & 0x8000)
+ operand1 -= 65536;
+ operand2 &= 0xFFFF;
+ if (operand2 & 0x8000)
+ operand2 -= 65536;
+
+ u64 dest = ((u64)RDHI << 32 | RDLO) + (operand1 * operand2);
+ RDLO = (dest & 0xFFFFFFFF);
+ RDHI = ((dest >> 32) & 0xFFFFFFFF);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlalxy_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
SMLAW_INST:
{
@@ -5836,16 +5891,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
SMULW_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- smlad_inst *inst_cream = (smlad_inst *)inst_base->component;
- int64_t rm = RM;
- int64_t rn = RN;
- if (inst_cream->m)
- rm = BITS(rm, 16, 31);
- else
- rm = BITS(rm, 0, 15);
- int64_t rst = rm * rn;
- RD = BITS(rst, 16, 47);
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ s16 rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF);
+
+ s64 result = (s64)rm * (s64)(s32)RN;
+ RD = BITS(result, 16, 47);
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(smlad_inst));
@@ -6267,6 +6319,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
addr = RN;
unsigned int value = Memory::Read8(addr);
Memory::Write8(addr, (RM & 0xFF));
+ RD = value;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(swp_inst));
@@ -6643,10 +6696,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
umaal_inst* const inst_cream = (umaal_inst*)inst_base->component;
- const u32 rm = RM;
- const u32 rn = RN;
- const u32 rd_lo = RDLO;
- const u32 rd_hi = RDHI;
+ const u64 rm = RM;
+ const u64 rn = RN;
+ const u64 rd_lo = RDLO;
+ const u64 rd_hi = RDHI;
const u64 result = (rm * rn) + rd_lo + rd_hi;
RDLO = (result & 0xFFFFFFFF);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8ac4481c..ff506d67 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -5,6 +5,7 @@
#include "common/common_types.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/settings.h"
#include "core/arm/disassembler/arm_disasm.h"
@@ -16,14 +17,22 @@
namespace Core {
-static u64 last_ticks = 0; ///< Last CPU ticks
-static ARM_Disasm* disasm = nullptr; ///< ARM disassembler
ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop(int tight_loop) {
- g_app_core->Run(tight_loop);
+ // If the current thread is an idle thread, then don't execute instructions,
+ // instead advance to the next event and try to yield to the next thread
+ if (Kernel::IsIdleThread(Kernel::GetCurrentThreadHandle())) {
+ LOG_TRACE(Core_ARM11, "Idling");
+ CoreTiming::Idle();
+ CoreTiming::Advance();
+ HLE::Reschedule(__func__);
+ } else {
+ g_app_core->Run(tight_loop);
+ }
+
HW::Update();
if (HLE::g_reschedule) {
Kernel::Reschedule();
@@ -49,7 +58,6 @@ void Stop() {
int Init() {
LOG_DEBUG(Core, "initialized OK");
- disasm = new ARM_Disasm();
g_sys_core = new ARM_Interpreter();
switch (Settings::values.cpu_core) {
@@ -62,13 +70,10 @@ int Init() {
break;
}
- last_ticks = Core::g_app_core->GetTicks();
-
return 0;
}
void Shutdown() {
- delete disasm;
delete g_app_core;
delete g_sys_core;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 321648b3..83319968 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -1,16 +1,14 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <vector>
-#include <cstdio>
#include <atomic>
+#include <cstdio>
#include <mutex>
+#include <vector>
#include "common/chunk_file.h"
-#include "common/msg_handler.h"
-#include "common/string_util.h"
-
+#include "common/log.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -22,16 +20,15 @@ int g_clock_rate_arm11 = 268123480;
namespace CoreTiming
{
-
struct EventType
{
EventType() {}
- EventType(TimedCallback cb, const char *n)
+ EventType(TimedCallback cb, const char* n)
: callback(cb), name(n) {}
TimedCallback callback;
- const char *name;
+ const char* name;
};
std::vector<EventType> event_types;
@@ -41,262 +38,247 @@ struct BaseEvent
s64 time;
u64 userdata;
int type;
- // Event *next;
};
typedef LinkedListItem<BaseEvent> Event;
-Event *first;
-Event *tsFirst;
-Event *tsLast;
+Event* first;
+Event* ts_first;
+Event* ts_last;
// event pools
-Event *eventPool = 0;
-Event *eventTsPool = 0;
-int allocatedTsEvents = 0;
+Event* event_pool = 0;
+Event* event_ts_pool = 0;
+int allocated_ts_events = 0;
// Optimization to skip MoveEvents when possible.
-std::atomic<u32> hasTsEvents;
+std::atomic<bool> has_ts_events(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;
+int g_slice_length;
-MEMORY_ALIGNED16(s64) globalTimer;
-s64 idledCycles;
+s64 global_timer;
+s64 idled_cycles;
+s64 last_global_time_ticks;
+s64 last_global_time_us;
-static std::recursive_mutex externalEventSection;
+static std::recursive_mutex external_event_section;
// Warning: not included in save state.
-void(*advanceCallback)(int cyclesExecuted) = nullptr;
+using AdvanceCallback = void(int cycles_executed);
+AdvanceCallback* advance_callback = nullptr;
+std::vector<MHzChangeCallback> mhz_change_callbacks;
-void SetClockFrequencyMHz(int cpuMhz)
-{
- g_clock_rate_arm11 = cpuMhz * 1000000;
+void FireMhzChange() {
+ for (auto callback : mhz_change_callbacks)
+ callback();
+}
+
+void SetClockFrequencyMHz(int cpu_mhz) {
+ // When the mhz changes, we keep track of what "time" it was before hand.
+ // This way, time always moves forward, even if mhz is changed.
+ last_global_time_us = GetGlobalTimeUs();
+ last_global_time_ticks = GetTicks();
+
+ g_clock_rate_arm11 = cpu_mhz * 1000000;
// TODO: Rescale times of scheduled events?
+
+ FireMhzChange();
}
-int GetClockFrequencyMHz()
-{
+int GetClockFrequencyMHz() {
return g_clock_rate_arm11 / 1000000;
}
+u64 GetGlobalTimeUs() {
+ s64 ticks_since_last = GetTicks() - last_global_time_ticks;
+ int freq = GetClockFrequencyMHz();
+ s64 us_since_last = ticks_since_last / freq;
+ return last_global_time_us + us_since_last;
+}
-Event* GetNewEvent()
-{
- if (!eventPool)
+Event* GetNewEvent() {
+ if (!event_pool)
return new Event;
- Event* ev = eventPool;
- eventPool = ev->next;
- return ev;
+ Event* event = event_pool;
+ event_pool = event->next;
+ return event;
}
-Event* GetNewTsEvent()
-{
- allocatedTsEvents++;
+Event* GetNewTsEvent() {
+ allocated_ts_events++;
- if (!eventTsPool)
+ if (!event_ts_pool)
return new Event;
- Event* ev = eventTsPool;
- eventTsPool = ev->next;
- return ev;
+ Event* event = event_ts_pool;
+ event_ts_pool = event->next;
+ return event;
}
-void FreeEvent(Event* ev)
-{
- ev->next = eventPool;
- eventPool = ev;
+void FreeEvent(Event* event) {
+ event->next = event_pool;
+ event_pool = event;
}
-void FreeTsEvent(Event* ev)
-{
- ev->next = eventTsPool;
- eventTsPool = ev;
- allocatedTsEvents--;
+void FreeTsEvent(Event* event) {
+ event->next = event_ts_pool;
+ event_ts_pool = event;
+ allocated_ts_events--;
}
-int RegisterEvent(const char *name, TimedCallback callback)
-{
+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)
-{
- LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called.");
+void AntiCrashCallback(u64 userdata, int cycles_late) {
+ LOG_CRITICAL(Core_Timing, "Savestate broken: an unregistered event was called.");
Core::Halt("invalid timing events");
}
-void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback)
-{
+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()
-{
+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 Init() {
+ Core::g_app_core->down_count = INITIAL_SLICE_LENGTH;
+ g_slice_length = INITIAL_SLICE_LENGTH;
+ global_timer = 0;
+ idled_cycles = 0;
+ last_global_time_ticks = 0;
+ last_global_time_us = 0;
+ has_ts_events = 0;
+ mhz_change_callbacks.clear();
}
-void Shutdown()
-{
+void Shutdown() {
MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
- while (eventPool)
- {
- Event *ev = eventPool;
- eventPool = ev->next;
- delete ev;
+ while (event_pool) {
+ Event* event = event_pool;
+ event_pool = event->next;
+ delete event;
}
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- while (eventTsPool)
- {
- Event *ev = eventTsPool;
- eventTsPool = ev->next;
- delete ev;
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ while (event_ts_pool) {
+ Event* event = event_ts_pool;
+ event_ts_pool = event->next;
+ delete event;
}
}
-u64 GetTicks()
-{
- LOG_ERROR(Core, "Unimplemented function!");
- return 0;
- //return (u64)globalTimer + slicelength - currentMIPS->downcount;
+u64 GetTicks() {
+ return (u64)global_timer + g_slice_length - Core::g_app_core->down_count;
}
-u64 GetIdleTicks()
-{
- return (u64)idledCycles;
+u64 GetIdleTicks() {
+ return (u64)idled_cycles;
}
// 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<std::recursive_mutex> 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;
-
- hasTsEvents.store(1, std::memory_order_release);
+void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata) {
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ Event* new_event = GetNewTsEvent();
+ new_event->time = GetTicks() + cycles_into_future;
+ new_event->type = event_type;
+ new_event->next = 0;
+ new_event->userdata = userdata;
+ if (!ts_first)
+ ts_first = new_event;
+ if (ts_last)
+ ts_last->next = new_event;
+ ts_last = new_event;
+
+ has_ts_events = true;
}
// 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)
-{
+void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) {
if (false) //Core::IsCPUThread())
{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
event_types[event_type].callback(userdata, 0);
}
else
ScheduleEvent_Threadsafe(0, event_type, userdata);
}
-void ClearPendingEvents()
-{
- while (first)
- {
- Event *e = first->next;
+void ClearPendingEvents() {
+ while (first) {
+ Event* event = first->next;
FreeEvent(first);
- first = e;
+ first = event;
}
}
-void AddEventToQueue(Event* ne)
-{
- Event* prev = nullptr;
- Event** pNext = &first;
- for (;;)
- {
- Event*& next = *pNext;
- if (!next || ne->time < next->time)
- {
- ne->next = next;
- next = ne;
+void AddEventToQueue(Event* new_event) {
+ Event* prev_event = nullptr;
+ Event** next_event = &first;
+ for (;;) {
+ Event*& next = *next_event;
+ if (!next || new_event->time < next->time) {
+ new_event->next = next;
+ next = new_event;
break;
}
- prev = next;
- pNext = &prev->next;
+ prev_event = next;
+ next_event = &prev_event->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);
+void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata) {
+ Event* new_event = GetNewEvent();
+ new_event->userdata = userdata;
+ new_event->type = event_type;
+ new_event->time = GetTicks() + cycles_into_future;
+ AddEventToQueue(new_event);
}
-// Returns cycles left in timer.
-s64 UnscheduleEvent(int event_type, u64 userdata)
-{
+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;
+ while (first) {
+ if (first->type == event_type && first->userdata == userdata) {
+ result = first->time - GetTicks();
- Event *next = first->next;
+ Event* next = first->next;
FreeEvent(first);
first = next;
- }
- else
- {
+ } 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;
+ Event* prev_event = first;
+ Event* ptr = prev_event->next;
+
+ while (ptr) {
+ if (ptr->type == event_type && ptr->userdata == userdata) {
+ result = ptr->time - GetTicks();
+
+ prev_event->next = ptr->next;
FreeEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
+ ptr = prev_event->next;
+ } else {
+ prev_event = ptr;
ptr = ptr->next;
}
}
@@ -304,51 +286,44 @@ s64 UnscheduleEvent(int event_type, u64 userdata)
return result;
}
-s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
-{
+s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) {
s64 result = 0;
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- if (!tsFirst)
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ if (!ts_first)
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
- {
+ while (ts_first) {
+ if (ts_first->type == event_type && ts_first->userdata == userdata) {
+ result = ts_first->time - GetTicks();
+
+ Event* next = ts_first->next;
+ FreeTsEvent(ts_first);
+ ts_first = next;
+ } else {
break;
}
}
- if (!tsFirst)
+
+ if (!ts_first)
{
- tsLast = nullptr;
+ ts_last = nullptr;
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;
+ Event* prev_event = ts_first;
+ Event* next = prev_event->next;
+ while (next) {
+ if (next->type == event_type && next->userdata == userdata) {
+ result = next->time - GetTicks();
+
+ prev_event->next = next->next;
+ if (next == ts_last)
+ ts_last = prev_event;
+ FreeTsEvent(next);
+ next = prev_event->next;
+ } else {
+ prev_event = next;
+ next = next->next;
}
}
@@ -356,271 +331,217 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
}
// Warning: not included in save state.
-void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted))
-{
- advanceCallback = callback;
+void RegisterAdvanceCallback(AdvanceCallback* callback) {
+ advance_callback = callback;
}
-bool IsScheduled(int event_type)
-{
+void RegisterMHzChangeCallback(MHzChangeCallback callback) {
+ mhz_change_callbacks.push_back(callback);
+}
+
+bool IsScheduled(int event_type) {
if (!first)
return false;
- Event *e = first;
- while (e) {
- if (e->type == event_type)
+ Event* event = first;
+ while (event) {
+ if (event->type == event_type)
return true;
- e = e->next;
+ event = event->next;
}
return false;
}
-void RemoveEvent(int event_type)
-{
+void RemoveEvent(int event_type) {
if (!first)
return;
- while (first)
- {
- if (first->type == event_type)
- {
+ while (first) {
+ if (first->type == event_type) {
Event *next = first->next;
FreeEvent(first);
first = next;
- }
- else
- {
+ } 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;
+ Event* prev = first;
+ Event* next = prev->next;
+ while (next) {
+ if (next->type == event_type) {
+ prev->next = next->next;
+ FreeEvent(next);
+ next = prev->next;
+ } else {
+ prev = next;
+ next = next->next;
}
}
}
-void RemoveThreadsafeEvent(int event_type)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- if (!tsFirst)
- {
+void RemoveThreadsafeEvent(int event_type) {
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ if (!ts_first)
return;
- }
- while (tsFirst)
- {
- if (tsFirst->type == event_type)
- {
- Event *next = tsFirst->next;
- FreeTsEvent(tsFirst);
- tsFirst = next;
- }
- else
- {
+
+ while (ts_first) {
+ if (ts_first->type == event_type) {
+ Event* next = ts_first->next;
+ FreeTsEvent(ts_first);
+ ts_first = next;
+ } else {
break;
}
}
- if (!tsFirst)
- {
- tsLast = nullptr;
+
+ if (!ts_first) {
+ ts_last = nullptr;
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;
+
+ Event* prev = ts_first;
+ Event* next = prev->next;
+ while (next) {
+ if (next->type == event_type) {
+ prev->next = next->next;
+ if (next == ts_last)
+ ts_last = prev;
+ FreeTsEvent(next);
+ next = prev->next;
+ } else {
+ prev = next;
+ next = next->next;
}
}
}
-void RemoveAllEvents(int event_type)
-{
+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);
+// This raise only the events required while the fifo is processing data
+void ProcessFifoWaitEvents() {
+ while (first) {
+ if (first->time <= (s64)GetTicks()) {
Event* evt = first;
first = first->next;
- event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
+ event_types[evt->type].callback(evt->userdata, (int)(GetTicks() - evt->time));
FreeEvent(evt);
- }
- else
- {
+ } else {
break;
}
}
}
-void MoveEvents()
-{
- hasTsEvents.store(0, std::memory_order_release);
+void MoveEvents() {
+ has_ts_events = false;
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
// Move events from async queue into main queue
- while (tsFirst)
- {
- Event *next = tsFirst->next;
- AddEventToQueue(tsFirst);
- tsFirst = next;
+ while (ts_first) {
+ Event* next = ts_first->next;
+ AddEventToQueue(ts_first);
+ ts_first = next;
}
- tsLast = nullptr;
+ ts_last = nullptr;
// Move free events to threadsafe pool
- while (allocatedTsEvents > 0 && eventPool)
- {
- Event *ev = eventPool;
- eventPool = ev->next;
- ev->next = eventTsPool;
- eventTsPool = ev;
- allocatedTsEvents--;
+ while (allocated_ts_events > 0 && event_pool) {
+ Event* event = event_pool;
+ event_pool = event->next;
+ event->next = event_ts_pool;
+ event_ts_pool = event;
+ allocated_ts_events--;
}
}
-void Advance()
-{
- LOG_ERROR(Core, "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 ForceCheck() {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ global_timer += cycles_executed;
+ // This will cause us to check for new events immediately.
+ Core::g_app_core->down_count = 0;
+ // But let's not eat a bunch more time in Advance() because of this.
+ g_slice_length = 0;
}
-void Idle(int maxIdle)
-{
- LOG_ERROR(Core, "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]";
+void Advance() {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ global_timer += cycles_executed;
+ Core::g_app_core->down_count = g_slice_length;
- text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)ptr->time,
- (u32)(ptr->userdata >> 32), (u32)(ptr->userdata));
+ if (has_ts_events)
+ MoveEvents();
+ ProcessFifoWaitEvents();
- ptr = ptr->next;
+ if (!first) {
+ if (g_slice_length < 10000) {
+ g_slice_length += 10000;
+ Core::g_app_core->down_count += g_slice_length;
+ }
+ } else {
+ // Note that events can eat cycles as well.
+ int target = (int)(first->time - global_timer);
+ if (target > MAX_SLICE_LENGTH)
+ target = MAX_SLICE_LENGTH;
+
+ const int diff = target - g_slice_length;
+ g_slice_length += diff;
+ Core::g_app_core->down_count += diff;
}
- return text;
+ if (advance_callback)
+ advance_callback(cycles_executed);
}
-void Event_DoState(PointerWrap &p, BaseEvent *ev)
-{
- p.Do(*ev);
+void LogPendingEvents() {
+ Event* event = first;
+ while (event) {
+ //LOG_TRACE(Core_Timing, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, next->time, next->type);
+ event = event->next;
+ }
}
-void DoState(PointerWrap &p)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+void Idle(int max_idle) {
+ int cycles_down = Core::g_app_core->down_count;
+ if (max_idle != 0 && cycles_down > max_idle)
+ cycles_down = max_idle;
- auto s = p.Section("CoreTiming", 1);
- if (!s)
- return;
+ if (first && cycles_down > 0) {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ int cycles_next_event = (int)(first->time - global_timer);
- 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"));
+ if (cycles_next_event < cycles_executed + cycles_down) {
+ cycles_down = cycles_next_event - cycles_executed;
+ // Now, now... no time machines, please.
+ if (cycles_down < 0)
+ cycles_down = 0;
+ }
+ }
- p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr);
- p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
+ LOG_TRACE(Core_Timing, "Idle for %i cycles! (%f ms)", cycles_down, cycles_down / (float)(g_clock_rate_arm11 * 0.001f));
- p.Do(g_clock_rate_arm11);
- p.Do(slicelength);
- p.Do(globalTimer);
- p.Do(idledCycles);
+ idled_cycles += cycles_down;
+ Core::g_app_core->down_count -= cycles_down;
+ if (Core::g_app_core->down_count == 0)
+ Core::g_app_core->down_count = -1;
+}
+
+std::string GetScheduledEventsSummary() {
+ Event* event = first;
+ std::string text = "Scheduled events\n";
+ text.reserve(1000);
+ while (event) {
+ unsigned int t = event->type;
+ if (t >= event_types.size())
+ PanicAlert("Invalid event type"); // %i", t);
+ const char* name = event_types[event->type].name;
+ if (!name)
+ name = "[unknown]";
+ text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)event->time,
+ (u32)(event->userdata >> 32), (u32)(event->userdata));
+ event = event->next;
+ }
+ return text;
}
} // namespace
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 49623453..d62ff360 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -1,9 +1,11 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
+#include <string>
+
// This is a system to schedule events into the emulated machine's future. Time is measured
// in main CPU clock cycles.
@@ -12,14 +14,14 @@
// 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.
+// The int cycles_late 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")
+// ScheduleEvent(periodInCycles - cycles_late, callback, "whatever")
-#include "common/common.h"
+#include <functional>
-class PointerWrap;
+#include "common/common.h"
extern int g_clock_rate_arm11;
@@ -55,55 +57,84 @@ inline s64 cyclesToUs(s64 cycles) {
return cycles / (g_clock_rate_arm11 / 1000000);
}
-namespace CoreTiming {
+inline u64 cyclesToMs(s64 cycles) {
+ return cycles / (g_clock_rate_arm11 / 1000);
+}
+namespace CoreTiming
+{
void Init();
void Shutdown();
-typedef void(*TimedCallback)(u64 userdata, int cyclesLate);
+typedef void(*MHzChangeCallback)();
+typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
u64 GetTicks();
u64 GetIdleTicks();
-
-// Returns the event_type identifier.
-int RegisterEvent(const char *name, TimedCallback callback);
-// For save states.
+u64 GetGlobalTimeUs();
+
+/**
+ * Registers an event type with the specified name and callback
+ * @param name Name of the event type
+ * @param callback Function that will execute when this event fires
+ * @returns An identifier for the event type that was registered
+ */
+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);
+/// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
+/// when we implement state saves.
+/**
+ * Schedules an event to run after the specified number of cycles,
+ * with an optional parameter to be passed to the callback handler.
+ * This must be run ONLY from within the cpu thread.
+ * @param cycles_into_future The number of cycles after which this event will be fired
+ * @param event_type The event type to fire, as returned from RegisterEvent
+ * @param userdata Optional parameter to pass to the callback when fired
+ */
+void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata = 0);
+
+void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata = 0);
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0);
+
+/**
+ * Unschedules an event with the specified type and userdata
+ * @param event_type The type of event to unschedule, as returned from RegisterEvent
+ * @param userdata The userdata that identifies this event, as passed to ScheduleEvent
+ * @returns The remaining ticks until the next invocation of the event callback
+ */
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);
+/// Runs any pending events and updates downcount for the next slice of cycles
void Advance();
void MoveEvents();
void ProcessFifoWaitEvents();
+void ForceCheck();
-// Pretend that the main CPU has executed enough cycles to reach the next event.
+/// 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.
+/// 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));
+/// Warning: not included in save states.
+void RegisterAdvanceCallback(void(*callback)(int cycles_executed));
+void RegisterMHzChangeCallback(MHzChangeCallback callback);
std::string GetScheduledEventsSummary();
-void DoState(PointerWrap &p);
-
-void SetClockFrequencyMHz(int cpuMhz);
+void SetClockFrequencyMHz(int cpu_mhz);
int GetClockFrequencyMHz();
-extern int slicelength;
+extern int g_slice_length;
} // namespace
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 0f822f84..8eb4f252 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -135,6 +135,12 @@ template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() {
FuncReturn(retval);
}
+template<s32 func(u32, s64, s64)> void Wrap() {
+ s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2);
+ s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1);
+ FuncReturn(func(PARAM(0), param1, param2));
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e59ed1b5..391e833c 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -9,6 +9,7 @@
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
namespace Kernel {
@@ -105,12 +106,13 @@ void HandleTable::Clear() {
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
+ Kernel::TimersInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
-
+ Kernel::TimersShutdown();
g_handle_table.Clear(); // Free all kernel objects
}
@@ -124,6 +126,8 @@ bool LoadExec(u32 entry_point) {
// 0x30 is the typical main thread priority I've seen used so far
g_main_thread = Kernel::SetupMainThread(0x30);
+ // Setup the idle thread
+ Kernel::SetupIdleThread();
return true;
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7f86fd07..3e381d77 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -39,6 +39,7 @@ enum class HandleType : u32 {
Process = 8,
AddressArbiter = 9,
Semaphore = 10,
+ Timer = 11
};
enum {
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 872df2d1..954bd09a 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -11,6 +11,7 @@
#include "common/thread_queue_list.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
@@ -34,6 +35,7 @@ public:
inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+ inline bool IsIdle() const { return idle; }
ResultVal<bool> WaitSynchronization() override {
const bool wait = status != THREADSTATUS_DORMANT;
@@ -69,13 +71,16 @@ public:
std::vector<Handle> waiting_threads;
std::string name;
+
+ /// Whether this thread is intended to never actually be executed, i.e. always idle
+ bool idle = false;
};
// Lists all thread ids that aren't deleted/etc.
static std::vector<Handle> thread_queue;
// Lists only ready thread ids.
-static Common::ThreadQueueList<Handle> thread_ready_queue;
+static Common::ThreadQueueList<Handle, THREADPRIO_LOWEST+1> thread_ready_queue;
static Handle current_thread_handle;
static Thread* current_thread;
@@ -303,6 +308,37 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_addres
GetCurrentThread()->wait_address = wait_address;
}
+/// Event type for the thread wake up event
+static int ThreadWakeupEventType = -1;
+
+/// Callback that will wake up the thread it was scheduled for
+static void ThreadWakeupCallback(u64 parameter, int cycles_late) {
+ Handle handle = static_cast<Handle>(parameter);
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+ return;
+ }
+
+ Kernel::ResumeThreadFromWait(handle);
+}
+
+
+void WakeThreadAfterDelay(Handle handle, s64 nanoseconds) {
+ // Don't schedule a wakeup if the thread wants to wait forever
+ if (nanoseconds == -1)
+ return;
+
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+ return;
+ }
+
+ u64 microseconds = nanoseconds / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, handle);
+}
+
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle) {
Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
@@ -444,7 +480,14 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
return RESULT_SUCCESS;
}
-/// Sets up the primary application thread
+Handle SetupIdleThread() {
+ Handle handle;
+ Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0);
+ thread->idle = true;
+ CallThread(thread);
+ return handle;
+}
+
Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
@@ -487,14 +530,15 @@ void Reschedule() {
thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
}
}
+}
- // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
- // to sleep. So, we'll just immediately set it to "ready" again after an attempted context
- // switch has occurred. This results in the current thread yielding on a sleep once, and then it
- // will immediately be placed back in the queue for execution.
-
- if (CheckWaitType(prev, WAITTYPE_SLEEP))
- ResumeThreadFromWait(prev->GetHandle());
+bool IsIdleThread(Handle handle) {
+ Thread* thread = g_handle_table.Get<Thread>(handle);
+ if (!thread) {
+ LOG_ERROR(Kernel, "Thread not found %u", handle);
+ return false;
+ }
+ return thread->IsIdle();
}
ResultCode GetThreadId(u32* thread_id, Handle handle) {
@@ -512,6 +556,7 @@ ResultCode GetThreadId(u32* thread_id, Handle handle) {
void ThreadingInit() {
next_thread_id = INITIAL_THREAD_ID;
+ ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 0e1397cd..58bd85ac 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -43,6 +43,7 @@ enum WaitType {
WAITTYPE_MUTEX,
WAITTYPE_SYNCH,
WAITTYPE_ARB,
+ WAITTYPE_TIMER,
};
namespace Kernel {
@@ -88,6 +89,13 @@ Handle GetCurrentThreadHandle();
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
/**
+ * Schedules an event to wake up the specified thread after the specified delay.
+ * @param handle The thread handle.
+ * @param nanoseconds The time this thread will be allowed to sleep for.
+ */
+void WakeThreadAfterDelay(Handle handle, s64 nanoseconds);
+
+/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
* @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
@@ -104,6 +112,17 @@ ResultVal<u32> GetThreadPriority(const Handle handle);
/// Set the priority of the thread specified by handle
ResultCode SetThreadPriority(Handle handle, s32 priority);
+/**
+ * Sets up the idle thread, this is a thread that is intended to never execute instructions,
+ * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue
+ * and will try to yield on every call.
+ * @returns The handle of the idle thread
+ */
+Handle SetupIdleThread();
+
+/// Whether the current thread is an idle thread
+bool IsIdleThread(Handle thread);
+
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
new file mode 100644
index 00000000..7ac669e3
--- /dev/null
+++ b/src/core/hle/kernel/timer.cpp
@@ -0,0 +1,142 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <set>
+
+#include "common/common.h"
+
+#include "core/core_timing.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/timer.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Timer : public Object {
+public:
+ std::string GetTypeName() const override { return "Timer"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::Timer;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ ResetType reset_type; ///< The ResetType of this timer
+
+ bool signaled; ///< Whether the timer has been signaled or not
+ std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer
+ std::string name; ///< Name of timer (optional)
+
+ u64 initial_delay; ///< The delay until the timer fires for the first time
+ u64 interval_delay; ///< The delay until the timer fires after the first time
+
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = !signaled;
+ if (wait) {
+ waiting_threads.insert(GetCurrentThreadHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_TIMER, GetHandle());
+ }
+ return MakeResult<bool>(wait);
+ }
+};
+
+/**
+ * Creates a timer.
+ * @param handle Reference to handle for the newly created timer
+ * @param reset_type ResetType describing how to create timer
+ * @param name Optional name of timer
+ * @return Newly created Timer object
+ */
+Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) {
+ Timer* timer = new Timer;
+
+ handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE);
+
+ timer->reset_type = reset_type;
+ timer->signaled = false;
+ timer->name = name;
+ timer->initial_delay = 0;
+ timer->interval_delay = 0;
+ return timer;
+}
+
+ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) {
+ CreateTimer(*handle, reset_type, name);
+ return RESULT_SUCCESS;
+}
+
+ResultCode ClearTimer(Handle handle) {
+ Timer* timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ timer->signaled = false;
+ return RESULT_SUCCESS;
+}
+
+/// The event type of the generic timer callback event
+static int TimerCallbackEventType = -1;
+
+/// The timer callback event, called when a timer is fired
+static void TimerCallback(u64 timer_handle, int cycles_late) {
+ Timer* timer = Kernel::g_handle_table.Get<Timer>(timer_handle);
+
+ if (timer == nullptr) {
+ LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle);
+ return;
+ }
+
+ LOG_TRACE(Kernel, "Timer %u fired", timer_handle);
+
+ timer->signaled = true;
+
+ // Resume all waiting threads
+ for (Handle thread : timer->waiting_threads)
+ ResumeThreadFromWait(thread);
+
+ timer->waiting_threads.clear();
+
+ if (timer->reset_type == RESETTYPE_ONESHOT)
+ timer->signaled = false;
+
+ if (timer->interval_delay != 0) {
+ // Reschedule the timer with the interval delay
+ u64 interval_microseconds = timer->interval_delay / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
+ TimerCallbackEventType, timer_handle);
+ }
+}
+
+ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
+ Timer* timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ timer->initial_delay = initial;
+ timer->interval_delay = interval;
+
+ u64 initial_microseconds = initial / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle);
+ return RESULT_SUCCESS;
+}
+
+ResultCode CancelTimer(Handle handle) {
+ Timer* timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle);
+ return RESULT_SUCCESS;
+}
+
+void TimersInit() {
+ TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
+}
+
+void TimersShutdown() {
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
new file mode 100644
index 00000000..f8aa66b6
--- /dev/null
+++ b/src/core/hle/kernel/timer.h
@@ -0,0 +1,47 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/svc.h"
+
+namespace Kernel {
+
+/**
+ * Cancels a timer
+ * @param handle Handle of the timer to cancel
+ */
+ResultCode CancelTimer(Handle handle);
+
+/**
+ * Starts a timer with the specified initial delay and interval
+ * @param handle Handle of the timer to start
+ * @param initial Delay until the timer is first fired
+ * @param interval Delay until the timer is fired after the first time
+ */
+ResultCode SetTimer(Handle handle, s64 initial, s64 interval);
+
+/**
+ * Clears a timer
+ * @param handle Handle of the timer to clear
+ */
+ResultCode ClearTimer(Handle handle);
+
+/**
+ * Creates a timer
+ * @param Handle to newly created Timer object
+ * @param reset_type ResetType describing how to create the timer
+ * @param name Optional name of timer
+ * @return ResultCode of the error
+ */
+ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown");
+
+/// Initializes the required variables for timers
+void TimersInit();
+/// Tears down the timer variables
+void TimersShutdown();
+} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 0127d4ee..26a43217 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -291,8 +291,11 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
// Update framebuffer information if requested
for (int screen_id = 0; screen_id < 2; ++screen_id) {
FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
- if (info->is_dirty)
+
+ if (info->is_dirty) {
SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
+ info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1;
+ }
info->is_dirty = false;
}
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index c5233e68..0c559728 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -46,36 +46,22 @@ Manager* g_manager = nullptr; ///< Service manager
////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Manager class
-Manager::Manager() {
-}
-
-Manager::~Manager() {
- for(Interface* service : m_services) {
- DeleteService(service->GetPortName());
- }
-}
-
-/// Add a service to the manager (does not create it though)
void Manager::AddService(Interface* service) {
// TOOD(yuriks): Fix error reporting
m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE);
m_services.push_back(service);
}
-/// Removes a service from the manager, also frees memory
void Manager::DeleteService(const std::string& port_name) {
Interface* service = FetchFromPortName(port_name);
m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end());
m_port_map.erase(port_name);
- delete service;
}
-/// Get a Service Interface from its Handle
Interface* Manager::FetchFromHandle(Handle handle) {
return Kernel::g_handle_table.Get<Interface>(handle);
}
-/// Get a Service Interface from its port
Interface* Manager::FetchFromPortName(const std::string& port_name) {
auto itr = m_port_map.find(port_name);
if (itr == m_port_map.end()) {
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 28b4ccd1..41ba1e55 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -114,29 +114,22 @@ private:
/// Simple class to manage accessing services from ports and UID handles
class Manager {
-
public:
- Manager();
-
- ~Manager();
-
- /// Add a service to the manager (does not create it though)
+ /// Add a service to the manager
void AddService(Interface* service);
- /// Removes a service from the manager (does not delete it though)
+ /// Removes a service from the manager
void DeleteService(const std::string& port_name);
- /// Get a Service Interface from its UID
- Interface* FetchFromHandle(u32 uid);
+ /// Get a Service Interface from its Handle
+ Interface* FetchFromHandle(Handle handle);
/// Get a Service Interface from its port
Interface* FetchFromPortName(const std::string& port_name);
private:
-
std::vector<Interface*> m_services;
std::map<std::string, u32> m_port_map;
-
};
/// Initialize ServiceManager
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 2b948d01..051f2d2c 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -15,6 +15,7 @@
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
#include "core/hle/function_wrappers.h"
#include "core/hle/result.h"
@@ -139,6 +140,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
s64 nano_seconds) {
+
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
bool unlock_all = true;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
@@ -343,12 +345,42 @@ static Result ClearEvent(Handle evt) {
return Kernel::ClearEvent(evt).raw;
}
+/// Creates a timer
+static Result CreateTimer(Handle* handle, u32 reset_type) {
+ ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type));
+ LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
+ reset_type, *handle);
+ return res.raw;
+}
+
+/// Clears a timer
+static Result ClearTimer(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::ClearTimer(handle).raw;
+}
+
+/// Starts a timer
+static Result SetTimer(Handle handle, s64 initial, s64 interval) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::SetTimer(handle, initial, interval).raw;
+}
+
+/// Cancels a timer
+static Result CancelTimer(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::CancelTimer(handle).raw;
+}
+
/// Sleep the current thread
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
// Sleep current thread and check for next thread to schedule
Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThreadHandle(), nanoseconds);
+
HLE::Reschedule(__func__);
}
@@ -396,10 +428,10 @@ const HLE::FunctionDef SVC_Table[] = {
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
- {0x1A, nullptr, "CreateTimer"},
- {0x1B, nullptr, "SetTimer"},
- {0x1C, nullptr, "CancelTimer"},
- {0x1D, nullptr, "ClearTimer"},
+ {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"},
+ {0x1B, HLE::Wrap<SetTimer>, "SetTimer"},
+ {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"},
+ {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"},
{0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
{0x20, nullptr, "UnmapMemoryBlock"},
diff --git a/src/core/system.cpp b/src/core/system.cpp
index d6188f05..f4c2df1c 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -21,11 +21,11 @@ void UpdateState(State state) {
void Init(EmuWindow* emu_window) {
Core::Init();
+ CoreTiming::Init();
Memory::Init();
HW::Init();
Kernel::Init();
HLE::Init();
- CoreTiming::Init();
VideoCore::Init(emu_window);
}
@@ -38,11 +38,11 @@ void RunLoopUntil(u64 global_cycles) {
void Shutdown() {
VideoCore::Shutdown();
- CoreTiming::Shutdown();
HLE::Shutdown();
Kernel::Shutdown();
HW::Shutdown();
Memory::Shutdown();
+ CoreTiming::Shutdown();
Core::Shutdown();
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 4df3a5e2..29d220e8 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -87,8 +87,11 @@ void RendererOpenGL::SwapBuffers() {
*/
void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
const TextureInfo& texture) {
+
+ // TODO: Why are active_fb and the valid framebuffer flipped compared to 3dbrew documentation
+ // and GSP definitions?
const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress(
- framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1);
+ framebuffer.active_fb == 0 ? framebuffer.address_left2 : framebuffer.address_left1);
LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
framebuffer.stride * framebuffer.height,