Skip to content

Commit

Permalink
Core/WiimoteEmu: Add functions to Nunchuk, Classic Controller, and Mo…
Browse files Browse the repository at this point in the history
…tionPlus extensions to get/set data without duplicate bithacks everywhere.
  • Loading branch information
jordan-woyak committed Feb 17, 2020
1 parent 8343dad commit 5af2081
Show file tree
Hide file tree
Showing 13 changed files with 651 additions and 285 deletions.
76 changes: 38 additions & 38 deletions Source/Core/Core/HW/WiimoteCommon/DataReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cassert>

#include "Common/BitUtils.h"
#include "Common/MathUtil.h"
#include "Core/HW/WiimoteCommon/DataReport.h"

namespace WiimoteCommon
Expand Down Expand Up @@ -75,40 +76,35 @@ struct IncludeAccel : virtual DataReportManipulator
void GetAccelData(AccelData* result) const override
{
const AccelMSB accel = Common::BitCastPtr<AccelMSB>(data_ptr + 2);
result->x = accel.x << 2;
result->y = accel.y << 2;
result->z = accel.z << 2;

// LSBs
const CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
result->x |= core.acc_bits & 0b11;
result->y |= (core.acc_bits2 & 0b1) << 1;
result->z |= core.acc_bits2 & 0b10;

// X has 10 bits of precision.
result->value.x = accel.x << 2;
result->value.x |= core.acc_bits & 0b11;

// Y and Z only have 9 bits of precision. (convert them to 10)
result->value.y =
Common::ExpandValue<u16>(accel.y << 1 | Common::ExtractBit<0>(core.acc_bits2), 1);
result->value.z =
Common::ExpandValue<u16>(accel.z << 1 | Common::ExtractBit<1>(core.acc_bits2), 1);
}

void SetAccelData(const AccelData& new_accel) override
{
AccelMSB accel = {};
accel.x = new_accel.x >> 2;
accel.y = new_accel.y >> 2;
accel.z = new_accel.z >> 2;
Common::BitCastPtr<AccelMSB>(data_ptr + 2) = accel;
Common::BitCastPtr<AccelMSB>(data_ptr + 2) = AccelMSB(new_accel.value / 4);

// LSBs
CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
core.acc_bits = (new_accel.x >> 0) & 0b11;
core.acc_bits2 = (new_accel.y >> 1) & 0x1;
core.acc_bits2 |= (new_accel.z & 0xb10);
core.acc_bits = (new_accel.value.x >> 0) & 0b11;
core.acc_bits2 = (new_accel.value.y >> 1) & 0x1;
core.acc_bits2 |= (new_accel.value.z & 0xb10);
Common::BitCastPtr<CoreData>(data_ptr) = core;
}

bool HasAccel() const override { return true; }

private:
struct AccelMSB
{
u8 x, y, z;
};
using AccelMSB = Common::TVec3<u8>;
static_assert(sizeof(AccelMSB) == 3, "Wrong size");
};

