mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 14:45:41 +12:00
commit
c7e3343974
7 changed files with 205 additions and 16 deletions
|
@ -35,6 +35,8 @@ include_directories(${SDL2_INCLUDE_DIR})
|
|||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||
set(Boost_NO_SYSTEM_PATHS ON)
|
||||
add_compile_definitions(BOOST_NO_CXX98_FUNCTION_BASE) # Forbid Boost from using std::unary_function (Fixes MacOS build)
|
||||
|
||||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
|
||||
|
|
|
@ -199,7 +199,8 @@ public:
|
|||
return &objects[handle];
|
||||
}
|
||||
|
||||
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.requestGPUInterrupt(type); }
|
||||
ServiceManager& getServiceManager() { return serviceManager; }
|
||||
|
||||
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
|
||||
void signalDSPEvents() { serviceManager.signalDSPEvents(); }
|
||||
void updateInputs() { serviceManager.updateInputs(); }
|
||||
};
|
|
@ -6,6 +6,31 @@
|
|||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace HID::Keys {
|
||||
enum : u32 {
|
||||
A = 1 << 0,
|
||||
B = 1 << 1,
|
||||
Select = 1 << 2,
|
||||
Start = 1 << 3,
|
||||
Right = 1 << 4,
|
||||
Left = 1 << 5,
|
||||
Up = 1 << 6,
|
||||
Down = 1 << 7,
|
||||
R = 1 << 8,
|
||||
L = 1 << 9,
|
||||
X = 1 << 10,
|
||||
Y = 1 << 11,
|
||||
|
||||
GPIO0Inv = 1 << 12, // Inverted value of GPIO bit 0
|
||||
GPIO14Inv = 1 << 13, // Inverted value of GPIO bit 14
|
||||
|
||||
CirclePadRight = 1 << 28, // X >= 41
|
||||
CirclePadLeft = 1 << 29, // X <= -41
|
||||
CirclePadUp = 1 << 30, // Y >= 41
|
||||
CirclePadDown = 1u << 31 // Y <= -41
|
||||
};
|
||||
}
|
||||
|
||||
// Circular dependency because we need HID to spawn events
|
||||
class Kernel;
|
||||
|
||||
|
@ -15,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;
|
||||
|
@ -30,11 +65,50 @@ class HIDService {
|
|||
void getGyroscopeCoefficient(u32 messagePointer);
|
||||
void getIPCHandles(u32 messagePointer);
|
||||
|
||||
// Don't call these prior to initializing shared mem pls
|
||||
template <typename T>
|
||||
T readSharedMem(size_t offset) {
|
||||
return *(T*)&sharedMem[offset];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 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;
|
||||
|
|
|
@ -79,10 +79,15 @@ public:
|
|||
void sendCommandToService(u32 messagePointer, Handle handle);
|
||||
|
||||
// Wrappers for communicating with certain services
|
||||
void requestGPUInterrupt(GPUInterrupt type) { gsp_gpu.requestInterrupt(type); }
|
||||
void sendGPUInterrupt(GPUInterrupt type) { gsp_gpu.requestInterrupt(type); }
|
||||
void setGSPSharedMem(u8* ptr) { gsp_gpu.setSharedMem(ptr); }
|
||||
void setHIDSharedMem(u8* ptr) { hid.setSharedMem(ptr); }
|
||||
|
||||
void signalDSPEvents() { dsp.signalEvents(); }
|
||||
void updateInputs() { hid.updateInputs(); }
|
||||
|
||||
void pressKey(u32 key) { hid.pressKey(key); }
|
||||
void releaseKey(u32 key) { hid.releaseKey(key); }
|
||||
void setCirclepadX(u16 x) { hid.setCirclepadX(x); }
|
||||
void setCirclepadY(u16 y) { hid.setCirclepadY(y); }
|
||||
void updateInputs(u64 currentTimestamp) { hid.updateInputs(currentTimestamp); }
|
||||
};
|
|
@ -5,9 +5,7 @@ Panda3DS is an HLE, red-panda-themed Nintendo 3DS emulator written in C++ which
|
|||
  
|
||||
|
||||
# Compatibility
|
||||
Panda3DS is still in the early stages of development. Many games boot, many don't. Most games have at least some hilariously broken graphics, audio is not supported, performance leaves a bit to be desired mainly thanks to lack of shader acceleration, and most QoL features (including input, or a GUI!) are missing.
|
||||
|
||||
In fact, the screenshots in the repo were created after I hooked the input state to rand() locally.
|
||||
Panda3DS is still in the early stages of development. Many games boot, many don't. Most games have at least some hilariously broken graphics, audio is not supported, performance leaves a bit to be desired mainly thanks to lack of shader acceleration, and most QoL features (including a GUI) are missing.
|
||||
|
||||
In addition, some games don't quiiite work with the upstream code. A lot of them might need some panics in the source code to be commented out before they work, etc. However, just the fact things can work as well as they do now is promising in itself.
|
||||
|
||||
|
|
|
@ -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,8 +114,58 @@ void HIDService::getIPCHandles(u32 messagePointer) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
void HIDService::updateInputs(u64 currentTick) {
|
||||
// Update shared memory if it has been initialized
|
||||
if (sharedMem) {
|
||||
// First, update the pad state
|
||||
if (nextPadIndex == 0) {
|
||||
writeSharedMem<u64>(0x8, readSharedMem<u64>(0x0)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x0, currentTick); // Write new tick count
|
||||
}
|
||||
|
||||
writeSharedMem<u32>(0x10, nextPadIndex); // Index last updated by the HID module
|
||||
writeSharedMem<u32>(0x1C, newButtons); // Current PAD state
|
||||
writeSharedMem<s16>(0x20, circlePadX); // Current circle pad state
|
||||
writeSharedMem<s16>(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<u32>(padEntryOffset, newButtons);
|
||||
writeSharedMem<u32>(padEntryOffset + 4, pressed);
|
||||
writeSharedMem<u32>(padEntryOffset + 8, released);
|
||||
writeSharedMem<s16>(padEntryOffset + 12, circlePadX);
|
||||
writeSharedMem<s16>(padEntryOffset + 14, circlePadY);
|
||||
|
||||
// Next, update touchscreen state
|
||||
if (nextTouchscreenIndex == 0) {
|
||||
writeSharedMem<u64>(0xB0, readSharedMem<u64>(0xA8)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0xA8, currentTick); // Write new tick count
|
||||
}
|
||||
writeSharedMem<u32>(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<u64>(0x110, readSharedMem<u64>(0x108)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x108, currentTick); // Write new tick count
|
||||
}
|
||||
writeSharedMem<u32>(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<u64>(0x160, readSharedMem<u64>(0x158)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x158, currentTick); // Write new tick count
|
||||
}
|
||||
writeSharedMem<u32>(0x168, nextGyroIndex); // Index last updated by the HID module
|
||||
nextGyroIndex = (nextGyroIndex + 1) % 32; // Move to next entry
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -27,22 +27,75 @@ void Emulator::run() {
|
|||
runFrame(); // Run 1 frame of instructions
|
||||
gpu.display(); // Display graphics
|
||||
|
||||
// Send VBlank interrupts
|
||||
kernel.sendGPUInterrupt(GPUInterrupt::VBlank0);
|
||||
kernel.sendGPUInterrupt(GPUInterrupt::VBlank1);
|
||||
ServiceManager& srv = kernel.getServiceManager();
|
||||
|
||||
// Update inputs in the HID module
|
||||
kernel.updateInputs();
|
||||
// Send VBlank interrupts
|
||||
srv.sendGPUInterrupt(GPUInterrupt::VBlank0);
|
||||
srv.sendGPUInterrupt(GPUInterrupt::VBlank1);
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_QUIT) {
|
||||
namespace Keys = HID::Keys;
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
printf("Bye :(\n");
|
||||
running = false;
|
||||
return;
|
||||
case SDL_KEYDOWN:
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_l: srv.pressKey(Keys::A); break;
|
||||
case SDLK_k: srv.pressKey(Keys::B); break;
|
||||
case SDLK_o: srv.pressKey(Keys::X); break;
|
||||
case SDLK_i: srv.pressKey(Keys::Y); break;
|
||||
|
||||
case SDLK_q: srv.pressKey(Keys::L); break;
|
||||
case SDLK_p: srv.pressKey(Keys::R); break;
|
||||
|
||||
case SDLK_RIGHT: srv.pressKey(Keys::Right); break;
|
||||
case SDLK_LEFT: srv.pressKey(Keys::Left); break;
|
||||
case SDLK_UP: srv.pressKey(Keys::Up); break;
|
||||
case SDLK_DOWN: srv.pressKey(Keys::Down); break;
|
||||
|
||||
case SDLK_w: srv.setCirclepadY(0x9C); break;
|
||||
case SDLK_a: srv.setCirclepadX(-0x9C); break;
|
||||
case SDLK_s: srv.setCirclepadY(-0x9C); break;
|
||||
case SDLK_d: srv.setCirclepadX(0x9C); break;
|
||||
|
||||
case SDLK_RETURN: srv.pressKey(Keys::Start); break;
|
||||
case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_l: srv.releaseKey(Keys::A); break;
|
||||
case SDLK_k: srv.releaseKey(Keys::B); break;
|
||||
case SDLK_o: srv.releaseKey(Keys::X); break;
|
||||
case SDLK_i: srv.releaseKey(Keys::Y); break;
|
||||
|
||||
case SDLK_q: srv.releaseKey(Keys::L); break;
|
||||
case SDLK_p: srv.releaseKey(Keys::R); break;
|
||||
|
||||
case SDLK_RIGHT: srv.releaseKey(Keys::Right); break;
|
||||
case SDLK_LEFT: srv.releaseKey(Keys::Left); break;
|
||||
case SDLK_UP: srv.releaseKey(Keys::Up); break;
|
||||
case SDLK_DOWN: srv.releaseKey(Keys::Down); break;
|
||||
|
||||
// Err this is probably not ideal
|
||||
case SDLK_w: srv.setCirclepadY(0); break;
|
||||
case SDLK_a: srv.setCirclepadX(0); break;
|
||||
case SDLK_s: srv.setCirclepadY(0); break;
|
||||
case SDLK_d: srv.setCirclepadX(0); break;
|
||||
|
||||
case SDLK_RETURN: srv.releaseKey(Keys::Start); break;
|
||||
case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update inputs in the HID module
|
||||
srv.updateInputs(cpu.getTicks());
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue