Initial CPP implementation

This commit is contained in:
wheremyfoodat 2025-06-30 01:33:32 +03:00
parent ce356c6e61
commit 382c0f953d
14 changed files with 658 additions and 184 deletions

View file

@ -81,6 +81,7 @@ struct EmulatorConfig {
bool sdCardInserted = true;
bool sdWriteProtected = false;
bool circlePadProEnabled = true;
bool usePortableBuild = false;
bool audioEnabled = audioEnabledDefault;

View file

@ -0,0 +1,14 @@
#pragma once
#include "services/ir/ir_device.hpp"
#include "services/ir/ir_types.hpp"
namespace IR {
class CirclePadPro : public IR::Device {
public:
virtual void connect() override;
virtual void disconnect() override;
virtual void receivePayload(Payload payload) override;
CirclePadPro(SendCallback sendCallback) : IR::Device(sendCallback) {}
};
} // namespace IR

View file

@ -0,0 +1,23 @@
#pragma once
#include <functional>
#include <span>
#include "helpers.hpp"
namespace IR {
class Device {
public:
using Payload = std::span<const u8>;
protected:
using SendCallback = std::function<void(Payload)>; // Callback for sending data from IR device->3DS
SendCallback sendCallback;
public:
virtual void connect() = 0;
virtual void disconnect() = 0;
virtual void receivePayload(Payload payload) = 0;
Device(SendCallback sendCallback) : sendCallback(sendCallback) {}
};
} // namespace IR

View file

@ -0,0 +1,148 @@
#pragma once
#include <array>
#include "bitfield.hpp"
#include "helpers.hpp"
#include "memory.hpp"
#include "swap.hpp"
// Type definitions for the IR service
// Based off https://github.com/azahar-emu/azahar/blob/de7b457ee466e432047c4ee8d37fca9eae7e031e/src/core/hle/service/ir/ir_user.cpp#L88
namespace IR {
class Buffer {
public:
Buffer(Memory& memory, u32 sharedMemBaseVaddr, u32 infoOffset, u32 bufferOffset, u32 maxPackets, u32 bufferSize)
: memory(memory), sharedMemBaseVaddr(sharedMemBaseVaddr), infoOffset(infoOffset), bufferOffset(bufferOffset), maxPackets(maxPackets),
maxDataSize(bufferSize - sizeof(PacketInfo) * maxPackets) {
updateBufferInfo();
}
u8* getPointer(u32 offset) { return (u8*)memory.getReadPointer(sharedMemBaseVaddr + offset); }
bool put(std::span<const u8> packet) {
if (info.packetCount == maxPackets) {
return false;
}
u32 offset;
// finds free space offset in data buffer
if (info.packetCount == 0) {
offset = 0;
if (packet.size() > maxDataSize) {
return false;
}
} else {
const u32 lastIndex = (info.endIndex + maxPackets - 1) % maxPackets;
const PacketInfo first = getPacketInfo(info.beginIndex);
const PacketInfo last = getPacketInfo(lastIndex);
offset = (last.offset + last.size) % maxDataSize;
const u32 freeSpace = (first.offset + maxDataSize - offset) % maxDataSize;
if (packet.size() > freeSpace) {
return false;
}
}
// Writes packet info
PacketInfo packet_info{offset, static_cast<u32>(packet.size())};
setPacketInfo(info.endIndex, packet_info);
// Writes packet data
for (std::size_t i = 0; i < packet.size(); ++i) {
*getDataBufferPointer((offset + i) % maxDataSize) = packet[i];
}
// Updates buffer info
info.endIndex++;
info.endIndex %= maxPackets;
info.packetCount++;
updateBufferInfo();
return true;
}
bool release(u32 count) {
if (info.packetCount < count) {
return false;
}
info.packetCount -= count;
info.beginIndex += count;
info.beginIndex %= maxPackets;
updateBufferInfo();
return true;
}
u32 getPacketCount() { return info.packetCount; }
private:
struct BufferInfo {
u32_le beginIndex;
u32_le endIndex;
u32_le packetCount;
u32_le unknown;
};
static_assert(sizeof(BufferInfo) == 16, "IR::BufferInfo has wrong size!");
struct PacketInfo {
u32_le offset;
u32_le size;
};
static_assert(sizeof(PacketInfo) == 8, "IR::PacketInfo has wrong size!");
u8* getPacketInfoPointer(u32 index) { return getPointer(bufferOffset + sizeof(PacketInfo) * index); }
void setPacketInfo(u32 index, const PacketInfo& packet_info) { std::memcpy(getPacketInfoPointer(index), &packet_info, sizeof(PacketInfo)); }
PacketInfo getPacketInfo(u32 index) {
PacketInfo packet_info;
std::memcpy(&packet_info, getPacketInfoPointer(index), sizeof(PacketInfo));
return packet_info;
}
u8* getDataBufferPointer(u32 offset) { return getPointer(bufferOffset + sizeof(PacketInfo) * maxPackets + offset); }
void updateBufferInfo() {
if (infoOffset) {
std::memcpy(getPointer(infoOffset), &info, sizeof(info));
}
}
BufferInfo info{0, 0, 0, 0};
Memory& memory;
u32 sharedMemBaseVaddr;
u32 infoOffset;
u32 bufferOffset;
u32 maxPackets;
u32 maxDataSize;
};
struct CPPResponse {
union {
BitField<0, 8, u32> header;
BitField<8, 12, u32> c_stick_x;
BitField<20, 12, u32> c_stick_y;
} c_stick;
union {
BitField<0, 5, u8> battery_level;
BitField<5, 1, u8> zl_not_held;
BitField<6, 1, u8> zr_not_held;
BitField<7, 1, u8> r_not_held;
} buttons;
u8 unknown;
};
static_assert(sizeof(CPPResponse) == 6, "Circlepad Pro response has wrong size");
namespace CPPResponseID {
enum : u8 {
PollButtons = 0x10, // This response contains the current Circlepad Pro button/stick state
ReadCalibrationData = 0x11, // Responding to a ReadCalibrationData request
};
}
namespace CPPRequestID {
enum : u8 {
ConfigurePolling = 1, // Configures if & how often to poll the Circlepad Pro button/stick state
ReadCalibrationData = 2, // Reads the Circlepad Pro calibration data
};
}
} // namespace IR

View file

@ -1,11 +1,14 @@
#pragma once
#include <optional>
#include <span>
#include "config.hpp"
#include "helpers.hpp"
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
#include "services/ir/circlepad_pro.hpp"
// Circular dependencies in this project? Never
class Kernel;
@ -20,21 +23,29 @@ class IRUserService {
Handle handle = KernelHandles::IR_USER;
Memory& mem;
Kernel& kernel;
const EmulatorConfig& config;
MAKE_LOG_FUNCTION(log, irUserLogger)
// Service commands
void clearReceiveBuffer(u32 messagePointer);
void clearSendBuffer(u32 messagePointer);
void disconnect(u32 messagePointer);
void finalizeIrnop(u32 messagePointer);
void getConnectionStatusEvent(u32 messagePointer);
void getReceiveEvent(u32 messagePointer);
void getSendEvent(u32 messagePointer);
void initializeIrnopShared(u32 messagePointer);
void requireConnection(u32 messagePointer);
void sendIrnop(u32 messagePointer);
void releaseReceivedData(u32 messagePointer);
using IREvent = std::optional<Handle>;
IREvent connectionStatusEvent = std::nullopt;
IREvent receiveEvent = std::nullopt;
IREvent sendEvent = std::nullopt;
IR::CirclePadPro cpp;
std::optional<MemoryBlock> sharedMemory = std::nullopt;
bool connectedDevice = false;
@ -56,8 +67,19 @@ class IRUserService {
};
static_assert(sizeof(SharedMemoryStatus) == 16);
// The IR service uses CRC8 with generator polynomial = 0x07 for verifying packets received from IR devices
static u8 crc8(std::span<const u8> data);
// IR service calls this to send a console->device payload
void receivePayload(std::span<const u8> data);
// IR devices call this to send a device->console payload
void sendPayload(std::span<const u8> payload);
public:
IRUserService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
IRUserService(Memory& mem, const EmulatorConfig& config, Kernel& kernel)
: mem(mem), config(config), kernel(kernel), cpp([&](IR::Device::Payload payload) { sendPayload(payload); }) {}
void reset();
void handleSyncRequest(u32 messagePointer);
void updateCirclePadPro();
};

View file

@ -23,7 +23,7 @@
#include "services/gsp_lcd.hpp"
#include "services/hid.hpp"
#include "services/http.hpp"
#include "services/ir_user.hpp"
#include "services/ir/ir_user.hpp"
#include "services/ldr_ro.hpp"
#include "services/mcu/mcu_hwc.hpp"
#include "services/mic.hpp"
@ -114,4 +114,5 @@ class ServiceManager {
NFCService& getNFC() { return nfc; }
DSPService& getDSP() { return dsp; }
Y2RService& getY2R() { return y2r; }
IRUserService& getIRUser() { return ir_user; }
};