WiimoteEmu: Process wiimote read data requests like they are on a real wiimote. It's not a queue. New requests are ignored and input is suppressed while processing a request. This simplifies the save state code greatly.

This commit is contained in:
Jordan Woyak 2018-11-23 18:01:56 -06:00
parent ec460da36d
commit 4dc0aa6f8e
4 changed files with 125 additions and 179 deletions

View File

@ -439,7 +439,7 @@ struct wm_read_data_reply
{
wm_buttons buttons;
u8 error : 4; // see WM_RDERR_*
u8 size : 4;
u8 size_minus_one : 4;
u16 address;
u8 data[16];
};

View File

@ -119,11 +119,12 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack)
case RT_WRITE_DATA: // 0x16
WriteData(reinterpret_cast<const wm_write_data*>(sr->data));
return; // sends its own ack
break;
case RT_READ_DATA: // 0x17
ReadData(reinterpret_cast<const wm_read_data*>(sr->data));
return; // sends its own ack
return; // sends its own ack/reply
break;
case RT_WRITE_SPEAKER_DATA: // 0x18
@ -167,7 +168,7 @@ void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack)
The first two bytes are the core buttons data,
00 00 means nothing is pressed.
The last byte is the success code 00. */
void Wiimote::SendAck(u8 report_id)
void Wiimote::SendAck(u8 report_id, u8 error_code)
{
u8 data[6];
@ -178,7 +179,7 @@ void Wiimote::SendAck(u8 report_id)
ack->buttons = m_status.buttons;
ack->reportID = report_id;
ack->errorID = 0;
ack->errorID = error_code;
Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
}
@ -235,6 +236,8 @@ void Wiimote::WriteData(const wm_write_data* const wd)
return;
}
u8 error_code = 0;
switch (wd->space)
{
case WS_EEPROM:
@ -267,13 +270,15 @@ void Wiimote::WriteData(const wm_write_data* const wd)
{
// Write to Control Register
// TODO: generate a writedata error reply, 7 == no such slave (no ack)
m_i2c_bus.BusWrite(wd->slave_address >> 1, address & 0xff, wd->size, wd->data);
// Top byte of address is ignored on the bus.
auto const bytes_written = m_i2c_bus.BusWrite(wd->slave_address >> 1, (u8)address, wd->size, wd->data);
if (bytes_written != wd->size)
{
// A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave)
error_code = 0x07;
}
return;
//else if (&m_reg_motion_plus == region_ptr)
// else if (&m_reg_motion_plus == region_ptr)
//{
// // activate/deactivate motion plus
// if (0x55 == m_reg_motion_plus.activated)
@ -291,50 +296,95 @@ void Wiimote::WriteData(const wm_write_data* const wd)
PanicAlert("WriteData: unimplemented parameters!");
break;
}
SendAck(RT_WRITE_DATA, error_code);
}
/* Read data from Wiimote and Extensions registers. */
void Wiimote::ReadData(const wm_read_data* const rd)
{
u16 address = Common::swap16(rd->address);
u16 size = Common::swap16(rd->size);
if (m_read_request.size)
{
// There is already an active read request.
// a real wiimote ignores the new one.
return;
}
//INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", rd->space, rd->slave_address, address, size);
// Save the request and process it on the next "Update()" calls
m_read_request.space = rd->space;
m_read_request.slave_address = rd->slave_address;
m_read_request.address = Common::swap16(rd->address);
// A zero size request is just ignored, like on the real wiimote.
m_read_request.size = Common::swap16(rd->size);
ReadRequest rr;
u8* const block = new u8[size];
INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", m_read_request.space,
m_read_request.slave_address, m_read_request.address, m_read_request.size);
switch (rd->space)
// Send up to one read-data-reply.
// If more data needs to be sent it will happen on the next "Update()"
ProcessReadDataRequest();
}
bool Wiimote::ProcessReadDataRequest()
{
// Limit the amt to 16 bytes
// AyuanX: the MTU is 640B though... what a waste!
u16 const bytes_to_read = std::min((u16)16, m_read_request.size);
if (0 == bytes_to_read)
{
// No active request:
return false;
}
u8 data[23] = {};
data[0] = 0xA1;
data[1] = RT_READ_DATA_REPLY;
wm_read_data_reply* const reply = reinterpret_cast<wm_read_data_reply*>(data + 2);
reply->buttons = m_status.buttons;
reply->address = Common::swap16(m_read_request.address);
switch (m_read_request.space)
{
case WS_EEPROM:
{
// Read from EEPROM
if (address + size >= WIIMOTE_EEPROM_FREE_SIZE)
if (m_read_request.address + m_read_request.size >= WIIMOTE_EEPROM_FREE_SIZE)
{
if (address + size > WIIMOTE_EEPROM_SIZE)
if (m_read_request.address + m_read_request.size > WIIMOTE_EEPROM_SIZE)
{
PanicAlert("ReadData: address + size out of bounds");
delete[] block;
return;
}
// generate a read error, even if the start of the block is readable a real wiimote just sends error code 8
size = 0;
}
// generate a read error, even if the start of the block is readable a real wiimote just sends
// error code 8
// read mii data from file
if (address >= 0x0FCA && address < 0x12C0)
// The real Wiimote generate an error for the first
// request to 0x1770 if we dont't replicate that the game will never
// 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.
reply->error = 0x08;
}
else
{
// TODO Only read the Mii block parts required
std::ifstream file;
File::OpenFStream(file, (File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin").c_str(),
std::ios::binary | std::ios::in);
file.read((char*)m_eeprom + 0x0FCA, 0x02f0);
file.close();
}
// Mii block handling:
// TODO: different filename for each wiimote?
if (m_read_request.address >= 0x0FCA && m_read_request.address < 0x12C0)
{
// TODO: Only read the Mii block parts required
std::ifstream file;
File::OpenFStream(file, (File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/mii.bin").c_str(),
std::ios::binary | std::ios::in);
file.read((char*)m_eeprom + 0x0FCA, 0x02f0);
file.close();
}
// read memory to be sent to Wii
memcpy(block, m_eeprom + address, size);
// read memory to be sent to Wii
std::copy_n(m_eeprom + m_read_request.address, bytes_to_read, reply->data);
reply->size_minus_one = bytes_to_read - 1;
}
}
break;
@ -343,83 +393,33 @@ void Wiimote::ReadData(const wm_read_data* const rd)
{
// Read from Control Register
m_i2c_bus.BusRead(rd->slave_address >> 1, address & 0xff, size, block);
// TODO: generate read errors, 7 == no such slave (no ack)
// Top byte of address is ignored on the bus, but it IS maintained in the read-reply.
auto const bytes_read = m_i2c_bus.BusRead(m_read_request.slave_address >> 1,
(u8)m_read_request.address, bytes_to_read, reply->data);
reply->size_minus_one = bytes_read - 1;
if (bytes_read != bytes_to_read)
{
// generate read error, 7 == no such slave (no ack)
reply->error = 0x07;
}
}
break;
default:
PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", rd->space);
PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", m_read_request.space);
break;
}
rr.address = address;
rr.size = size;
rr.position = 0;
rr.data = block;
// Modify the read request, zero size == complete
m_read_request.address += bytes_to_read;
m_read_request.size -= bytes_to_read;
// TODO: read requests suppress normal input reports
// TODO: if there is currently an active read request ignore new ones
// send up to 16 bytes
SendReadDataReply(rr);
// if there is more data to be sent, add it to the queue
if (rr.size)
m_read_requests.push(rr);
else
delete[] rr.data;
}
void Wiimote::SendReadDataReply(ReadRequest& request)
{
u8 data[23];
data[0] = 0xA1;
data[1] = RT_READ_DATA_REPLY;
wm_read_data_reply* const reply = reinterpret_cast<wm_read_data_reply*>(data + 2);
reply->buttons = m_status.buttons;
reply->address = Common::swap16(request.address);
// generate a read error
// Out of bounds. The real Wiimote generate an error for the first
// request to 0x1770 if we dont't replicate that the game will never
// 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.
if (0 == request.size)
{
reply->size = 0x0f;
reply->error = 0x08;
memset(reply->data, 0, sizeof(reply->data));
}
else
{
// Limit the amt to 16 bytes
// AyuanX: the MTU is 640B though... what a waste!
const int amt = std::min((u16)16, request.size);
// no error
reply->error = 0;
// 0x1 means two bytes, 0xf means 16 bytes
reply->size = amt - 1;
// Clear the mem first
memset(reply->data, 0, sizeof(reply->data));
// copy piece of mem
memcpy(reply->data, request.data + request.position, amt);
// update request struct
request.size -= amt;
request.position += amt;
request.address += amt;
}
// Send a piece
// Send the data
Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
return true;
}
void Wiimote::DoState(PointerWrap& p)
@ -446,47 +446,7 @@ void Wiimote::DoState(PointerWrap& p)
p.Do(m_camera_logic.reg_data);
p.Do(m_ext_logic.reg_data);
p.Do(m_speaker_logic.reg_data);
// Do 'm_read_requests' queue
{
u32 size = 0;
if (p.mode == PointerWrap::MODE_READ)
{
// clear
while (!m_read_requests.empty())
{
delete[] m_read_requests.front().data;
m_read_requests.pop();
}
p.Do(size);
while (size--)
{
ReadRequest tmp;
p.Do(tmp.address);
p.Do(tmp.position);
p.Do(tmp.size);
tmp.data = new u8[tmp.size];
p.DoArray(tmp.data, tmp.size);
m_read_requests.push(tmp);
}
}
else
{
std::queue<ReadRequest> tmp_queue(m_read_requests);
size = (u32)(m_read_requests.size());
p.Do(size);
while (!tmp_queue.empty())
{
ReadRequest tmp = tmp_queue.front();
p.Do(tmp.address);
p.Do(tmp.position);
p.Do(tmp.size);
p.DoArray(tmp.data, tmp.size);
tmp_queue.pop();
}
}
}
p.Do(m_read_request);
p.DoMarker("Wiimote");
if (p.GetMode() == PointerWrap::MODE_READ)
@ -504,4 +464,4 @@ void Wiimote::RealState()
g_wiimotes[m_index]->EnableDataReporting(m_reporting_mode);
}
}
}
} // namespace WiimoteEmu

