From 3c333c53f1f2c8daa3eab8c83d403d0d23682747 Mon Sep 17 00:00:00 2001
From: wwylele <wwylele@gmail.com>
Date: Sun, 15 Jan 2017 21:27:00 +0200
Subject: [PATCH 1/2] HID: manages updating itself using correct ticks

---
 src/core/hle/service/hid/hid.cpp | 142 ++++++++++++++++++++-----------
 src/core/hle/service/hid/hid.h   |   3 -
 src/core/hw/gpu.cpp              |   4 -
 3 files changed, 90 insertions(+), 59 deletions(-)

diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 676154bd41..9e4a7f8889 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -35,6 +35,15 @@ static u32 next_gyroscope_index;
 static int enable_accelerometer_count = 0; // positive means enabled
 static int enable_gyroscope_count = 0;     // positive means enabled
 
+static int pad_update_event;
+static int accelerometer_update_event;
+static int gyroscope_update_event;
+
+// Updating period for each HID device. These empirical values are measured from a 11.2 3DS.
+constexpr u64 pad_update_ticks = 268123480ull / 234;
+constexpr u64 accelerometer_update_ticks = 268123480ull / 104;
+constexpr u64 gyroscope_update_ticks = 268123480ull / 101;
+
 static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
     // 30 degree and 60 degree are angular thresholds for directions
     constexpr float TAN30 = 0.577350269f;
@@ -65,14 +74,9 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
     return state;
 }
 
-void Update() {
+static void UpdatePadCallback(u64 userdata, int cycles_late) {
     SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
 
-    if (mem == nullptr) {
-        LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!");
-        return;
-    }
-
     PadState state = VideoCore::g_emu_window->GetPadState();
 
     // Get current circle pad position and update circle pad direction
@@ -131,59 +135,68 @@ void Update() {
     event_pad_or_touch_1->Signal();
     event_pad_or_touch_2->Signal();
 
-    // Update accelerometer
-    if (enable_accelerometer_count > 0) {
-        mem->accelerometer.index = next_accelerometer_index;
-        next_accelerometer_index =
-            (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
+    // Reschedule recurrent event
+    CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
+}
 
-        AccelerometerDataEntry& accelerometer_entry =
-            mem->accelerometer.entries[mem->accelerometer.index];
-        std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) =
-            VideoCore::g_emu_window->GetAccelerometerState();
+static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
+    SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
 
-        // Make up "raw" entry
-        // TODO(wwylele):
-        // From hardware testing, the raw_entry values are approximately,
-        // but not exactly, as twice as corresponding entries (or with a minus sign).
-        // It may caused by system calibration to the accelerometer.
-        // Figure out how it works, or, if no game reads raw_entry,
-        // the following three lines can be removed and leave raw_entry unimplemented.
-        mem->accelerometer.raw_entry.x = -2 * accelerometer_entry.x;
-        mem->accelerometer.raw_entry.z = 2 * accelerometer_entry.y;
-        mem->accelerometer.raw_entry.y = -2 * accelerometer_entry.z;
+    mem->accelerometer.index = next_accelerometer_index;
+    next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
 
-        // If we just updated index 0, provide a new timestamp
-        if (mem->accelerometer.index == 0) {
-            mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks;
-            mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks();
-        }
+    AccelerometerDataEntry& accelerometer_entry =
+        mem->accelerometer.entries[mem->accelerometer.index];
+    std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) =
+        VideoCore::g_emu_window->GetAccelerometerState();
 
-        event_accelerometer->Signal();
+    // Make up "raw" entry
+    // TODO(wwylele):
+    // From hardware testing, the raw_entry values are approximately, but not exactly, as twice as
+    // corresponding entries (or with a minus sign). It may caused by system calibration to the
+    // accelerometer. Figure out how it works, or, if no game reads raw_entry, the following three
+    // lines can be removed and leave raw_entry unimplemented.
+    mem->accelerometer.raw_entry.x = -2 * accelerometer_entry.x;
+    mem->accelerometer.raw_entry.z = 2 * accelerometer_entry.y;
+    mem->accelerometer.raw_entry.y = -2 * accelerometer_entry.z;
+
+    // If we just updated index 0, provide a new timestamp
+    if (mem->accelerometer.index == 0) {
+        mem->accelerometer.index_reset_ticks_previous = mem->accelerometer.index_reset_ticks;
+        mem->accelerometer.index_reset_ticks = (s64)CoreTiming::GetTicks();
     }
 
-    // Update gyroscope
-    if (enable_gyroscope_count > 0) {
-        mem->gyroscope.index = next_gyroscope_index;
-        next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
+    event_accelerometer->Signal();
 
-        GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
-        std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) =
-            VideoCore::g_emu_window->GetGyroscopeState();
+    // Reschedule recurrent event
+    CoreTiming::ScheduleEvent(accelerometer_update_ticks - cycles_late, accelerometer_update_event);
+}
 
-        // Make up "raw" entry
-        mem->gyroscope.raw_entry.x = gyroscope_entry.x;
-        mem->gyroscope.raw_entry.z = -gyroscope_entry.y;
-        mem->gyroscope.raw_entry.y = gyroscope_entry.z;
+static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
+    SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
 
-        // If we just updated index 0, provide a new timestamp
-        if (mem->gyroscope.index == 0) {
-            mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks;
-            mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks();
-        }
+    mem->gyroscope.index = next_gyroscope_index;
+    next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
 
-        event_gyroscope->Signal();
+    GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
+    std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) =
+        VideoCore::g_emu_window->GetGyroscopeState();
+
+    // Make up "raw" entry
+    mem->gyroscope.raw_entry.x = gyroscope_entry.x;
+    mem->gyroscope.raw_entry.z = -gyroscope_entry.y;
+    mem->gyroscope.raw_entry.y = gyroscope_entry.z;
+
+    // If we just updated index 0, provide a new timestamp
+    if (mem->gyroscope.index == 0) {
+        mem->gyroscope.index_reset_ticks_previous = mem->gyroscope.index_reset_ticks;
+        mem->gyroscope.index_reset_ticks = (s64)CoreTiming::GetTicks();
     }
