mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-10 08:09:26 +01:00
WiimoteEmu: Cleanups and work towards making motion plus functional.
This commit is contained in:
parent
a25e8cb516
commit
42b9392784
@ -354,6 +354,7 @@ bool Wiimote::ProcessReadDataRequest()
|
||||
rpt.report_id = RT_READ_DATA_REPLY;
|
||||
|
||||
auto reply = &rpt.data;
|
||||
reply->error = 0;
|
||||
reply->buttons = m_status.buttons;
|
||||
reply->address = Common::swap16(m_read_request.address);
|
||||
|
||||
@ -377,6 +378,7 @@ bool Wiimote::ProcessReadDataRequest()
|
||||
// read the calibration data at the beginning of Eeprom. I think this
|
||||
// error is supposed to occur when we try to read above the freely
|
||||
// usable space that ends at 0x16ff.
|
||||
INFO_LOG(WIIMOTE, "Responding with read error 8.");
|
||||
reply->error = 0x08;
|
||||
}
|
||||
else
|
||||
@ -414,6 +416,7 @@ bool Wiimote::ProcessReadDataRequest()
|
||||
if (bytes_read != bytes_to_read)
|
||||
{
|
||||
// generate read error, 7 == no such slave (no ack)
|
||||
INFO_LOG(WIIMOTE, "Responding with read error 7.");
|
||||
reply->error = 0x07;
|
||||
}
|
||||
}
|
||||
@ -464,6 +467,8 @@ void Wiimote::DoState(PointerWrap& p)
|
||||
|
||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||
RealState();
|
||||
|
||||
// TODO: rebuild i2c bus state after state-change
|
||||
}
|
||||
|
||||
// load real Wiimote state
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// TODO: kill this whole file, we have Matrix functions in MathUtil.h
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/BitUtils.h"
|
||||
|
||||
#include "Core/Config/SYSCONFSettings.h"
|
||||
#include "Core/Config/WiimoteInputSettings.h"
|
||||
@ -330,9 +331,18 @@ void Wiimote::Reset()
|
||||
memset(&m_camera_logic.reg_data, 0, sizeof(m_camera_logic.reg_data));
|
||||
memset(&m_ext_logic.reg_data, 0, sizeof(m_ext_logic.reg_data));
|
||||
|
||||
memset(&m_motion_plus_logic.reg_data, 0, sizeof(m_motion_plus_logic.reg_data));
|
||||
memset(&m_motion_plus_logic.reg_data, 0x00, sizeof(m_motion_plus_logic.reg_data));
|
||||
memcpy(&m_motion_plus_logic.reg_data.ext_identifier, motion_plus_id, sizeof(motion_plus_id));
|
||||
|
||||
// calibration hackery
|
||||
static const u8 c1[16] = {0x78, 0xd9, 0x78, 0x38, 0x77, 0x9d, 0x2f, 0x0c, 0xcf, 0xf0, 0x31, 0xad,
|
||||
0xc8, 0x0b, 0x5e, 0x39};
|
||||
static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, 0xc9, 0xf5, 0x37, 0xc1,
|
||||
0x2d, 0xe9, 0x15, 0x8d};
|
||||
|
||||
//std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data);
|
||||
//std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10);
|
||||
|
||||
// status
|
||||
memset(&m_status, 0, sizeof(m_status));
|
||||
|
||||
@ -828,12 +838,6 @@ void Wiimote::UpdateIRData(bool use_accel)
|
||||
}
|
||||
}
|
||||
|
||||
void Wiimote::UpdateExtData()
|
||||
{
|
||||
// Write extension data to addr 0x00 of extension register
|
||||
m_extension->GetState(m_ext_logic.reg_data.controller_data);
|
||||
}
|
||||
|
||||
void Wiimote::Update()
|
||||
{
|
||||
// no channel == not connected i guess
|
||||
@ -896,26 +900,27 @@ void Wiimote::Update()
|
||||
}
|
||||
|
||||
// IR Camera
|
||||
// TODO: kill use_accel param
|
||||
// TODO: call only if camera logic is enabled?
|
||||
UpdateIRData(rptf.accel_size != 0);
|
||||
// TODO: kill use_accel param, I think it exists for TAS reasons..
|
||||
if (m_status.ir)
|
||||
UpdateIRData(rptf.accel_size != 0);
|
||||
|
||||
if (rptf.ir_size)
|
||||
{
|
||||
if (!m_status.ir)
|
||||
WARN_LOG(WIIMOTE, "Game is reading IR data without enabling IR logic first.");
|
||||
|
||||
m_i2c_bus.BusRead(IRCameraLogic::DEVICE_ADDR, offsetof(IRCameraLogic::RegData, camera_data),
|
||||
rptf.ir_size, feature_ptr);
|
||||
feature_ptr += rptf.ir_size;
|
||||
}
|
||||
|
||||
// motion plus
|
||||
auto* mplus_data =
|
||||
reinterpret_cast<wm_motionplus_data*>(m_motion_plus_logic.reg_data.controller_data);
|
||||
*mplus_data = wm_motionplus_data();
|
||||
mplus_data->is_mp_data = true;
|
||||
|
||||
// extension
|
||||
UpdateExtData();
|
||||
// extension / motion-plus
|
||||
if (rptf.ext_size)
|
||||
{
|
||||
// Update extension first as motion-plus will read from it.
|
||||
m_ext_logic.Update();
|
||||
m_motion_plus_logic.Update();
|
||||
|
||||
m_i2c_bus.BusRead(ExtensionLogic::DEVICE_ADDR, 0x00, rptf.ext_size, feature_ptr);
|
||||
feature_ptr += rptf.ext_size;
|
||||
}
|
||||
@ -1115,4 +1120,145 @@ bool Wiimote::ExtensionLogic::ReadDeviceDetectPin()
|
||||
return extension->active_extension ? true : false;
|
||||
}
|
||||
|
||||
void Wiimote::ExtensionLogic::Update()
|
||||
{
|
||||
// Update controller data from user input:
|
||||
// Write data to addr 0x00 of extension register
|
||||
extension->GetState(reg_data.controller_data);
|
||||
}
|
||||
|
||||
// TODO: move this to Common if it doesn't exist already?
|
||||
template <typename T>
|
||||
void SetBit(T& value, u32 bit_number, bool bit_value)
|
||||
{
|
||||
bit_value ? value |= (1 << bit_number) : value &= ~(1 << bit_number);
|
||||
}
|
||||
|
||||
void Wiimote::MotionPlusLogic::Update()
|
||||
{
|
||||
if (!IsActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: clean up this hackery:
|
||||
// the value seems to increase based on time starting after the first read of 0x00
|
||||
if (IsActive() && times_updated_since_activation < 0xff)
|
||||
{
|
||||
++times_updated_since_activation;
|
||||
|
||||
// TODO: wtf is this value actually..
|
||||
if (times_updated_since_activation == 9)
|
||||
reg_data.initialization_status = 0x4;
|
||||
else if (times_updated_since_activation == 10)
|
||||
reg_data.initialization_status = 0x8;
|
||||
else if (times_updated_since_activation == 18)
|
||||
reg_data.initialization_status = 0xc;
|
||||
else if (times_updated_since_activation == 53)
|
||||
reg_data.initialization_status = 0xe;
|
||||
}
|
||||
|
||||
auto& mplus_data = *reinterpret_cast<wm_motionplus_data*>(reg_data.controller_data);
|
||||
auto& data = reg_data.controller_data;
|
||||
|
||||
// TODO: make sure a motion plus report is sent first after init
|
||||
|
||||
// On real mplus:
|
||||
// For some reason the first read seems to have garbage data
|
||||
// is_mp_data and extension_connected are set, but the data is junk
|
||||
// it does seem to have some sort of pattern though, byte 5 is always 2
|
||||
// something like: d5, b0, 4e, 6e, fc, 2
|
||||
// When a passthrough mode is set:
|
||||
// the second read is valid mplus data, which then triggers a read from the extension
|
||||
// the third read is finally extension data
|
||||
// If an extension is not attached the data is always mplus data
|
||||
// even when passthrough is enabled
|
||||
|
||||
switch (GetPassthroughMode())
|
||||
{
|
||||
case PassthroughMode::PASSTHROUGH_DISABLED:
|
||||
{
|
||||
mplus_data.is_mp_data = true;
|
||||
break;
|
||||
}
|
||||
case PassthroughMode::PASSTHROUGH_NUNCHUK:
|
||||
{
|
||||
// If we sent mplus data last time now we will try to send ext data.
|
||||
if (mplus_data.is_mp_data)
|
||||
{
|
||||
// The real mplus seems to only ever read 6 bytes from the extension
|
||||
// bytes after 6 seem to be zero filled
|
||||
// The real hardware uses these 6 bytes for the next frame,
|
||||
// but we aren't going to do that
|
||||
if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data))
|
||||
{
|
||||
// Passthrough data modifications via wiibrew.org
|
||||
// Data passing through drops the least significant bit of the three accelerometer values
|
||||
// Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it
|
||||
SetBit(data[5], 6, Common::ExtractBit(data[5], 7));
|
||||
// Bit 0 of byte 4 is moved to bit 7 of byte 5
|
||||
SetBit(data[5], 7, Common::ExtractBit(data[4], 0));
|
||||
// Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it
|
||||
SetBit(data[5], 4, Common::ExtractBit(data[5], 3));
|
||||
// Bit 1 of byte 5 is moved to bit 3 of byte 5
|
||||
SetBit(data[5], 3, Common::ExtractBit(data[5], 1));
|
||||
// Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it
|
||||
SetBit(data[5], 2, Common::ExtractBit(data[5], 0));
|
||||
|
||||
mplus_data.is_mp_data = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PassthroughMode::PASSTHROUGH_CLASSIC:
|
||||
{
|
||||
// If we sent mplus data last time now we will try to send ext data.
|
||||
if (mplus_data.is_mp_data)
|
||||
{
|
||||
if (6 == i2c_bus.BusRead(ACTIVE_DEVICE_ADDR, 0x00, 6, data))
|
||||
{
|
||||
// Passthrough data modifications via wiibrew.org
|
||||
// Data passing through drops the least significant bit of the axes of the left (or only) joystick
|
||||
// Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag]
|
||||
// Bits 0 and 1 of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before
|
||||
SetBit(data[0], 0, Common::ExtractBit(data[5], 0));
|
||||
SetBit(data[1], 0, Common::ExtractBit(data[5], 1));
|
||||
|
||||
mplus_data.is_mp_data = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
PanicAlert("MotionPlus unknown passthrough-mode %d", GetPassthroughMode());
|
||||
break;
|
||||
}
|
||||
|
||||
// If the above logic determined this should be mp data, update it here
|
||||
if (mplus_data.is_mp_data)
|
||||
{
|
||||
// Wiibrew: "While the Wiimote is still, the values will be about 0x1F7F (8,063)"
|
||||
u16 yaw_value = 0x1F7F;
|
||||
u16 roll_value = 0x1F7F;
|
||||
u16 pitch_value = 0x1F7F;
|
||||
|
||||
mplus_data.yaw_slow = 1;
|
||||
mplus_data.roll_slow = 1;
|
||||
mplus_data.pitch_slow = 1;
|
||||
|
||||
// Bits 0-7
|
||||
mplus_data.yaw1 = yaw_value & 0xff;
|
||||
mplus_data.roll1 = roll_value & 0xff;
|
||||
mplus_data.pitch1 = pitch_value & 0xff;
|
||||
|
||||
// Bits 8-13
|
||||
mplus_data.yaw1 = yaw_value >> 8;
|
||||
mplus_data.roll1 = roll_value >> 8;
|
||||
mplus_data.pitch1 = pitch_value >> 8;
|
||||
}
|
||||
|
||||
mplus_data.extension_connected = extension_port.IsDeviceConnected();
|
||||
mplus_data.zero = 0;
|
||||
}
|
||||
|
||||
} // namespace WiimoteEmu
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
|
||||
@ -266,9 +266,10 @@ public:
|
||||
|
||||
void Reset() { m_slaves.clear(); }
|
||||
|
||||
// TODO: change int to u16 or something
|
||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
||||
{
|
||||
INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
|
||||
// INFO_LOG(WIIMOTE, "i2c bus read: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
|
||||
for (auto& slave : m_slaves)
|
||||
{
|
||||
auto const bytes_read = slave->BusRead(slave_addr, addr, count, data_out);
|
||||
@ -281,9 +282,13 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: change int to u16 or something
|
||||
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
||||
{
|
||||
INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
|
||||
// TODO: write in blocks of 6 to simulate the real bus
|
||||
// this might trigger activation writes more accurately
|
||||
|
||||
// INFO_LOG(WIIMOTE, "i2c bus write: 0x%02x @ 0x%02x (%d)", slave_addr, addr, count);
|
||||
for (auto& slave : m_slaves)
|
||||
{
|
||||
auto const bytes_written = slave->BusWrite(slave_addr, addr, count, data_in);
|
||||
@ -386,7 +391,6 @@ protected:
|
||||
void GetButtonData(u8* data);
|
||||
void GetAccelData(u8* data);
|
||||
void UpdateIRData(bool use_accel);
|
||||
void UpdateExtData();
|
||||
|
||||
private:
|
||||
I2CBus m_i2c_bus;
|
||||
@ -438,18 +442,35 @@ private:
|
||||
|
||||
static const u8 DEVICE_ADDR = 0x52;
|
||||
|
||||
void Update();
|
||||
|
||||
private:
|
||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override
|
||||
{
|
||||
if (DEVICE_ADDR != slave_addr)
|
||||
return 0;
|
||||
|
||||
if (0x00 == addr)
|
||||
{
|
||||
// Here we should sample user input and update controller data
|
||||
// Moved into Update() function for TAS determinism
|
||||
// TAS code fails to sync data reads and such..
|
||||
// extension->GetState(reg_data.controller_data);
|
||||
}
|
||||
|
||||
auto const result = RawRead(®_data, addr, count, data_out);
|
||||
|
||||
// Encrypt data read from extension register
|
||||
// Check if encrypted reads is on
|
||||
if (0xaa == reg_data.encryption)
|
||||
{
|
||||
INFO_LOG(WIIMOTE, "Encrypted read.");
|
||||
WiimoteEncrypt(&ext_key, data_out, addr, (u8)count);
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(WIIMOTE, "Unencrypted read.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -538,19 +559,30 @@ private:
|
||||
// The port on the end of the motion plus:
|
||||
ExtensionPort extension_port{i2c_bus};
|
||||
|
||||
void Update();
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct MotionPlusRegister
|
||||
{
|
||||
u8 controller_data[0x10];
|
||||
u8 unknown[0x10];
|
||||
u8 controller_data[21];
|
||||
u8 unknown[11];
|
||||
|
||||
// address 0x20
|
||||
u8 calibration_data[0x20];
|
||||
u8 unknown2[0xb0];
|
||||
|
||||
// address 0xF0
|
||||
// TODO: bad name
|
||||
u8 activated;
|
||||
u8 initialized;
|
||||
|
||||
u8 unknown3[9];
|
||||
u8 unknown3[6];
|
||||
|
||||
// address 0xf7
|
||||
// Wii Sports Resort reads regularly and claims mplus is disconnected if not to its liking
|
||||
// Value starts at 0x00 and goes up after activation (not initialization)
|
||||
// Immediately returns 0x02, even still after 15 and 30 seconds
|
||||
u8 initialization_status;
|
||||
|
||||
u8 unknown4[2];
|
||||
|
||||
// address 0xFA
|
||||
u8 ext_identifier[6];
|
||||
@ -559,19 +591,35 @@ private:
|
||||
|
||||
static_assert(0x100 == sizeof(reg_data));
|
||||
|
||||
private:
|
||||
static const u8 INACTIVE_DEVICE_ADDR = 0x53;
|
||||
static const u8 ACTIVE_DEVICE_ADDR = 0x52;
|
||||
|
||||
enum class PassthroughMode : u8
|
||||
{
|
||||
PASSTHROUGH_DISABLED = 0x04,
|
||||
PASSTHROUGH_NUNCHUK = 0x05,
|
||||
PASSTHROUGH_CLASSIC = 0x07,
|
||||
};
|
||||
|
||||
// TODO: savestate
|
||||
u8 times_updated_since_activation = 0;
|
||||
|
||||
bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; }
|
||||
|
||||
u8 GetPassthroughMode() const { return reg_data.ext_identifier[4]; }
|
||||
PassthroughMode GetPassthroughMode() const
|
||||
{
|
||||
return static_cast<PassthroughMode>(reg_data.ext_identifier[4]);
|
||||
}
|
||||
|
||||
// TODO: when activated it seems the motion plus continuously reads from the ext port even when not in passthrough mode
|
||||
// 16 bytes it seems
|
||||
|
||||
// It also sends the 0x55 to 0xf0 activation
|
||||
// TODO: when activated it seems the motion plus reactivates the extension
|
||||
// It sends 0x55 to 0xf0
|
||||
// It also writes 0x00 to slave:0x52 addr:0xfa for some reason
|
||||
// And starts a write to 0xfa but never writes bytes..
|
||||
// It tries to read data at 0x00 for 3 times (failing)
|
||||
// then it reads the 16 bytes of calibration at 0x20 and stops
|
||||
|
||||
// TODO: if an extension is attached after activation, it also does this.
|
||||
|
||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override
|
||||
{
|
||||
@ -605,15 +653,17 @@ private:
|
||||
auto const result = RawWrite(®_data, addr, count, data_in);
|
||||
return result;
|
||||
|
||||
// TODO: Should any write (of any value) trigger activation?
|
||||
// It seems a write of any value triggers deactivation.
|
||||
if (0xf0 == addr)
|
||||
{
|
||||
// Deactivate motion plus:
|
||||
reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1;
|
||||
times_updated_since_activation = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No i2c passthrough when activated.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -623,11 +673,18 @@ private:
|
||||
{
|
||||
auto const result = RawWrite(®_data, addr, count, data_in);
|
||||
|
||||
// TODO: Should any write (of any value) trigger activation?
|
||||
// It seems a write of any value triggers activation.
|
||||
if (0xfe == addr)
|
||||
{
|
||||
INFO_LOG(WIIMOTE, "Motion Plus has been activated with value: %d", data_in[0]);
|
||||
|
||||
// Activate motion plus:
|
||||
reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1;
|
||||
times_updated_since_activation = 0x2;
|
||||
|
||||
// Test some hax
|
||||
std::array<u8, 1> data = {0x55};
|
||||
i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
Loading…
x
Reference in New Issue
Block a user