View File

@ -353,12 +353,7 @@ void Wiimote::Reset()
m_swing_dynamic_data = {};
m_shake_dynamic_data = {};
// clear read request queue
while (!m_read_requests.empty())
{
delete[] m_read_requests.front().data;
m_read_requests.pop();
}
m_read_request.size = 0;
// Yamaha ADPCM state initialize
m_speaker_logic.adpcm_state.predictor = 0;
@ -552,21 +547,10 @@ bool Wiimote::Step()
UpdateButtonsStatus();
}
// check if there is a read data request
if (!m_read_requests.empty())
if (ProcessReadDataRequest())
{
ReadRequest& rr = m_read_requests.front();
// send up to 16 bytes to the Wii
SendReadDataReply(rr);
// if there is no more data, remove from queue
if (0 == rr.size)
{
delete[] rr.data;
m_read_requests.pop();
}
// don't send any other reports
// Read requests suppress normal input reports
// Don't send any other reports
return true;
}
@ -923,7 +907,8 @@ void Wiimote::Update()
feature_ptr += rptf.ext;
}
Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, m_ext_logic.ext_key);
Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension,
m_ext_logic.ext_key);
}
if (NetPlay::IsNetPlayRunning())
{
@ -933,7 +918,8 @@ void Wiimote::Update()
}
// TODO: need to fix usage of rptf probably
Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key);
Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension,
m_ext_logic.ext_key);
// don't send a data report if auto reporting is off
if (false == m_reporting_auto && data[1] >= RT_REPORT_CORE)

