From dc7f8a48bdca9ce444dbf862da10c04dea584b4c Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 3 Jul 2025 02:42:43 +0300 Subject: [PATCH] IR: Move to scheduler --- include/scheduler.hpp | 3 ++- include/services/ir/circlepad_pro.hpp | 5 ++++- include/services/ir/ir_device.hpp | 4 +++- include/services/ir/ir_user.hpp | 8 +++----- src/core/services/ir/circlepad_pro.cpp | 11 ++++++++--- src/core/services/ir/ir_user.cpp | 9 ++++++++- src/emulator.cpp | 3 +-- 7 files changed, 29 insertions(+), 14 deletions(-) diff --git a/include/scheduler.hpp b/include/scheduler.hpp index cfc4d5e8..8765ae66 100644 --- a/include/scheduler.hpp +++ b/include/scheduler.hpp @@ -12,7 +12,8 @@ struct Scheduler { UpdateTimers = 1, // Update kernel timer objects RunDSP = 2, // Make the emulated DSP run for one audio frame SignalY2R = 3, // Signal that a Y2R conversion has finished - Panic = 4, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX) + UpdateIR = 4, // Update an IR device (For now, just the CirclePad Pro/N3DS controls) + Panic = 5, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX) TotalNumberOfEvents // How many event types do we have in total? }; static constexpr usize totalNumberOfEvents = static_cast(EventType::TotalNumberOfEvents); diff --git a/include/services/ir/circlepad_pro.hpp b/include/services/ir/circlepad_pro.hpp index 6895ab6f..4cee6007 100644 --- a/include/services/ir/circlepad_pro.hpp +++ b/include/services/ir/circlepad_pro.hpp @@ -44,7 +44,10 @@ namespace IR { virtual void disconnect() override; virtual void receivePayload(Payload payload) override; - CirclePadPro(SendCallback sendCallback) : IR::Device(sendCallback) {} + CirclePadPro(SendCallback sendCallback, Scheduler& scheduler) : IR::Device(sendCallback, scheduler) {} + ButtonState state; + // The current polling period in cycles, configured via the ConfigurePolling command + s64 period = Scheduler::nsToCycles(16000); }; } // namespace IR \ No newline at end of file diff --git a/include/services/ir/ir_device.hpp b/include/services/ir/ir_device.hpp index 9a73f9ab..db82ea99 100644 --- a/include/services/ir/ir_device.hpp +++ b/include/services/ir/ir_device.hpp @@ -3,6 +3,7 @@ #include #include "helpers.hpp" +#include "scheduler.hpp" namespace IR { class Device { @@ -11,6 +12,7 @@ namespace IR { protected: using SendCallback = std::function; // Callback for sending data from IR device->3DS + Scheduler& scheduler; SendCallback sendCallback; public: @@ -18,6 +20,6 @@ namespace IR { virtual void disconnect() = 0; virtual void receivePayload(Payload payload) = 0; - Device(SendCallback sendCallback) : sendCallback(sendCallback) {} + Device(SendCallback sendCallback, Scheduler& scheduler) : sendCallback(sendCallback), scheduler(scheduler) {} }; } // namespace IR \ No newline at end of file diff --git a/include/services/ir/ir_user.hpp b/include/services/ir/ir_user.hpp index 00944e93..797e5d9c 100644 --- a/include/services/ir/ir_user.hpp +++ b/include/services/ir/ir_user.hpp @@ -16,6 +16,7 @@ class Kernel; class IRUserService { + using Payload = IR::Device::Payload; using Handle = HorizonHandle; enum class DeviceID : u8 { @@ -75,14 +76,11 @@ class IRUserService { // The IR service uses CRC8 with generator polynomial = 0x07 for verifying packets received from IR devices static u8 crc8(std::span data); - // IR service calls this to send a console->device payload - void receivePayload(std::span data); // IR devices call this to send a device->console payload - void sendPayload(std::span payload); + void sendPayload(Payload payload); public: - IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel) - : mem(mem), hid(hid), config(config), kernel(kernel), cpp([&](IR::Device::Payload payload) { sendPayload(payload); }) {} + IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel); void setCStickX(s16 value) { cpp.state.cStick.x = value; } void setCStickY(s16 value) { cpp.state.cStick.y = value; } diff --git a/src/core/services/ir/circlepad_pro.cpp b/src/core/services/ir/circlepad_pro.cpp index 9e652a40..5ef2135a 100644 --- a/src/core/services/ir/circlepad_pro.cpp +++ b/src/core/services/ir/circlepad_pro.cpp @@ -7,15 +7,20 @@ using namespace IR; void CirclePadPro::connect() {} -void CirclePadPro::disconnect() {} +void CirclePadPro::disconnect() { scheduler.removeEvent(Scheduler::EventType::UpdateIR); } void CirclePadPro::receivePayload(Payload payload) { const u8 type = payload[0]; switch (type) { case CPPRequestID::ConfigurePolling: { - [[maybe_unused]] const u8 pollingPeriodMs = payload[1]; - // TODO + // Convert polling period from ms to ns for easier use with the scheduler + const s64 periodNs = s64(payload[1]) * 1000ll; + // Convert to cycles + period = Scheduler::nsToCycles(periodNs); + + scheduler.removeEvent(Scheduler::EventType::UpdateIR); + scheduler.addEvent(Scheduler::EventType::UpdateIR, scheduler.currentTimestamp + period); break; } diff --git a/src/core/services/ir/ir_user.cpp b/src/core/services/ir/ir_user.cpp index 8c565ec0..8e1693de 100644 --- a/src/core/services/ir/ir_user.cpp +++ b/src/core/services/ir/ir_user.cpp @@ -28,6 +28,9 @@ namespace IRUserCommands { }; } +IRUserService::IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel) + : mem(mem), hid(hid), config(config), kernel(kernel), cpp([&](IR::Device::Payload payload) { sendPayload(payload); }, kernel.getScheduler()) {} + void IRUserService::reset() { connectionStatusEvent = std::nullopt; receiveEvent = std::nullopt; @@ -285,7 +288,7 @@ void IRUserService::clearSendBuffer(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } -void IRUserService::sendPayload(std::span payload) { +void IRUserService::sendPayload(IRUserService::Payload payload) { if (!receiveBuffer) { return; } @@ -343,4 +346,8 @@ void IRUserService::updateCirclePadPro() { std::vector response(sizeof(cppState)); std::memcpy(response.data(), &cppState, sizeof(cppState)); sendPayload(response); + + // Schedule next IR event. TODO: Maybe account for cycle drift. + auto& scheduler = kernel.getScheduler(); + scheduler.addEvent(Scheduler::EventType::UpdateIR, scheduler.currentTimestamp + cpp.period); } \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 74832214..e7556e4e 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -139,8 +139,6 @@ void Emulator::runFrame() { if (cheats.haveCheats()) [[unlikely]] { cheats.run(); } - - getServiceManager().getIRUser().updateCirclePadPro(); } else if (romType != ROMType::None) { // If the emulator is not running and a game is loaded, we still want to display the framebuffer otherwise we will get weird // double-buffering issues @@ -182,6 +180,7 @@ void Emulator::pollScheduler() { } case Scheduler::EventType::SignalY2R: kernel.getServiceManager().getY2R().signalConversionDone(); break; + case Scheduler::EventType::UpdateIR: kernel.getServiceManager().getIRUser().updateCirclePadPro(); break; default: { Helpers::panic("Scheduler: Unimplemented event type received: %d\n", static_cast(eventType));