+
+    event_gyroscope->Signal();
+
+    // Reschedule recurrent event
+    CoreTiming::ScheduleEvent(gyroscope_update_ticks - cycles_late, gyroscope_update_event);
 }
 
 void GetIPCHandles(Service::Interface* self) {
@@ -204,7 +217,11 @@ void EnableAccelerometer(Service::Interface* self) {
     u32* cmd_buff = Kernel::GetCommandBuffer();
 
     ++enable_accelerometer_count;
-    event_accelerometer->Signal();
+
+    // Schedules the accelerometer update event if the accelerometer was just enabled
+    if (enable_accelerometer_count == 1) {
+        CoreTiming::ScheduleEvent(accelerometer_update_ticks, accelerometer_update_event);
+    }
 
     cmd_buff[1] = RESULT_SUCCESS.raw;
 
@@ -215,7 +232,11 @@ void DisableAccelerometer(Service::Interface* self) {
     u32* cmd_buff = Kernel::GetCommandBuffer();
 
     --enable_accelerometer_count;
-    event_accelerometer->Signal();
+
+    // Unschedules the accelerometer update event if the accelerometer was just disabled
+    if (enable_accelerometer_count == 0) {
+        CoreTiming::UnscheduleEvent(accelerometer_update_event, 0);
+    }
 
     cmd_buff[1] = RESULT_SUCCESS.raw;
 
@@ -226,7 +247,11 @@ void EnableGyroscopeLow(Service::Interface* self) {
     u32* cmd_buff = Kernel::GetCommandBuffer();
 
     ++enable_gyroscope_count;
-    event_gyroscope->Signal();
+
+    // Schedules the gyroscope update event if the gyroscope was just enabled
+    if (enable_gyroscope_count == 1) {
+        CoreTiming::ScheduleEvent(gyroscope_update_ticks, gyroscope_update_event);
+    }
 
     cmd_buff[1] = RESULT_SUCCESS.raw;
 
@@ -237,7 +262,11 @@ void DisableGyroscopeLow(Service::Interface* self) {
     u32* cmd_buff = Kernel::GetCommandBuffer();
 
     --enable_gyroscope_count;
-    event_gyroscope->Signal();
+
+    // Unschedules the gyroscope update event if the gyroscope was just disabled
+    if (enable_gyroscope_count == 0) {
+        CoreTiming::UnscheduleEvent(gyroscope_update_event, 0);
+    }
 
     cmd_buff[1] = RESULT_SUCCESS.raw;
 
@@ -298,6 +327,15 @@ void Init() {
     event_accelerometer = Event::Create(ResetType::OneShot, "HID:EventAccelerometer");
     event_gyroscope = Event::Create(ResetType::OneShot, "HID:EventGyroscope");
     event_debug_pad = Event::Create(ResetType::OneShot, "HID:EventDebugPad");
+
+    // Register update callbacks
+    pad_update_event = CoreTiming::RegisterEvent("HID::UpdatePadCallback", UpdatePadCallback);
+    accelerometer_update_event =
+        CoreTiming::RegisterEvent("HID::UpdateAccelerometerCallback", UpdateAccelerometerCallback);
+    gyroscope_update_event =
+        CoreTiming::RegisterEvent("HID::UpdateGyroscopeCallback", UpdateGyroscopeCallback);
+
+    CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
 }
 
 void Shutdown() {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 7904e73550..21e66dfe07 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -296,9 +296,6 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self);
  */
 void GetGyroscopeLowCalibrateParam(Service::Interface* self);
 
-/// Checks for user input updates
-void Update();
-
 /// Initialize HID service
 void Init();
 
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 1a1ee90b2f..58c0d9908f 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -15,7 +15,6 @@
 #include "common/vector_math.h"
 #include "core/core_timing.h"
 #include "core/hle/service/gsp_gpu.h"
-#include "core/hle/service/hid/hid.h"
 #include "core/hw/gpu.h"
 #include "core/hw/hw.h"
 #include "core/memory.h"
@@ -551,9 +550,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
     Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0);
     Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1);
 
-    // Check for user input updates
-    Service::HID::Update();
-
     if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) {
         FrameLimiter();
     }

From 47960b065901f8aec53696535fb46fe20c0908e4 Mon Sep 17 00:00:00 2001
From: wwylele <wwylele@gmail.com>
Date: Mon, 16 Jan 2017 09:59:16 +0200
Subject: [PATCH 2/2] CoreTiming: use named constant for ARM11 clock rate

---
 src/core/core_timing.cpp         | 2 +-
 src/core/core_timing.h           | 1 +
 src/core/hle/service/hid/hid.cpp | 6 +++---
 src/core/hw/gpu.cpp              | 2 +-
 4 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index a437d08233..276ecfdf68 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -13,7 +13,7 @@
 #include "core/core.h"
 #include "core/core_timing.h"
 
-int g_clock_rate_arm11 = 268123480;
+int g_clock_rate_arm11 = BASE_CLOCK_RATE_ARM11;
 
 // is this really necessary?
 #define INITIAL_SLICE_LENGTH 20000
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index b72a1b5001..d2f85cd4d6 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -21,6 +21,7 @@
 // inside callback:
 //   ScheduleEvent(periodInCycles - cycles_late, callback, "whatever")
 
+constexpr int BASE_CLOCK_RATE_ARM11 = 268123480;
 extern int g_clock_rate_arm11;
 
 inline s64 msToCycles(int ms) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 9e4a7f8889..9bca97c1c3 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -40,9 +40,9 @@ static int accelerometer_update_event;
 static int gyroscope_update_event;
 
 // Updating period for each HID device. These empirical values are measured from a 11.2 3DS.
-constexpr u64 pad_update_ticks = 268123480ull / 234;
-constexpr u64 accelerometer_update_ticks = 268123480ull / 104;
-constexpr u64 gyroscope_update_ticks = 268123480ull / 101;
+constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
+constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
+constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
 
 static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
     // 30 degree and 60 degree are angular thresholds for directions
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 58c0d9908f..fa8c13d366 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -32,7 +32,7 @@ namespace GPU {
 Regs g_regs;
 
 /// 268MHz CPU clocks / 60Hz frames per second
-const u64 frame_ticks = 268123480ull / 60;
+const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / 60;
 /// Event id for CoreTiming
 static int vblank_event;
 /// Total number of frames drawn