diff --git a/include/services/hid.hpp b/include/services/hid.hpp index c13d0756..70a25673 100644 --- a/include/services/hid.hpp +++ b/include/services/hid.hpp @@ -27,7 +27,7 @@ namespace HID::Keys { CirclePadRight = 1 << 28, // X >= 41 CirclePadLeft = 1 << 29, // X <= -41 CirclePadUp = 1 << 30, // Y >= 41 - CirclePadDown = 1 << 31 // Y <= -41 + CirclePadDown = 1u << 31 // Y <= -41 }; } @@ -40,6 +40,16 @@ class HIDService { Kernel& kernel; u8* sharedMem = nullptr; // Pointer to HID shared memory + uint nextPadIndex; + uint nextTouchscreenIndex; + uint nextAccelerometerIndex; + uint nextGyroIndex; + + u32 newButtons; // The button state currently being edited + u32 oldButtons; // The previous pad state + + s16 circlePadX, circlePadY; // Circlepad state + bool accelerometerEnabled; bool eventsInitialized; bool gyroEnabled; @@ -55,16 +65,50 @@ class HIDService { void getGyroscopeCoefficient(u32 messagePointer); void getIPCHandles(u32 messagePointer); + // Don't call these prior to initializing shared mem pls + template + T readSharedMem(size_t offset) { + return *(T*)&sharedMem[offset]; + } + + template + void writeSharedMem(size_t offset, T value) { + *(T*)&sharedMem[offset] = value; + } + public: HIDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); - void pressKey(u32 key); - void releaseKey(u32 key); - void setCirclepadX(u16 x); - void setCirclepadY(u16 y); - void updateInputs(); + void pressKey(u32 mask) { newButtons |= mask; } + void releaseKey(u32 mask) { newButtons &= ~mask; } + + void setCirclepadX(s16 x) { + circlePadX = x; + + // Turn bits 28 and 29 off in the new button state, which indicate whether the circlepad is steering left or right + // Then, set them according to the new value of x + newButtons &= ~0x3000'0000; + if (x >= 41) // Pressing right + newButtons |= 1 << 28; + else if (x <= -41) // Pressing left + newButtons |= 1 << 29; + } + + void setCirclepadY(s16 y) { + circlePadY = y; + + // Turn bits 30 and 31 off in the new button state, which indicate whether the circlepad is steering up or down + // Then, set them according to the new value of y + newButtons &= ~0xC000'0000; + if (y >= 41) // Pressing up + newButtons |= 1 << 30; + else if (y <= -41) // Pressing down + newButtons |= 1 << 31; + } + + void updateInputs(u64 currentTimestamp); void setSharedMem(u8* ptr) { sharedMem = ptr; diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 088adbc5..8084c1ea 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -89,5 +89,5 @@ public: void releaseKey(u32 key) { hid.releaseKey(key); } void setCirclepadX(u16 x) { hid.setCirclepadX(x); } void setCirclepadY(u16 y) { hid.setCirclepadY(y); } - void updateInputs() { hid.updateInputs(); } + void updateInputs(u64 currentTimestamp) { hid.updateInputs(currentTimestamp); } }; \ No newline at end of file diff --git a/src/core/services/hid.cpp b/src/core/services/hid.cpp index 854a4286..cc4d5575 100644 --- a/src/core/services/hid.cpp +++ b/src/core/services/hid.cpp @@ -30,6 +30,12 @@ void HIDService::reset() { for (auto& e : events) { e = std::nullopt; } + + // Reset indices for the various HID shared memory entries + nextPadIndex = nextTouchscreenIndex = nextAccelerometerIndex = nextGyroIndex = 0; + // Reset button states + newButtons = oldButtons = 0; + circlePadX = circlePadY = 0; } void HIDService::handleSyncRequest(u32 messagePointer) { @@ -108,13 +114,58 @@ void HIDService::getIPCHandles(u32 messagePointer) { } } -void HIDService::pressKey(u32 key) { sharedMem[0]++; *(u32*)&sharedMem[0x28] |= key; } -void HIDService::releaseKey(u32 key) { sharedMem[0]++; *(u32*)&sharedMem[0x28] &= ~key; } -void HIDService::setCirclepadX(u16 x) { sharedMem[0]++; *(u16*)&sharedMem[0x28 + 0xC] = x; } -void HIDService::setCirclepadY(u16 y) { sharedMem[0]++; *(u16*)&sharedMem[0x28 + 0xC + 2] = y; } +void HIDService::updateInputs(u64 currentTick) { + // Update shared memory if it has been initialized + if (sharedMem) { + // First, update the pad state + if (nextPadIndex == 0) { + writeSharedMem(0x8, readSharedMem(0x0)); // Copy previous tick count + writeSharedMem(0x0, currentTick); // Write new tick count + } + + writeSharedMem(0x10, nextPadIndex); // Index last updated by the HID module + writeSharedMem(0x1C, newButtons); // Current PAD state + writeSharedMem(0x20, circlePadX); // Current circle pad state + writeSharedMem(0x22, circlePadY); + + const size_t padEntryOffset = 0x28 + (nextPadIndex * 0x10); // Offset in the array of 8 pad entries + nextPadIndex = (nextPadIndex + 1) % 8; // Move to next entry + + const u32 pressed = (newButtons ^ oldButtons) & newButtons; // Pressed buttons + const u32 released = (newButtons ^ oldButtons) & oldButtons; // Released buttons + oldButtons = newButtons; + + writeSharedMem(padEntryOffset, newButtons); + writeSharedMem(padEntryOffset + 4, pressed); + writeSharedMem(padEntryOffset + 8, released); + writeSharedMem(padEntryOffset + 12, circlePadX); + writeSharedMem(padEntryOffset + 14, circlePadY); + + // Next, update touchscreen state + if (nextTouchscreenIndex == 0) { + writeSharedMem(0xB0, readSharedMem(0xA8)); // Copy previous tick count + writeSharedMem(0xA8, currentTick); // Write new tick count + } + writeSharedMem(0xB8, nextTouchscreenIndex); // Index last updated by the HID module + nextTouchscreenIndex = (nextTouchscreenIndex + 1) % 8; // Move to next entry + + // Next, update accelerometer state + if (nextAccelerometerIndex == 0) { + writeSharedMem(0x110, readSharedMem(0x108)); // Copy previous tick count + writeSharedMem(0x108, currentTick); // Write new tick count + } + writeSharedMem(0x118, nextAccelerometerIndex); // Index last updated by the HID module + nextAccelerometerIndex = (nextAccelerometerIndex + 1) % 8; // Move to next entry + + // Next, update gyro state + if (nextGyroIndex == 0) { + writeSharedMem(0x160, readSharedMem(0x158)); // Copy previous tick count + writeSharedMem(0x158, currentTick); // Write new tick count + } + writeSharedMem(0x168, nextGyroIndex); // Index last updated by the HID module + nextGyroIndex = (nextGyroIndex + 1) % 32; // Move to next entry + } -// TODO: We don't currently have inputs but we must at least try to signal the HID key input events now and then -void HIDService::updateInputs() { // For some reason, the original developers decided to signal the HID events each time the OS rescanned inputs // Rather than once every time the state of a key, or the accelerometer state, etc is updated // This means that the OS will signal the events even if literally nothing happened diff --git a/src/emulator.cpp b/src/emulator.cpp index 19fdcd82..9d274ebf 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -95,7 +95,7 @@ void Emulator::run() { } // Update inputs in the HID module - srv.updateInputs(); + srv.updateInputs(cpu.getTicks()); SDL_GL_SwapWindow(window); } }