WiimoteEmu: Cleanups and work towards making motion plus functional.

This commit is contained in:
Jordan Woyak 2018-11-25 11:08:42 -06:00
parent a25e8cb516
commit 42b9392784
4 changed files with 244 additions and 34 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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(&reg_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(&reg_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(&reg_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;