Merge pull request #1372 from ggrtk/analog-controller-refactor

AnalogController: Refactor data transfer implementation
This commit is contained in:
Connor McLaughlin 2021-01-05 00:45:37 +10:00 committed by GitHub
commit 35fdae0d95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 311 additions and 332 deletions

View File

@ -23,10 +23,12 @@ ControllerType AnalogController::GetType() const
void AnalogController::Reset() void AnalogController::Reset()
{ {
m_state = State::Idle; m_command = Command::Idle;
m_command_step = 0;
m_rx_buffer.fill(0x00);
m_tx_buffer.fill(0x00);
m_analog_mode = false; m_analog_mode = false;
m_configuration_mode = false; m_configuration_mode = false;
m_command_param = 0;
m_motor_state.fill(0); m_motor_state.fill(0);
ResetRumbleConfig(); ResetRumbleConfig();
@ -63,7 +65,7 @@ bool AnalogController::DoState(StateWrapper& sw, bool apply_input_state)
if (apply_input_state) if (apply_input_state)
m_button_state = button_state; m_button_state = button_state;
sw.Do(&m_state); sw.Do(&m_command);
sw.DoEx(&m_rumble_config, 45, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}); sw.DoEx(&m_rumble_config, 45, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
sw.DoEx(&m_rumble_config_large_motor_index, 45, -1); sw.DoEx(&m_rumble_config_large_motor_index, 45, -1);
@ -195,19 +197,8 @@ void AnalogController::ResetTransferState()
m_analog_toggle_queued = false; m_analog_toggle_queued = false;
} }
m_state = State::Idle; m_command = Command::Idle;
} m_command_step = 0;
u16 AnalogController::GetID() const
{
static constexpr u16 DIGITAL_MODE_ID = 0x5A41;
static constexpr u16 ANALOG_MODE_ID = 0x5A73;
static constexpr u16 CONFIG_MODE_ID = 0x5AF3;
if (m_configuration_mode)
return CONFIG_MODE_ID;
return m_analog_mode ? ANALOG_MODE_ID : DIGITAL_MODE_ID;
} }
void AnalogController::SetAnalogMode(bool enabled) void AnalogController::SetAnalogMode(bool enabled)
@ -271,316 +262,329 @@ void AnalogController::SetMotorStateForConfigIndex(int index, u8 value)
SetMotorState(LargeMotor, value); SetMotorState(LargeMotor, value);
} }
u8 AnalogController::GetResponseNumHalfwords() const
{
if (m_configuration_mode || m_analog_mode)
return 0x3;
return (0x1 + m_digital_mode_extra_halfwords);
}
u8 AnalogController::GetModeID() const
{
if (m_configuration_mode)
return 0xF;
if (m_analog_mode)
return 0x7;
return 0x4;
}
u8 AnalogController::GetIDByte() const
{
return Truncate8((GetModeID() << 4) | GetResponseNumHalfwords());
}
bool AnalogController::Transfer(const u8 data_in, u8* data_out) bool AnalogController::Transfer(const u8 data_in, u8* data_out)
{ {
bool ack; bool ack;
#ifdef _DEBUG m_rx_buffer[m_command_step] = data_in;
u8 old_state = static_cast<u8>(m_state);
#endif
switch (m_state) switch (m_command)
{ {
#define FIXED_REPLY_STATE(state, reply, ack_value, next_state) \ case Command::Idle:
case state: \
{ \
*data_out = reply; \
m_state = next_state; \
ack = ack_value; \
} \
break;
#define ID_STATE_MSB(state, next_state) \
case state: \
{ \
*data_out = Truncate8(GetID() >> 8); \
m_state = next_state; \
ack = true; \
} \
break;
#define REPLY_RUMBLE_CONFIG(state, index, ack_value, next_state) \
case state: \
{ \
DebugAssert(index < m_rumble_config.size()); \
*data_out = m_rumble_config[index]; \
m_rumble_config[index] = data_in; \
\
if (data_in == 0x00) \
m_rumble_config_small_motor_index = index; \
else if (data_in == 0x01) \
m_rumble_config_large_motor_index = index; \
\
m_state = next_state; \
ack = ack_value; \
} \
break;
case State::Idle:
{ {
// ack when sent 0x01, send ID for 0x42 // ack when sent 0x01, send ID for 0x42
if (data_in == 0x42) if (data_in == 0x42)
{ {
*data_out = Truncate8(GetID()); Assert(m_command_step == 0);
m_state = State::GetStateIDMSB; m_response_length = (GetResponseNumHalfwords() + 1) * 2;
ack = true; m_command = Command::ReadPad;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
} }
else if (data_in == 0x43) else if (data_in == 0x43)
{ {
*data_out = Truncate8(GetID()); Assert(m_command_step == 0);
m_state = State::ConfigModeIDMSB; m_response_length = (GetResponseNumHalfwords() + 1) * 2;
ack = true; m_command = Command::ConfigModeSetMode;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
} }
else if (m_configuration_mode && data_in == 0x44) else if (m_configuration_mode && data_in == 0x44)
{ {
*data_out = Truncate8(GetID()); Assert(m_command_step == 0);
m_state = State::SetAnalogModeIDMSB; m_response_length = (GetResponseNumHalfwords() + 1) * 2;
ack = true; m_command = Command::SetAnalogMode;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
} }
else if (m_configuration_mode && data_in == 0x45) else if (m_configuration_mode && data_in == 0x45)
{ {
*data_out = Truncate8(GetID()); Assert(m_command_step == 0);
m_state = State::GetAnalogModeIDMSB; m_response_length = (GetResponseNumHalfwords() + 1) * 2;
ack = true; m_command = Command::GetAnalogMode;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x01, 0x02, BoolToUInt8(m_analog_mode), 0x02, 0x01, 0x00};
} }
else if (m_configuration_mode && data_in == 0x46) else if (m_configuration_mode && data_in == 0x46)
{ {
*data_out = Truncate8(GetID()); Assert(m_command_step == 0);
m_state = State::Command46IDMSB; m_response_length = (GetResponseNumHalfwords() + 1) * 2;
ack = true; m_command = Command::Command46;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A};
} }
else if (m_configuration_mode && data_in == 0x47) else if (m_configuration_mode && data_in == 0x47)
{ {
*data_out = Truncate8(GetID()); Assert(m_command_step == 0);
m_state = State::Command47IDMSB; m_response_length = (GetResponseNumHalfwords() + 1) * 2;
ack = true; m_command = Command::Command47;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x00, 0x00, 0x02, 0x00, 0x01, 0x00};
} }
else if (m_configuration_mode && data_in == 0x4C) else if (m_configuration_mode && data_in == 0x4C)
{ {
*data_out = Truncate8(GetID()); Assert(m_command_step == 0);
m_state = State::Command4CIDMSB; m_response_length = (GetResponseNumHalfwords() + 1) * 2;
ack = true; m_command = Command::Command4C;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
} }
else if (m_configuration_mode && data_in == 0x4D) else if (m_configuration_mode && data_in == 0x4D)
{ {
Assert(m_command_step == 0);
m_response_length = (GetResponseNumHalfwords() + 1) * 2;
m_command = Command::GetSetRumble;
m_tx_buffer = {GetIDByte(), GetStatusByte(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
m_rumble_unlocked = true; m_rumble_unlocked = true;
*data_out = Truncate8(GetID());
m_state = State::UnlockRumbleIDMSB;
m_rumble_config_large_motor_index = -1; m_rumble_config_large_motor_index = -1;
m_rumble_config_small_motor_index = -1; m_rumble_config_small_motor_index = -1;
ack = true;
} }
else else
{ {
Log_DebugPrintf("data_in = 0x%02X", data_in);
*data_out = 0xFF; *data_out = 0xFF;
ack = (data_in == 0x01); ack = (data_in == 0x01);
if (ack)
Log_DevPrintf("ACK controller access");
else
Log_DevPrintf("Unknown data_in = 0x%02X", data_in);
return ack;
} }
} }
break; break;
ID_STATE_MSB(State::GetStateIDMSB, State::GetStateButtonsLSB); case Command::ReadPad:
case State::GetStateButtonsLSB:
{ {
const int rumble_index = m_command_step - 2;
switch (m_command_step)
{
case 2:
{
m_tx_buffer[m_command_step] = Truncate8(m_button_state) & GetExtraButtonMaskLSB();
if (m_rumble_unlocked) if (m_rumble_unlocked)
SetMotorStateForConfigIndex(0, data_in); SetMotorStateForConfigIndex(rumble_index, data_in);
else if (data_in >= 0x40 && data_in <= 0x7F) else if (data_in >= 0x40 && data_in <= 0x7F)
m_legacy_rumble_unlocked = true; m_legacy_rumble_unlocked = true;
else else
SetMotorState(SmallMotor, 0); SetMotorState(SmallMotor, 0);
*data_out = Truncate8(m_button_state) & GetExtraButtonMaskLSB();
m_state = State::GetStateButtonsMSB;
ack = true;
} }
break; break;
case State::GetStateButtonsMSB: case 3:
{ {
m_tx_buffer[m_command_step] = Truncate8(m_button_state >> 8);
if (m_rumble_unlocked) if (m_rumble_unlocked)
{ {
SetMotorStateForConfigIndex(1, data_in); SetMotorStateForConfigIndex(rumble_index, data_in);
} }
else if (m_legacy_rumble_unlocked) else if (m_legacy_rumble_unlocked)
{ {
SetMotorState(SmallMotor, ((data_in & 0x01) != 0) ? 255 : 0); SetMotorState(SmallMotor, ((data_in & 0x01) != 0) ? 255 : 0);
m_legacy_rumble_unlocked = false; m_legacy_rumble_unlocked = false;
} }
*data_out = Truncate8(m_button_state >> 8);
m_state = (m_analog_mode || m_configuration_mode) ? State::GetStateRightAxisX : State::Idle;
ack = m_analog_mode || m_configuration_mode;
} }
break; break;
case State::GetStateRightAxisX: case 4:
{ {
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::RightX)];
if (m_rumble_unlocked) if (m_rumble_unlocked)
SetMotorStateForConfigIndex(2, data_in); SetMotorStateForConfigIndex(rumble_index, data_in);
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::RightX)]);
m_state = State::GetStateRightAxisY;
ack = true;
} }
break; break;
case State::GetStateRightAxisY: case 5:
{ {
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::RightY)];
if (m_rumble_unlocked) if (m_rumble_unlocked)
SetMotorStateForConfigIndex(3, data_in); SetMotorStateForConfigIndex(rumble_index, data_in);
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::RightY)]);
m_state = State::GetStateLeftAxisX;
ack = true;
} }
break; break;
case State::GetStateLeftAxisX: case 6:
{ {
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::LeftX)];
if (m_rumble_unlocked) if (m_rumble_unlocked)
SetMotorStateForConfigIndex(4, data_in); SetMotorStateForConfigIndex(rumble_index, data_in);
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::LeftX)]);
m_state = State::GetStateLeftAxisY;
ack = true;
} }
break; break;
case State::GetStateLeftAxisY: case 7:
{ {
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::LeftY)];
if (m_rumble_unlocked) if (m_rumble_unlocked)
SetMotorStateForConfigIndex(5, data_in); SetMotorStateForConfigIndex(rumble_index, data_in);
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::LeftY)]);
m_state = State::Idle;
ack = false;
} }
break; break;
ID_STATE_MSB(State::ConfigModeIDMSB, State::ConfigModeSetMode); default:
case State::ConfigModeSetMode:
{ {
// If 0x43 "enter/leave config mode" is called from within config mode, return all zeros }
Log_DebugPrintf("0x%02x(%s) config mode", data_in, data_in == 1 ? "enter" : "leave"); break;
bool prev_configuration_mode = m_configuration_mode; }
m_configuration_mode = (data_in == 1);
*data_out = prev_configuration_mode ? 0x00 : Truncate8(m_button_state);
m_state = prev_configuration_mode ? State::Pad5Bytes : State::GetStateButtonsMSB;
ack = true;
} }
break; break;
ID_STATE_MSB(State::SetAnalogModeIDMSB, State::SetAnalogModeVal); case Command::ConfigModeSetMode:
{
if (!m_configuration_mode)
{
switch (m_command_step)
{
case 2:
{
m_tx_buffer[m_command_step] = Truncate8(m_button_state) & GetExtraButtonMaskLSB();
}
break;
case State::SetAnalogModeVal: case 3:
{
m_tx_buffer[m_command_step] = Truncate8(m_button_state >> 8);
}
break;
case 4:
{
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::RightX)];
}
break;
case 5:
{
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::RightY)];
}
break;
case 6:
{
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::LeftX)];
}
break;
case 7:
{
if (m_configuration_mode || m_analog_mode)
m_tx_buffer[m_command_step] = m_axis_state[static_cast<u8>(Axis::LeftY)];
}
break;
default:
{
}
break;
}
}
if (m_command_step == (static_cast<s32>(m_response_length) - 1))
{
m_configuration_mode = (m_rx_buffer[2] == 1);
Log_DevPrintf("0x%02x(%s) config mode", m_rx_buffer[2], m_configuration_mode ? "enter" : "leave");
}
}
break;
case Command::SetAnalogMode:
{
if (m_command_step == 2)
{ {
Log_DevPrintf("analog mode val 0x%02x", data_in); Log_DevPrintf("analog mode val 0x%02x", data_in);
if (data_in == 0x00 || data_in == 0x01) if (data_in == 0x00 || data_in == 0x01)
SetAnalogMode((data_in == 0x01)); SetAnalogMode((data_in == 0x01));
*data_out = 0x00;
m_state = State::SetAnalogModeSel;
ack = true;
} }
break; else if (m_command_step == 3)
case State::SetAnalogModeSel:
{ {
Log_DevPrintf("analog mode lock 0x%02x", data_in); Log_DevPrintf("analog mode lock 0x%02x", data_in);
if (data_in == 0x02 || data_in == 0x03) if (data_in == 0x02 || data_in == 0x03)
m_analog_locked = (data_in == 0x03); m_analog_locked = (data_in == 0x03);
}
*data_out = 0x00;
m_state = State::Pad4Bytes;
ack = true;
} }
break; break;
ID_STATE_MSB(State::GetAnalogModeIDMSB, State::GetAnalogMode1); case Command::GetAnalogMode:
FIXED_REPLY_STATE(State::GetAnalogMode1, 0x01, true, State::GetAnalogMode2);
FIXED_REPLY_STATE(State::GetAnalogMode2, 0x02, true, State::GetAnalogMode3);
FIXED_REPLY_STATE(State::GetAnalogMode3, BoolToUInt8(m_analog_mode), true, State::GetAnalogMode4);
FIXED_REPLY_STATE(State::GetAnalogMode4, 0x02, true, State::GetAnalogMode5);
FIXED_REPLY_STATE(State::GetAnalogMode5, 0x01, true, State::GetAnalogMode6);
FIXED_REPLY_STATE(State::GetAnalogMode6, 0x00, false, State::Idle);
ID_STATE_MSB(State::Command46IDMSB, State::Command461);
case State::Command461:
{ {
Log_DebugPrintf("command 46 param 0x%02X", data_in); // Intentionally empty, analog mode byte is set in reply buffer when command is first received
m_command_param = data_in;
*data_out = 0x00;
m_state = State::Command462;
ack = true;
} }
break; break;
FIXED_REPLY_STATE(State::Command462, 0x00, true, State::Command463); case Command::Command46:
FIXED_REPLY_STATE(State::Command463, 0x01, true, State::Command464);
FIXED_REPLY_STATE(State::Command464, ((m_command_param == 1) ? 1 : 2), true, State::Command465);
FIXED_REPLY_STATE(State::Command465, ((m_command_param == 1) ? 1 : 0), true, State::Command466);
FIXED_REPLY_STATE(State::Command466, ((m_command_param == 1) ? 0x14 : 0x0A), false, State::Idle);
ID_STATE_MSB(State::Command47IDMSB, State::Command471);
FIXED_REPLY_STATE(State::Command471, 0x00, true, State::Command472);
FIXED_REPLY_STATE(State::Command472, 0x00, true, State::Command473);
FIXED_REPLY_STATE(State::Command473, 0x02, true, State::Command474);
FIXED_REPLY_STATE(State::Command474, 0x00, true, State::Command475);
FIXED_REPLY_STATE(State::Command475, 0x01, true, State::Command476);
FIXED_REPLY_STATE(State::Command476, 0x00, false, State::Idle);
ID_STATE_MSB(State::Command4CIDMSB, State::Command4CMode);
case State::Command4CMode:
{ {
m_command_param = data_in; if (m_command_step == 2 && data_in == 0x01)
*data_out = 0x00; {
m_state = State::Command4C1; m_tx_buffer[5] = 0x01;
ack = true; m_tx_buffer[6] = 0x01;
m_tx_buffer[7] = 0x14;
}
} }
break; break;
FIXED_REPLY_STATE(State::Command4C1, 0x00, true, State::Command4C2); case Command::Command47:
FIXED_REPLY_STATE(State::Command4C2, 0x00, true, State::Command4C3);
case State::Command4C3:
{ {
// Ape Escape sends both 0x00 and 0x01 sequences on startup and checks for correct response // Intentionally empty, use fixed reply buffer
if (m_command_param == 0x00)
*data_out = 0x04;
else if (m_command_param == 0x01)
*data_out = 0x07;
else
*data_out = 0x00;
m_state = State::Command4C4;
ack = true;
} }
break; break;
FIXED_REPLY_STATE(State::Command4C4, 0x00, true, State::Command4C5); case Command::Command4C:
FIXED_REPLY_STATE(State::Command4C5, 0x00, false, State::Idle);
ID_STATE_MSB(State::UnlockRumbleIDMSB, State::GetSetRumble1);
REPLY_RUMBLE_CONFIG(State::GetSetRumble1, 0, true, State::GetSetRumble2);
REPLY_RUMBLE_CONFIG(State::GetSetRumble2, 1, true, State::GetSetRumble3);
REPLY_RUMBLE_CONFIG(State::GetSetRumble3, 2, true, State::GetSetRumble4);
REPLY_RUMBLE_CONFIG(State::GetSetRumble4, 3, true, State::GetSetRumble5);
REPLY_RUMBLE_CONFIG(State::GetSetRumble5, 4, true, State::GetSetRumble6);
case State::GetSetRumble6:
{ {
DebugAssert(5 < m_rumble_config.size()); if (m_command_step == 2)
*data_out = m_rumble_config[5]; {
m_rumble_config[5] = data_in; if (data_in == 0x00)
m_tx_buffer[5] = 0x04;
else if (data_in == 0x01)
m_tx_buffer[5] = 0x07;
}
}
break;
case Command::GetSetRumble:
{
int rumble_index = m_command_step - 2;
if (rumble_index >= 0)
{
m_tx_buffer[m_command_step] = m_rumble_config[rumble_index];
m_rumble_config[rumble_index] = data_in;
if (data_in == 0x00) if (data_in == 0x00)
m_rumble_config_small_motor_index = 5; m_rumble_config_small_motor_index = rumble_index;
else if (data_in == 0x01) else if (data_in == 0x01)
m_rumble_config_large_motor_index = 5; m_rumble_config_large_motor_index = rumble_index;
}
if (m_command_step == 7)
{
if (m_rumble_config_large_motor_index == -1) if (m_rumble_config_large_motor_index == -1)
SetMotorState(LargeMotor, 0); SetMotorState(LargeMotor, 0);
@ -589,32 +593,35 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
if (m_rumble_config_large_motor_index == -1 && m_rumble_config_small_motor_index == -1) if (m_rumble_config_large_motor_index == -1 && m_rumble_config_small_motor_index == -1)
m_rumble_unlocked = false; m_rumble_unlocked = false;
}
// Unknown if motor config array forces 0xFF values if configured byte is not 0x00 or 0x01 // Unknown if motor config array forces 0xFF values if configured byte is not 0x00 or 0x01
// Unknown under what circumstances rumble is locked and legacy rumble is re-enabled, if even possible // Unknown under what circumstances rumble is locked and legacy rumble is re-enabled, if even possible
// e.g. need all 0xFFs? // Current assumption is that rumble is locked and legacy rumble is re-enabled when new config is all 0xFF
m_state = State::Idle;
ack = false;
} }
break; break;
FIXED_REPLY_STATE(State::Pad6Bytes, 0x00, true, State::Pad5Bytes); DefaultCaseIsUnreachable();
FIXED_REPLY_STATE(State::Pad5Bytes, 0x00, true, State::Pad4Bytes); }
FIXED_REPLY_STATE(State::Pad4Bytes, 0x00, true, State::Pad3Bytes);
FIXED_REPLY_STATE(State::Pad3Bytes, 0x00, true, State::Pad2Bytes);
FIXED_REPLY_STATE(State::Pad2Bytes, 0x00, true, State::Pad1Byte);
FIXED_REPLY_STATE(State::Pad1Byte, 0x00, false, State::Idle);
default: *data_out = m_tx_buffer[m_command_step];
m_command_step = (m_command_step + 1) % m_response_length;
ack = (m_command_step == 0) ? false : true;
if (m_command_step == 0)
{ {
UnreachableCode(); m_command = Command::Idle;
return false;
} Log_DevPrintf("Rx: %02x %02x %02x %02x %02x %02x %02x %02x", m_rx_buffer[0], m_rx_buffer[1], m_rx_buffer[2],
m_rx_buffer[3], m_rx_buffer[4], m_rx_buffer[5], m_rx_buffer[6], m_rx_buffer[7]);
Log_DevPrintf("Tx: %02x %02x %02x %02x %02x %02x %02x %02x", m_tx_buffer[0], m_tx_buffer[1], m_tx_buffer[2],
m_tx_buffer[3], m_tx_buffer[4], m_tx_buffer[5], m_tx_buffer[6], m_tx_buffer[7]);
m_rx_buffer.fill(0x00);
m_tx_buffer.fill(0x00);
} }
Log_DebugPrintf("Transfer, old_state=%u, new_state=%u, data_in=0x%02X, data_out=0x%02X, ack=%s",
static_cast<u32>(old_state), static_cast<u32>(m_state), data_in, *data_out, ack ? "true" : "false");
return ack; return ack;
} }