Expand Down Expand Up @@ -195,26 +191,28 @@ struct ReportExt21 : NoCore, NoAccel, NoIR, IncludeExt<0, 21>
struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt
{
// FYI: Only 8-bits of precision in this report, and no Y axis.
// Only contains 4 MSB of Z axis.

void GetAccelData(AccelData* accel) const override
{
accel->x = data_ptr[2] << 2;
// X axis only has 8 bits of precision. (converted to 10)
accel->value.x = Common::ExpandValue<u16>(data_ptr[2], 2);

// Y axis is not contained in this report. (provided by "Interleave2")

// Retain lower 6 bits.
accel->z &= 0b111111;
// Clear upper bits, retain lower bits. (provided by "Interleave2")
accel->value.z &= 0b111111;

// Report only contains 4 MSB of Z axis.
const CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
accel->z |= (core.acc_bits << 6) | (core.acc_bits2 << 8);
accel->value.z |= (core.acc_bits << 6) | (core.acc_bits2 << 8);
}

void SetAccelData(const AccelData& accel) override
{
data_ptr[2] = accel.x >> 2;
data_ptr[2] = accel.value.x >> 2;

CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
core.acc_bits = (accel.z >> 6) & 0b11;
core.acc_bits2 = (accel.z >> 8) & 0b11;
core.acc_bits = (accel.value.z >> 6) & 0b11;
core.acc_bits2 = (accel.value.z >> 8) & 0b11;
Common::BitCastPtr<CoreData>(data_ptr) = core;
}

Expand All @@ -226,26 +224,28 @@ struct ReportInterleave1 : IncludeCore, IncludeIR<3, 18, 0>, NoExt
struct ReportInterleave2 : IncludeCore, IncludeIR<3, 18, 18>, NoExt
{
// FYI: Only 8-bits of precision in this report, and no X axis.
// Only contains 4 LSB of Z axis.

void GetAccelData(AccelData* accel) const override
{
accel->y = data_ptr[2] << 2;
// X axis is not contained in this report. (provided by "Interleave1")

// Y axis only has 8 bits of precision. (converted to 10)
accel->value.y = Common::ExpandValue<u16>(data_ptr[2], 2);

// Retain upper 4 bits.
accel->z &= ~0b111111;
// Clear lower bits, retain upper bits. (provided by "Interleave1")
accel->value.z &= ~0b111111;

// Report only contains 4 LSBs of Z axis. (converted to 6)
const CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
accel->z |= (core.acc_bits << 2) | (core.acc_bits2 << 4);
accel->value.z |= Common::ExpandValue<u16>(core.acc_bits | core.acc_bits2 << 2, 2);
}

void SetAccelData(const AccelData& accel) override
{
data_ptr[2] = accel.y >> 2;
data_ptr[2] = accel.value.y >> 2;

CoreData core = Common::BitCastPtr<CoreData>(data_ptr);
core.acc_bits = (accel.z >> 2) & 0b11;
core.acc_bits2 = (accel.z >> 4) & 0b11;
core.acc_bits = (accel.value.z >> 2) & 0b11;
core.acc_bits2 = (accel.value.z >> 4) & 0b11;
Common::BitCastPtr<CoreData>(data_ptr) = core;
}

Expand Down
12 changes: 3 additions & 9 deletions Source/Core/Core/HW/WiimoteCommon/DataReport.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#include <memory>

#include "Common/CommonTypes.h"
#include "Common/Matrix.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"

namespace WiimoteCommon
{
Expand All @@ -21,12 +23,6 @@ class DataReportManipulator
public:
virtual ~DataReportManipulator() = default;

// Accel data handled as if there were always 10 bits of precision.
struct AccelData
{
u16 x, y, z;
};

using CoreData = ButtonData;

virtual bool HasCore() const = 0;
Expand Down Expand Up @@ -66,7 +62,6 @@ class DataReportBuilder
explicit DataReportBuilder(InputReportID rpt_id);

using CoreData = ButtonData;
using AccelData = DataReportManipulator::AccelData;

void SetMode(InputReportID rpt_id);
InputReportID GetMode() const;
Expand Down Expand Up @@ -99,11 +94,10 @@ class DataReportBuilder

u32 GetDataSize() const;

private:
static constexpr int HEADER_SIZE = 2;

static constexpr int MAX_DATA_SIZE = MAX_PAYLOAD - 2;

private:
TypedHIDInputData<std::array<u8, MAX_DATA_SIZE>> m_data;

std::unique_ptr<DataReportManipulator> m_manip;
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ namespace WiimoteCommon
{
constexpr u8 MAX_PAYLOAD = 23;

// Based on testing, old WiiLi.org docs, and WiiUse library:
// Max battery level seems to be 0xc8 (decimal 200)
constexpr u8 MAX_BATTERY_LEVEL = 0xc8;

enum class InputReportID : u8
{
Status = 0x20,
Expand Down
104 changes: 104 additions & 0 deletions Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include <vector>

#include "Common/CommonTypes.h"
#include "Common/Matrix.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"

#ifdef _MSC_VER
#pragma warning(push)
Expand Down Expand Up @@ -41,6 +43,8 @@ static_assert(sizeof(OutputReportGeneric) == 2, "Wrong size");

struct OutputReportRumble
{
static constexpr OutputReportID REPORT_ID = OutputReportID::Rumble;

u8 rumble : 1;
};
static_assert(sizeof(OutputReportRumble) == 1, "Wrong size");
Expand All @@ -55,8 +59,34 @@ struct OutputReportEnableFeature
};
static_assert(sizeof(OutputReportEnableFeature) == 1, "Wrong size");

struct OutputReportIRLogicEnable : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::IRLogicEnable;
};
static_assert(sizeof(OutputReportIRLogicEnable) == 1, "Wrong size");

struct OutputReportIRLogicEnable2 : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::IRLogicEnable2;
};
static_assert(sizeof(OutputReportIRLogicEnable2) == 1, "Wrong size");

struct OutputReportSpeakerEnable : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::SpeakerEnable;
};
static_assert(sizeof(OutputReportSpeakerEnable) == 1, "Wrong size");

struct OutputReportSpeakerMute : OutputReportEnableFeature
{
static constexpr OutputReportID REPORT_ID = OutputReportID::SpeakerMute;
};
static_assert(sizeof(OutputReportSpeakerMute) == 1, "Wrong size");

struct OutputReportLeds
{
static constexpr OutputReportID REPORT_ID = OutputReportID::LED;

u8 rumble : 1;
u8 ack : 1;
u8 : 2;
Expand All @@ -66,6 +96,8 @@ static_assert(sizeof(OutputReportLeds) == 1, "Wrong size");

struct OutputReportMode
{
static constexpr OutputReportID REPORT_ID = OutputReportID::ReportMode;

u8 rumble : 1;
u8 ack : 1;
u8 continuous : 1;
Expand All @@ -76,13 +108,17 @@ static_assert(sizeof(OutputReportMode) == 2, "Wrong size");

struct OutputReportRequestStatus
{
static constexpr OutputReportID REPORT_ID = OutputReportID::RequestStatus;

u8 rumble : 1;
u8 : 7;
};
static_assert(sizeof(OutputReportRequestStatus) == 1, "Wrong size");

struct OutputReportWriteData
{
static constexpr OutputReportID REPORT_ID = OutputReportID::WriteData;

u8 rumble : 1;
u8 : 1;
u8 space : 2;
Expand All @@ -100,6 +136,8 @@ static_assert(sizeof(OutputReportWriteData) == 21, "Wrong size");

struct OutputReportReadData
{
static constexpr OutputReportID REPORT_ID = OutputReportID::ReadData;

u8 rumble : 1;
u8 : 1;
u8 space : 2;
Expand All @@ -116,6 +154,8 @@ static_assert(sizeof(OutputReportReadData) == 6, "Wrong size");

struct OutputReportSpeakerData
{
static constexpr OutputReportID REPORT_ID = OutputReportID::SpeakerData;

u8 rumble : 1;
u8 : 2;
u8 length : 5;
Expand Down Expand Up @@ -157,6 +197,8 @@ static_assert(sizeof(ButtonData) == 2, "Wrong size");

struct InputReportStatus
{
static constexpr InputReportID REPORT_ID = InputReportID::Status;

ButtonData buttons;
u8 battery_low : 1;
u8 extension : 1;
Expand All @@ -170,6 +212,8 @@ static_assert(sizeof(InputReportStatus) == 6, "Wrong size");

struct InputReportAck
{
static constexpr InputReportID REPORT_ID = InputReportID::Ack;

ButtonData buttons;
OutputReportID rpt_id;
ErrorCode error_code;
Expand All @@ -178,6 +222,8 @@ static_assert(sizeof(InputReportAck) == 4, "Wrong size");

struct InputReportReadDataReply
{
static constexpr InputReportID REPORT_ID = InputReportID::ReadDataReply;

ButtonData buttons;
u8 error : 4;
u8 size_minus_one : 4;
Expand All @@ -187,6 +233,64 @@ struct InputReportReadDataReply
};
static_assert(sizeof(InputReportReadDataReply) == 21, "Wrong size");

// Accel data handled as if there were always 10 bits of precision.
using AccelType = Common::TVec3<u16>;
using AccelData = ControllerEmu::RawValue<AccelType, 10>;

// Found in Wiimote EEPROM and Nunchuk "register".
// 0g and 1g points exist.
struct AccelCalibrationPoint
{
// All components have 10 bits of precision.
u16 GetX() const { return x2 << 2 | x1; }
u16 GetY() const { return y2 << 2 | y1; }
u16 GetZ() const { return z2 << 2 | z1; }
auto Get() const { return AccelType{GetX(), GetY(), GetZ()}; }

void SetX(u16 x)
{
x2 = x >> 2;
x1 = x;
}
void SetY(u16 y)
{
y2 = y >> 2;
y1 = y;
}
void SetZ(u16 z)
{
z2 = z >> 2;
z1 = z;
}
void Set(AccelType accel)
{
SetX(accel.x);
SetY(accel.y);
SetZ(accel.z);
}

u8 x2, y2, z2;
u8 z1 : 2;
u8 y1 : 2;
u8 x1 : 2;
u8 : 2;
};

// Located at 0x16 and 0x20 of Wii Remote EEPROM.
struct AccelCalibrationData
{
using Calibration = ControllerEmu::TwoPointCalibration<AccelType, 10>;

auto GetCalibration() const { return Calibration(zero_g.Get(), one_g.Get()); }

AccelCalibrationPoint zero_g;
AccelCalibrationPoint one_g;

u8 volume : 7;
u8 motor : 1;
u8 checksum;
};

} // namespace WiimoteCommon

#pragma pack(pop)
Expand Down
Loading

0 comments on commit 5af2081

Please sign in to comment.