View File

@ -5,7 +5,6 @@
#pragma once
#include <array>
#include <queue>
#include <string>
#include "Common/Logging/Log.h"
@ -220,6 +219,8 @@ public:
{
static_assert(std::is_pod<T>::value);
// TODO: addr wraps around after 0xff
u8* src = reinterpret_cast<u8*>(reg_data) + addr;
count = std::min(count, int(reinterpret_cast<u8*>(reg_data + 1) - src));
@ -233,6 +234,8 @@ public:
{
static_assert(std::is_pod<T>::value);
// TODO: addr wraps around after 0xff
u8* dst = reinterpret_cast<u8*>(reg_data) + addr;
count = std::min(count, int(reinterpret_cast<u8*>(reg_data + 1) - dst));
@ -440,18 +443,12 @@ private:
} m_speaker_logic;
struct ReadRequest
{
u16 address, size, position;
u8* data;
};
void ReportMode(const wm_report_mode* dr);
void SendAck(u8 report_id);
void SendAck(u8 report_id, u8 error_code = 0x0);
void RequestStatus(const wm_request_status* rs = nullptr);
void ReadData(const wm_read_data* rd);
void WriteData(const wm_write_data* wd);
void SendReadDataReply(ReadRequest& request);
bool ProcessReadDataRequest();
bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode);
// control groups
@ -505,10 +502,13 @@ private:
wm_status_report m_status;
// read data request queue
// maybe it isn't actually a queue
// maybe read requests cancel any current requests
std::queue<ReadRequest> m_read_requests;
struct ReadRequest
{
u8 space;
u8 slave_address;
u16 address;
u16 size;
} m_read_request;
#pragma pack(push, 1)
u8 m_eeprom[WIIMOTE_EEPROM_SIZE];