View File

@ -77,65 +77,37 @@ public:
private: private:
using MotorState = std::array<u8, NUM_MOTORS>; using MotorState = std::array<u8, NUM_MOTORS>;
enum class State : u8 enum class Command : u8
{ {
Idle, Idle,
GetStateIDMSB, ReadPad, // 0x42
GetStateButtonsLSB, ConfigModeSetMode, // 0x43
GetStateButtonsMSB, SetAnalogMode, // 0x44
GetStateRightAxisX, GetAnalogMode, // 0x45
GetStateRightAxisY, Command46, // 0x46
GetStateLeftAxisX, Command47, // 0x47
GetStateLeftAxisY, Command4C, // 0x4C
ConfigModeIDMSB, GetSetRumble // 0x4D
ConfigModeSetMode,
SetAnalogModeIDMSB,
SetAnalogModeVal,
SetAnalogModeSel,
GetAnalogModeIDMSB,
GetAnalogMode1,
GetAnalogMode2,
GetAnalogMode3,
GetAnalogMode4,
GetAnalogMode5,
GetAnalogMode6,
UnlockRumbleIDMSB,
GetSetRumble1,
GetSetRumble2,
GetSetRumble3,
GetSetRumble4,
GetSetRumble5,
GetSetRumble6,
Command46IDMSB,
Command461,
Command462,
Command463,
Command464,
Command465,
Command466,
Command47IDMSB,
Command471,
Command472,
Command473,
Command474,
Command475,
Command476,
Command4CIDMSB,
Command4CMode,
Command4C1,
Command4C2,
Command4C3,
Command4C4,
Command4C5,
Pad6Bytes,
Pad5Bytes,
Pad4Bytes,
Pad3Bytes,
Pad2Bytes,
Pad1Byte,
}; };
u16 GetID() const; Command m_command = Command::Idle;
int m_command_step = 0;
// Transmit and receive buffers, not including the first Hi-Z/ack response byte
static constexpr u32 MAX_RESPONSE_LENGTH = 8;
std::array<u8, MAX_RESPONSE_LENGTH> m_rx_buffer;
std::array<u8, MAX_RESPONSE_LENGTH> m_tx_buffer;
u32 m_response_length = 0;
// Get number of response halfwords (excluding the initial controller info halfword)
u8 GetResponseNumHalfwords() const;
u8 GetModeID() const;
u8 GetIDByte() const;
// TODO: Return 0x00 on manual toggles
constexpr u8 GetStatusByte() const { return 0x5A; };
void SetAnalogMode(bool enabled); void SetAnalogMode(bool enabled);
void SetMotorState(u8 motor, u8 value); void SetMotorState(u8 motor, u8 value);
u8 GetExtraButtonMaskLSB() const; u8 GetExtraButtonMaskLSB() const;
@ -154,7 +126,6 @@ private:
bool m_rumble_unlocked = false; bool m_rumble_unlocked = false;
bool m_legacy_rumble_unlocked = false; bool m_legacy_rumble_unlocked = false;
bool m_configuration_mode = false; bool m_configuration_mode = false;
u8 m_command_param = 0;
std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{}; std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{};
@ -171,12 +142,13 @@ private:
bool m_analog_toggle_queued = false; bool m_analog_toggle_queued = false;
// TODO: Set this with command 0x4D and increase response length in digital mode accordingly // TODO: Set this with command 0x4D and increase response length in digital mode accordingly
u8 m_digital_mode_additional_bytes = 0; u8 m_digital_mode_extra_halfwords = 0;
// buttons are active low // buttons are active low
u16 m_button_state = UINT16_C(0xFFFF); u16 m_button_state = UINT16_C(0xFFFF);
MotorState m_motor_state{}; MotorState m_motor_state{};
State m_state = State::Idle; // Member variable that is no longer used, but kept and serialized for compatibility with older save states
u8 m_command_param = 0;
}; };