Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USIO: Emulation Performance Optimization #13174

Merged
merged 1 commit into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 51 additions & 112 deletions rpcs3/Emu/Io/usio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

#include "stdafx.h"
#include "usio.h"
#include "Emu/Cell/lv2/sys_usbd.h"
#include "Input/pad_thread.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/system_utils.hpp"

Expand Down Expand Up @@ -120,7 +118,7 @@ void usb_device_usio::load_backup()

if (usio_backup_file.size() != file_size)
{
usio_log.trace("Invalid USIO Backup file detected. Treating it as an empty file: %s", usio_backup_path);
usio_log.trace("Invalid USIO Backup file detected: %s", usio_backup_path);
usio_backup_file.trunc(file_size);
return;
}
Expand All @@ -130,8 +128,9 @@ void usb_device_usio::load_backup()

void usb_device_usio::save_backup()
{
if (!usio_backup_file)
if (!usio_backup_file && !usio_backup_path.empty() && !usio_backup_file.open(usio_backup_path, fs::create + fs::write + fs::lock))
{
usio_log.error("Failed to create a new USIO Backup file: %s", usio_backup_path);
return;
}

Expand All @@ -144,7 +143,7 @@ void usb_device_usio::translate_input()
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();

std::vector<u8> input_buf = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
std::vector<u8> input_buf = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr le_t<u16> c_small_hit = 0x4D0;
constexpr le_t<u16> c_big_hit = 0x1800;
le_t<u16> test_keys = 0x0000;
Expand Down Expand Up @@ -263,23 +262,23 @@ void usb_device_usio::translate_input()
std::memcpy(input_buf.data(), &test_keys, sizeof(u16));
std::memcpy(input_buf.data() + 16, &coin_counter, sizeof(u16));

q_replies.push(input_buf);
q_replies.push({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
response = std::move(input_buf);
}

void usb_device_usio::usio_write(u8 channel, u16 reg, const std::vector<u8>& data)
void usb_device_usio::usio_write(u8 channel, u16 reg, std::vector<u8>& data)
{
auto write_memory = [&](std::vector<u8>& memory, bool exact_size = true)
auto write_memory = [&](std::vector<u8>& memory)
{
ensure(data.size() == memory.size() || (data.size() <= memory.size() && !exact_size));
memcpy(memory.data(), data.data(), data.size());
auto size = memory.size();
memory = std::move(data);
memory.resize(size);
brian218 marked this conversation as resolved.
Show resolved Hide resolved
};

const auto get_u16 = [&](std::string_view usio_func) -> u16
{
if (data.size() != 2)
{
usio_log.fatal("data.size() is %d, expected 2 for get_u16 in %s", data.size(), usio_func);
usio_log.error("data.size() is %d, expected 2 for get_u16 in %s", data.size(), usio_func);
}
return *reinterpret_cast<const le_t<u16>*>(data.data());
};
Expand All @@ -290,35 +289,34 @@ void usb_device_usio::usio_write(u8 channel, u16 reg, const std::vector<u8>& dat
{
case 0x0002:
{
usio_log.notice("SetSystemError: 0x%04X", get_u16("SetSystemError"));
usio_log.trace("SetSystemError: 0x%04X", get_u16("SetSystemError"));
break;
}
case 0x000A:
{
u16 command = get_u16("ClearSram");
ensure(command == 0x6666, "USIO: Unexpected Command instead of ClearSram");
usio_log.notice("ClearSram");
if (get_u16("ClearSram") == 0x6666)
usio_log.trace("ClearSram");
break;
}
case 0x0028:
{
usio_log.notice("SetExpansionMode: 0x%04X", get_u16("SetExpansionMode"));
usio_log.trace("SetExpansionMode: 0x%04X", get_u16("SetExpansionMode"));
break;
}
case 0x0048:
case 0x0058:
case 0x0068:
case 0x0078:
{
usio_log.notice("SetHopperRequest(Hopper: %d, Request: 0x%04X)", (reg - 0x48) / 0x10, get_u16("SetHopperRequest"));
usio_log.trace("SetHopperRequest(Hopper: %d, Request: 0x%04X)", (reg - 0x48) / 0x10, get_u16("SetHopperRequest"));
break;
}
case 0x004A:
case 0x005A:
case 0x006A:
case 0x007A:
{
usio_log.notice("SetHopperRequest(Hopper: %d, Limit: 0x%04X)", (reg - 0x4A) / 0x10, get_u16("SetHopperLimit"));
usio_log.trace("SetHopperRequest(Hopper: %d, Limit: 0x%04X)", (reg - 0x4A) / 0x10, get_u16("SetHopperLimit"));
break;
}
default:
Expand All @@ -337,7 +335,7 @@ void usb_device_usio::usio_write(u8 channel, u16 reg, const std::vector<u8>& dat
{
case 0x0000:
{
write_memory(g_fxo->get<usio_memory>().backup_memory, false);
write_memory(g_fxo->get<usio_memory>().backup_memory);
break;
}
case 0x0180:
Expand All @@ -358,34 +356,6 @@ void usb_device_usio::usio_write(u8 channel, u16 reg, const std::vector<u8>& dat

void usb_device_usio::usio_read(u8 channel, u16 reg, u16 size)
{
auto push_zeroes = [&]()
{
// Give it 00s
std::vector<u8> zeroes;
u16 left = size;
while (left > 0)
{
u16 to_push = std::min(left, static_cast<u16>(64));
zeroes.resize(to_push);
q_replies.push(zeroes);
left -= to_push;
}
};

auto push_vector = [&](std::vector<u8>& memory)
{
std::vector<u8> buffer;
u16 left = size;
while (left > 0)
{
u16 to_push = std::min(left, static_cast<u16>(64));
buffer.resize(to_push);
memcpy(buffer.data(), memory.data() + (size - left), buffer.size());
q_replies.push(buffer);
left -= to_push;
}
};

if (channel == 0)
{
switch (reg)
Expand All @@ -395,21 +365,19 @@ void usb_device_usio::usio_read(u8 channel, u16 reg, u16 size)
// Get Buffer, rarely gives a reply on real HW
// First U16 seems to be a timestamp of sort
// Purpose seems related to connectivity check
q_replies.push({0x7E, 0xE4, 0x00, 0x00, 0x74, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
response = {0x7E, 0xE4, 0x00, 0x00, 0x74, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
break;
}
case 0x0080:
{
// Card reader check - 1
ensure(size == 0x10);
q_replies.push({0x02, 0x03, 0x06, 0x00, 0xFF, 0x0F, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x10, 0x00});
response = {0x02, 0x03, 0x06, 0x00, 0xFF, 0x0F, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x10, 0x00};
break;
}
case 0x7000:
{
// Card reader check - 2
ensure(size == 0x06);
// No data returned
response.resize(size);
break;
}
case 0x1080:
Expand All @@ -420,28 +388,22 @@ void usb_device_usio::usio_read(u8 channel, u16 reg, u16 size)
}
case 0x1800:
{
usio_log.trace("Firmware Query on 0x1800");
ensure(size == 0x70);
// Firmware
// "NBGI.;USIO01;Ver1.00;JPN,Multipurpose with PPG."
q_replies.push({0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
q_replies.push({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
response = {0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
break;
}
case 0x1880:
{
// Seems to contain a few extra bytes of info in addition to the firmware string
usio_log.trace("Firmware query on 0x1880");
ensure(size == 0x70);
// Firmware
// "NBGI2;USIO01;Ver1.00;JPN,Multipurpose with PPG."
q_replies.push({0x4E, 0x42, 0x47, 0x49, 0x32, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
q_replies.push({0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x08, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
response = {0x4E, 0x42, 0x47, 0x49, 0x32, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x08, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
break;
}
default:
{
usio_log.fatal("Unhandled channel 0 register read: 0x%04X", reg);
usio_log.error("Unhandled channel 0 register read: 0x%04X", reg);
break;
}
}
Expand All @@ -458,41 +420,39 @@ void usb_device_usio::usio_read(u8 channel, u16 reg, u16 size)
{
case 0x0000:
{
ensure(size <= 0xB8);
push_vector(g_fxo->get<usio_memory>().backup_memory);
response = g_fxo->get<usio_memory>().backup_memory;
break;
}
case 0x0180:
{
ensure(size == 0x28);
push_vector(g_fxo->get<usio_memory>().last_game_status);
response = g_fxo->get<usio_memory>().last_game_status;
break;
}
case 0x0200:
{
ensure(size == 0x100);
//ensure(size == 0x100);
// No data returned
break;
}
case 0x1000:
{
ensure(size == 0x1000);
push_zeroes();
//ensure(size == 0x1000);
response.resize(size);
break;
}
default:
{
usio_log.fatal("Unhandled read of sram(chip: %d, addr: 0x%04X)", channel - 2, reg);
push_zeroes();
usio_log.error("Unhandled read of sram(chip: %d, addr: 0x%04X)", channel - 2, reg);
response.resize(size);
break;
}
}
break;
}
default:
{
usio_log.fatal("Unhandled read of sram(chip: %d, addr: 0x%04X)", channel - 2, reg);
push_zeroes();
usio_log.error("Unhandled read of sram(chip: %d, addr: 0x%04X)", channel - 2, reg);
response.resize(size);
break;
}
}
Expand All @@ -509,9 +469,11 @@ void usb_device_usio::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, Us
{
constexpr u8 USIO_COMMAND_WRITE = 0x90;
constexpr u8 USIO_COMMAND_READ = 0x10;
constexpr u8 USIO_COMMAND_INIT = 0xA0;

static bool expecting_data = false;
static std::vector<u8> usio_data;
static u32 response_seek = 0;
static u8 usio_channel = 0;
static u16 usio_register = 0;
static u16 usio_length = 0;
Expand All @@ -521,11 +483,6 @@ void usb_device_usio::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, Us
// The latency varies per operation but it doesn't seem to matter for this device so let's go fast!
transfer->expected_time = get_timestamp();

if (!usio_backup_path.empty() && !usio_backup_file && !usio_backup_file.open(usio_backup_path, fs::create + fs::read + fs::write + fs::lock))
{
usio_log.error("Failed to create a new USIO Backup file: %s", usio_backup_path);
}

switch (endpoint)
{
case 0x01:
Expand Down Expand Up @@ -556,56 +513,38 @@ void usb_device_usio::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, Us
{
usio_log.trace("UsioWrite(Channel: 0x%02X, Register: 0x%04X, Length: 0x%04X)", usio_channel, usio_register, usio_length);
ensure(((~(usio_register >> 8)) & 0xF0) == buf[1]);

expecting_data = true;
usio_data.clear();
return;
}

if ((buf[0] & USIO_COMMAND_READ) == USIO_COMMAND_READ)
else if ((buf[0] & USIO_COMMAND_READ) == USIO_COMMAND_READ)
{
usio_log.trace("UsioRead(Channel: 0x%02X, Register: 0x%04X, Length: 0x%04X)", usio_channel, usio_register, usio_length);
response_seek = 0;
response.clear();
usio_read(usio_channel, usio_register, usio_length);
return;
}

// Init and reset commands
if ((buf[0] & 0xA0) == 0xA0)
else if ((buf[0] & USIO_COMMAND_INIT) == USIO_COMMAND_INIT) // Init and reset commands
{
const std::array<u8, 2> init_command = {0xA0, 0xF0}; // This kind of command starts with 0xA0, 0xF0 commonly. For example, {0xA0, 0xF0, 0x28, 0x00, 0x00, 0x80}
ensure(memcmp(buf, init_command.data(), 2) == 0);
return;
//const std::array<u8, 2> init_command = {0xA0, 0xF0}; // This kind of command starts with 0xA0, 0xF0 commonly. For example, {0xA0, 0xF0, 0x28, 0x00, 0x00, 0x80}
//ensure(memcmp(buf, init_command.data(), 2) == 0);
brian218 marked this conversation as resolved.
Show resolved Hide resolved
}

fmt::throw_exception("Received an unexpected command: 0x%02X", buf[0]);
else
{
usio_log.error("Received an unexpected command: 0x%02X", buf[0]);
}
break;
}
case 0x82:
{
// Read endpoint
if (!q_replies.empty())
{
// Sometimes software will outright ignore what usio sends and read with a buffer of 0
if (buf_size == 0)
{
transfer->expected_count = q_replies.front().size();
q_replies.pop();
break;
}

// Otherwise we expect the buffer to be appropriately sized
ensure(buf_size >= q_replies.front().size());
memcpy(buf, q_replies.front().data(), q_replies.front().size());
transfer->expected_count = q_replies.front().size();
q_replies.pop();
}
else
{
transfer->expected_count = 0;
}
const u32 size = std::min(buf_size, static_cast<u32>(response.size() - response_seek));
memcpy(buf, response.data() + response_seek, size);
response_seek += size;
transfer->expected_count = size;
break;
}
default:
usio_log.fatal("Unhandled endpoint: 0x%x", endpoint);
usio_log.error("Unhandled endpoint: 0x%x", endpoint);
break;
}
}
5 changes: 2 additions & 3 deletions rpcs3/Emu/Io/usio.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include "Emu/Io/usb_device.h"
#include <queue>

class usb_device_usio : public usb_device_emulated
{
Expand All @@ -17,7 +16,7 @@ class usb_device_usio : public usb_device_emulated
void load_backup();
void save_backup();
void translate_input();
void usio_write(u8 channel, u16 reg, const std::vector<u8>& data);
void usio_write(u8 channel, u16 reg, std::vector<u8>& data);
void usio_read(u8 channel, u16 reg, u16 size);

private:
Expand All @@ -27,5 +26,5 @@ class usb_device_usio : public usb_device_emulated
le_t<u16> coin_counter = 0;
std::string usio_backup_path;
fs::file usio_backup_file;
std::queue<std::vector<u8>> q_replies;
std::vector<u8> response;
};