wip
This commit is contained in:
parent
e9aab649e5
commit
82eceeef8e
|
@ -148,6 +148,7 @@
|
||||||
<ClCompile Include="settings.cpp" />
|
<ClCompile Include="settings.cpp" />
|
||||||
<ClCompile Include="shadergen.cpp" />
|
<ClCompile Include="shadergen.cpp" />
|
||||||
<ClCompile Include="sio.cpp" />
|
<ClCompile Include="sio.cpp" />
|
||||||
|
<ClCompile Include="sio_connection.cpp" />
|
||||||
<ClCompile Include="spu.cpp" />
|
<ClCompile Include="spu.cpp" />
|
||||||
<ClCompile Include="system.cpp" />
|
<ClCompile Include="system.cpp" />
|
||||||
<ClCompile Include="texture_replacements.cpp" />
|
<ClCompile Include="texture_replacements.cpp" />
|
||||||
|
@ -228,6 +229,7 @@
|
||||||
<ClInclude Include="shadergen.h" />
|
<ClInclude Include="shadergen.h" />
|
||||||
<ClInclude Include="shader_cache_version.h" />
|
<ClInclude Include="shader_cache_version.h" />
|
||||||
<ClInclude Include="sio.h" />
|
<ClInclude Include="sio.h" />
|
||||||
|
<ClInclude Include="sio_connection.h" />
|
||||||
<ClInclude Include="spu.h" />
|
<ClInclude Include="spu.h" />
|
||||||
<ClInclude Include="system.h" />
|
<ClInclude Include="system.h" />
|
||||||
<ClInclude Include="texture_replacements.h" />
|
<ClInclude Include="texture_replacements.h" />
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
<ClCompile Include="texture_replacements.cpp" />
|
<ClCompile Include="texture_replacements.cpp" />
|
||||||
<ClCompile Include="gdb_protocol.h" />
|
<ClCompile Include="gdb_protocol.h" />
|
||||||
<ClCompile Include="multitap.cpp" />
|
<ClCompile Include="multitap.cpp" />
|
||||||
|
<ClCompile Include="sio_connection.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
@ -118,5 +119,6 @@
|
||||||
<ClInclude Include="texture_replacements.h" />
|
<ClInclude Include="texture_replacements.h" />
|
||||||
<ClInclude Include="shader_cache_version.h" />
|
<ClInclude Include="shader_cache_version.h" />
|
||||||
<ClInclude Include="multitap.h" />
|
<ClInclude Include="multitap.h" />
|
||||||
|
<ClInclude Include="sio_connection.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -610,6 +610,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
||||||
si.SetBoolValue("Debug", "ShowTimersState", false);
|
si.SetBoolValue("Debug", "ShowTimersState", false);
|
||||||
si.SetBoolValue("Debug", "ShowMDECState", false);
|
si.SetBoolValue("Debug", "ShowMDECState", false);
|
||||||
si.SetBoolValue("Debug", "ShowDMAState", false);
|
si.SetBoolValue("Debug", "ShowDMAState", false);
|
||||||
|
si.SetBoolValue("Debug", "ShowSIOState", false);
|
||||||
|
|
||||||
si.SetIntValue("Hacks", "DMAMaxSliceTicks", static_cast<int>(Settings::DEFAULT_DMA_MAX_SLICE_TICKS));
|
si.SetIntValue("Hacks", "DMAMaxSliceTicks", static_cast<int>(Settings::DEFAULT_DMA_MAX_SLICE_TICKS));
|
||||||
si.SetIntValue("Hacks", "DMAHaltTicks", static_cast<int>(Settings::DEFAULT_DMA_HALT_TICKS));
|
si.SetIntValue("Hacks", "DMAHaltTicks", static_cast<int>(Settings::DEFAULT_DMA_HALT_TICKS));
|
||||||
|
|
|
@ -308,6 +308,7 @@ void Settings::Load(SettingsInterface& si)
|
||||||
debugging.show_timers_state = si.GetBoolValue("Debug", "ShowTimersState");
|
debugging.show_timers_state = si.GetBoolValue("Debug", "ShowTimersState");
|
||||||
debugging.show_mdec_state = si.GetBoolValue("Debug", "ShowMDECState");
|
debugging.show_mdec_state = si.GetBoolValue("Debug", "ShowMDECState");
|
||||||
debugging.show_dma_state = si.GetBoolValue("Debug", "ShowDMAState");
|
debugging.show_dma_state = si.GetBoolValue("Debug", "ShowDMAState");
|
||||||
|
debugging.show_sio_state = si.GetBoolValue("Debug", "ShowSIOState");
|
||||||
|
|
||||||
texture_replacements.enable_vram_write_replacements =
|
texture_replacements.enable_vram_write_replacements =
|
||||||
si.GetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false);
|
si.GetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false);
|
||||||
|
@ -468,6 +469,7 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
si.SetBoolValue("Debug", "ShowTimersState", debugging.show_timers_state);
|
si.SetBoolValue("Debug", "ShowTimersState", debugging.show_timers_state);
|
||||||
si.SetBoolValue("Debug", "ShowMDECState", debugging.show_mdec_state);
|
si.SetBoolValue("Debug", "ShowMDECState", debugging.show_mdec_state);
|
||||||
si.SetBoolValue("Debug", "ShowDMAState", debugging.show_dma_state);
|
si.SetBoolValue("Debug", "ShowDMAState", debugging.show_dma_state);
|
||||||
|
si.SetBoolValue("Debug", "ShowSIOState", debugging.show_sio_state);
|
||||||
|
|
||||||
si.SetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements",
|
si.SetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements",
|
||||||
texture_replacements.enable_vram_write_replacements);
|
texture_replacements.enable_vram_write_replacements);
|
||||||
|
|
|
@ -185,6 +185,7 @@ struct Settings
|
||||||
mutable bool show_timers_state = false;
|
mutable bool show_timers_state = false;
|
||||||
mutable bool show_mdec_state = false;
|
mutable bool show_mdec_state = false;
|
||||||
mutable bool show_dma_state = false;
|
mutable bool show_dma_state = false;
|
||||||
|
mutable bool show_sio_state = false;
|
||||||
} debugging;
|
} debugging;
|
||||||
|
|
||||||
// texture replacements
|
// texture replacements
|
||||||
|
|
452
src/core/sio.cpp
452
src/core/sio.cpp
|
@ -1,12 +1,16 @@
|
||||||
#include "sio.h"
|
#include "sio.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/state_wrapper.h"
|
#include "common/state_wrapper.h"
|
||||||
#include "controller.h"
|
|
||||||
#include "host_interface.h"
|
#include "host_interface.h"
|
||||||
|
#include "imgui.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
#include "memory_card.h"
|
#include "sio_connection.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "timing_event.h"
|
||||||
Log_SetChannel(SIO);
|
Log_SetChannel(SIO);
|
||||||
|
|
||||||
|
static constexpr std::array<u32, 4> s_mul_factors = {{1, 16, 64, 0}};
|
||||||
|
|
||||||
SIO g_sio;
|
SIO g_sio;
|
||||||
|
|
||||||
SIO::SIO() = default;
|
SIO::SIO() = default;
|
||||||
|
@ -15,10 +19,23 @@ SIO::~SIO() = default;
|
||||||
|
|
||||||
void SIO::Initialize()
|
void SIO::Initialize()
|
||||||
{
|
{
|
||||||
|
m_transfer_event = TimingEvents::CreateTimingEvent(
|
||||||
|
"SIO Transfer", 1, 1, [](void* param, TickCount ticks, TickCount ticks_late) { g_sio.TransferEvent(); }, nullptr,
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (true)
|
||||||
|
m_connection = SIOConnection::CreateSocketServer("0.0.0.0", 1337);
|
||||||
|
//m_connection = SIOConnection::CreateSocketClient("127.0.0.1", 1337);
|
||||||
|
|
||||||
|
m_stat.bits = 0;
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SIO::Shutdown() {}
|
void SIO::Shutdown()
|
||||||
|
{
|
||||||
|
m_connection.reset();
|
||||||
|
m_transfer_event.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void SIO::Reset()
|
void SIO::Reset()
|
||||||
{
|
{
|
||||||
|
@ -27,41 +44,112 @@ void SIO::Reset()
|
||||||
|
|
||||||
bool SIO::DoState(StateWrapper& sw)
|
bool SIO::DoState(StateWrapper& sw)
|
||||||
{
|
{
|
||||||
sw.Do(&m_SIO_CTRL.bits);
|
const bool dtr = m_stat.DTRINPUTLEVEL;
|
||||||
sw.Do(&m_SIO_STAT.bits);
|
const bool rts = m_stat.CTSINPUTLEVEL;
|
||||||
sw.Do(&m_SIO_MODE.bits);
|
|
||||||
sw.Do(&m_SIO_BAUD);
|
sw.Do(&m_ctrl.bits);
|
||||||
|
sw.Do(&m_stat.bits);
|
||||||
|
sw.Do(&m_mode.bits);
|
||||||
|
sw.Do(&m_baud_rate);
|
||||||
|
|
||||||
|
m_stat.DTRINPUTLEVEL = dtr;
|
||||||
|
m_stat.CTSINPUTLEVEL = rts;
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SIO::SoftReset()
|
||||||
|
{
|
||||||
|
m_ctrl.bits = 0;
|
||||||
|
m_stat.RXPARITY = false;
|
||||||
|
m_stat.RXFIFOOVERRUN = false;
|
||||||
|
m_stat.RXBADSTOPBIT = false;
|
||||||
|
m_stat.INTR = false;
|
||||||
|
m_mode.bits = 0;
|
||||||
|
m_baud_rate = 0xDC;
|
||||||
|
m_data_in.Clear();
|
||||||
|
m_data_out = 0;
|
||||||
|
m_data_out_full = false;
|
||||||
|
|
||||||
|
UpdateEvent();
|
||||||
|
UpdateTXRX();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIO::UpdateTXRX()
|
||||||
|
{
|
||||||
|
m_stat.TXRDY = !m_data_out_full && m_ctrl.TXEN;
|
||||||
|
m_stat.TXDONE = !m_data_out_full;
|
||||||
|
m_stat.RXFIFONEMPTY = !m_data_in.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIO::SetInterrupt()
|
||||||
|
{
|
||||||
|
Log_DevPrintf("Set SIO IRQ");
|
||||||
|
m_stat.INTR = true;
|
||||||
|
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::SIO);
|
||||||
|
}
|
||||||
|
|
||||||
u32 SIO::ReadRegister(u32 offset)
|
u32 SIO::ReadRegister(u32 offset)
|
||||||
{
|
{
|
||||||
switch (offset)
|
switch (offset)
|
||||||
{
|
{
|
||||||
case 0x00: // SIO_DATA
|
case 0x00: // SIO_DATA
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Read SIO_DATA");
|
m_transfer_event->InvokeEarly(false);
|
||||||
|
|
||||||
const u8 value = 0xFF;
|
const u32 data_in_size = m_data_in.GetSize();
|
||||||
return (ZeroExtend32(value) | (ZeroExtend32(value) << 8) | (ZeroExtend32(value) << 16) |
|
u32 res = 0;
|
||||||
(ZeroExtend32(value) << 24));
|
switch (data_in_size)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
case 7:
|
||||||
|
case 6:
|
||||||
|
case 5:
|
||||||
|
case 4:
|
||||||
|
res = ZeroExtend32(m_data_in.Peek(3)) << 24;
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
res |= ZeroExtend32(m_data_in.Peek(2)) << 16;
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
res |= ZeroExtend32(m_data_in.Peek(1)) << 8;
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
res |= ZeroExtend32(m_data_in.Peek(0));
|
||||||
|
m_data_in.RemoveOne();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
res = 0xFFFFFFFFu;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_WarningPrintf("Read SIO_DATA -> 0x%08X", res);
|
||||||
|
UpdateTXRX();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x04: // SIO_STAT
|
case 0x04: // SIO_STAT
|
||||||
{
|
{
|
||||||
const u32 bits = m_SIO_STAT.bits;
|
m_transfer_event->InvokeEarly(false);
|
||||||
|
|
||||||
|
const u32 bits = m_stat.bits;
|
||||||
|
Log_DevPrintf("Read SIO_STAT -> 0x%08X", bits);
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x08: // SIO_MODE
|
case 0x08: // SIO_MODE
|
||||||
return ZeroExtend32(m_SIO_MODE.bits);
|
return ZeroExtend32(m_mode.bits);
|
||||||
|
|
||||||
case 0x0A: // SIO_CTRL
|
case 0x0A: // SIO_CTRL
|
||||||
return ZeroExtend32(m_SIO_CTRL.bits);
|
return ZeroExtend32(m_ctrl.bits);
|
||||||
|
|
||||||
case 0x0E: // SIO_BAUD
|
case 0x0E: // SIO_BAUD
|
||||||
return ZeroExtend32(m_SIO_BAUD);
|
return ZeroExtend32(m_baud_rate);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Log_ErrorPrintf("Unknown register read: 0x%X", offset);
|
Log_ErrorPrintf("Unknown register read: 0x%X", offset);
|
||||||
|
@ -76,31 +164,61 @@ void SIO::WriteRegister(u32 offset, u32 value)
|
||||||
case 0x00: // SIO_DATA
|
case 0x00: // SIO_DATA
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("SIO_DATA (W) <- 0x%02X", value);
|
Log_WarningPrintf("SIO_DATA (W) <- 0x%02X", value);
|
||||||
|
m_transfer_event->InvokeEarly(false);
|
||||||
|
|
||||||
|
if (m_data_out_full)
|
||||||
|
Log_WarningPrintf("SIO TX buffer overflow, lost 0x%02X when writing 0x%02X", m_data_out, value);
|
||||||
|
|
||||||
|
m_data_out = Truncate8(value);
|
||||||
|
m_data_out_full = true;
|
||||||
|
UpdateTXRX();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x0A: // SIO_CTRL
|
case 0x0A: // SIO_CTRL
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("SIO_CTRL <- 0x%04X", value);
|
Log_DevPrintf("SIO_CTRL <- 0x%04X", value);
|
||||||
|
m_transfer_event->InvokeEarly(false);
|
||||||
|
|
||||||
m_SIO_CTRL.bits = Truncate16(value);
|
m_ctrl.bits = Truncate16(value);
|
||||||
if (m_SIO_CTRL.RESET)
|
if (m_ctrl.RESET)
|
||||||
SoftReset();
|
SoftReset();
|
||||||
|
|
||||||
|
if (m_ctrl.ACK)
|
||||||
|
{
|
||||||
|
m_stat.RXPARITY = false;
|
||||||
|
m_stat.RXFIFOOVERRUN = false;
|
||||||
|
m_stat.RXBADSTOPBIT = false;
|
||||||
|
m_stat.INTR = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ctrl.RXEN)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Clearing Input FIFO");
|
||||||
|
m_data_in.Clear();
|
||||||
|
UpdateTXRX();
|
||||||
|
}
|
||||||
|
/*if (!m_ctrl.TXEN)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Clearing output fifo");
|
||||||
|
m_data_out_full = false;
|
||||||
|
UpdateTXRX();
|
||||||
|
}*/
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x08: // SIO_MODE
|
case 0x08: // SIO_MODE
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("SIO_MODE <- 0x%08X", value);
|
Log_DevPrintf("SIO_MODE <- 0x%08X", value);
|
||||||
m_SIO_MODE.bits = Truncate16(value);
|
m_mode.bits = Truncate16(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x0E:
|
case 0x0E:
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("SIO_BAUD <- 0x%08X", value);
|
Log_DevPrintf("SIO_BAUD <- 0x%08X", value);
|
||||||
m_SIO_BAUD = Truncate16(value);
|
m_baud_rate = Truncate16(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,12 +228,288 @@ void SIO::WriteRegister(u32 offset, u32 value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SIO::SoftReset()
|
void SIO::DrawDebugStateWindow()
|
||||||
{
|
{
|
||||||
m_SIO_CTRL.bits = 0;
|
#ifdef WITH_IMGUI
|
||||||
m_SIO_STAT.bits = 0;
|
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||||
m_SIO_STAT.TXDONE = true;
|
|
||||||
m_SIO_STAT.TXRDY = true;
|
ImGui::SetNextWindowSize(ImVec2(600.0f * framebuffer_scale, 400.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
|
||||||
m_SIO_MODE.bits = 0;
|
if (!ImGui::Begin("SIO", nullptr))
|
||||||
m_SIO_BAUD = 0xDC;
|
{
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f};
|
||||||
|
|
||||||
|
ImGui::Text("Connected: ");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored((m_connection && m_connection->IsConnected()) ? active_color : inactive_color,
|
||||||
|
(m_connection && m_connection->IsConnected()) ? "Yes" : "No");
|
||||||
|
|
||||||
|
ImGui::Text("Status: ");
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
float pos = ImGui::GetCursorPosX();
|
||||||
|
ImGui::TextColored(m_stat.TXRDY ? active_color : inactive_color, "TXRDY");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.RXFIFONEMPTY ? active_color : inactive_color, "RXFIFONEMPTY");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.TXDONE ? active_color : inactive_color, "TXDONE");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.RXPARITY ? active_color : inactive_color, "RXPARITY");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.RXFIFOOVERRUN ? active_color : inactive_color, "RXFIFOOVERRUN");
|
||||||
|
ImGui::SetCursorPosX(pos);
|
||||||
|
ImGui::TextColored(m_stat.RXBADSTOPBIT ? active_color : inactive_color, "RXBADSTOPBIT");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.RXINPUTLEVEL ? active_color : inactive_color, "RXINPUTLEVEL");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.DTRINPUTLEVEL ? active_color : inactive_color, "DTRINPUTLEVEL");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.CTSINPUTLEVEL ? active_color : inactive_color, "CTSINPUTLEVEL");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_stat.INTR ? active_color : inactive_color, "INTR");
|
||||||
|
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
ImGui::Text("Control: ");
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
pos = ImGui::GetCursorPosX();
|
||||||
|
ImGui::TextColored(m_ctrl.TXEN ? active_color : inactive_color, "TXEN");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_ctrl.DTROUTPUT ? active_color : inactive_color, "DTROUTPUT");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_ctrl.RXEN ? active_color : inactive_color, "RXEN");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_ctrl.TXOUTPUT ? active_color : inactive_color, "TXOUTPUT");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_ctrl.RTSOUTPUT ? active_color : inactive_color, "RTSOUTPUT");
|
||||||
|
ImGui::SetCursorPosX(pos);
|
||||||
|
ImGui::TextColored(m_ctrl.TXINTEN ? active_color : inactive_color, "TXINTEN");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_ctrl.RXINTEN ? active_color : inactive_color, "RXINTEN");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored(m_ctrl.RXINTEN ? active_color : inactive_color, "RXIMODE: %u", m_ctrl.RXIMODE.GetValue());
|
||||||
|
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
ImGui::Text("Mode: ");
|
||||||
|
ImGui::Text(" Reload Factor: %u", s_mul_factors[m_mode.reload_factor]);
|
||||||
|
ImGui::Text(" Character Length: %u", m_mode.character_length.GetValue());
|
||||||
|
ImGui::Text(" Parity Enable: %s", m_mode.parity_enable ? "Yes" : "No");
|
||||||
|
ImGui::Text(" Parity Type: %u", m_mode.parity_type.GetValue());
|
||||||
|
ImGui::Text(" Stop Bit Length: %u", m_mode.stop_bit_length.GetValue());
|
||||||
|
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
ImGui::Text("Baud Rate: %u", m_baud_rate);
|
||||||
|
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
ImGui::TextColored(m_data_out_full ? active_color : inactive_color, "Output buffer: 0x%02X", m_data_out);
|
||||||
|
|
||||||
|
ImGui::Text("Input buffer: ");
|
||||||
|
for (u32 i = 0; i < m_data_in.GetSize(); i++)
|
||||||
|
{
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("0x%02X ", m_data_in.Peek(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TickCount SIO::GetTicksBetweenTransfers() const
|
||||||
|
{
|
||||||
|
const u32 factor = s_mul_factors[m_mode.reload_factor];
|
||||||
|
const u32 ticks = std::max<u32>((m_baud_rate * factor) & ~u32(1), factor);
|
||||||
|
|
||||||
|
return static_cast<TickCount>(ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIO::UpdateEvent()
|
||||||
|
{
|
||||||
|
if (!m_connection)
|
||||||
|
{
|
||||||
|
m_transfer_event->Deactivate();
|
||||||
|
m_stat.CTSINPUTLEVEL = false;
|
||||||
|
m_stat.DTRINPUTLEVEL = false;
|
||||||
|
m_sync_last_cts = false;
|
||||||
|
m_sync_last_dtr = false;
|
||||||
|
m_sync_last_rts = false;
|
||||||
|
m_sync_remote_rts = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TickCount ticks = GetTicksBetweenTransfers();
|
||||||
|
if (ticks == 0)
|
||||||
|
ticks = System::GetMaxSliceTicks();
|
||||||
|
|
||||||
|
if (m_transfer_event->GetPeriod() == ticks && m_transfer_event->IsActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_transfer_event->Deactivate();
|
||||||
|
m_transfer_event->SetPeriodAndSchedule(ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIO::TransferEvent()
|
||||||
|
{
|
||||||
|
if (m_sync_mode)
|
||||||
|
TransferWithSync();
|
||||||
|
else
|
||||||
|
TransferWithoutSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIO::TransferWithoutSync()
|
||||||
|
{
|
||||||
|
// bytes aren't transmitted when CTS isn't set (i.e. there's nothing on the other side)
|
||||||
|
if (m_connection && m_connection->IsConnected())
|
||||||
|
{
|
||||||
|
m_stat.CTSINPUTLEVEL = true;
|
||||||
|
m_stat.DTRINPUTLEVEL = true;
|
||||||
|
|
||||||
|
if (m_ctrl.RXEN)
|
||||||
|
{
|
||||||
|
u8 data_in;
|
||||||
|
u32 data_in_size = m_connection->Read(&data_in, sizeof(data_in), 0);
|
||||||
|
if (data_in_size > 0)
|
||||||
|
{
|
||||||
|
if (m_data_in.IsFull())
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("FIFO overrun");
|
||||||
|
m_data_in.RemoveOne();
|
||||||
|
m_stat.RXFIFOOVERRUN = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_data_in.Push(data_in);
|
||||||
|
|
||||||
|
if (m_ctrl.RXINTEN)
|
||||||
|
SetInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ctrl.TXEN && m_data_out_full)
|
||||||
|
{
|
||||||
|
const u8 data_out = m_data_out;
|
||||||
|
m_data_out_full = false;
|
||||||
|
|
||||||
|
const u32 data_sent = m_connection->Write(&data_out, sizeof(data_out));
|
||||||
|
if (data_sent != sizeof(data_out))
|
||||||
|
Log_WarningPrintf("Failed to send 0x%02X to connection", data_out);
|
||||||
|
|
||||||
|
if (m_ctrl.TXINTEN)
|
||||||
|
SetInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_stat.CTSINPUTLEVEL = false;
|
||||||
|
m_stat.DTRINPUTLEVEL = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTXRX();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIO::TransferWithSync()
|
||||||
|
{
|
||||||
|
enum : u8
|
||||||
|
{
|
||||||
|
STATE_HAS_DATA = (1 << 0),
|
||||||
|
STATE_DTR_LEVEL = (1 << 1),
|
||||||
|
STATE_CTS_LEVEL = (1 << 2),
|
||||||
|
STATE_RTS_LEVEL = (1 << 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!m_connection || !m_connection->IsConnected())
|
||||||
|
{
|
||||||
|
m_stat.CTSINPUTLEVEL = false;
|
||||||
|
m_stat.DTRINPUTLEVEL = false;
|
||||||
|
m_sync_last_cts = false;
|
||||||
|
m_sync_last_dtr = false;
|
||||||
|
m_sync_last_rts = false;
|
||||||
|
m_sync_remote_rts = false;
|
||||||
|
UpdateTXRX();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 buf[2] = {};
|
||||||
|
if (m_connection->HasData())
|
||||||
|
{
|
||||||
|
while (m_connection->Read(buf, sizeof(buf), sizeof(buf)) != 0)
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("In: %02X %02X", buf[0], buf[1]);
|
||||||
|
|
||||||
|
if (buf[0] & STATE_HAS_DATA)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Received: %02X", buf[1]);
|
||||||
|
if (m_data_in.IsFull())
|
||||||
|
m_stat.RXFIFOOVERRUN = true;
|
||||||
|
else
|
||||||
|
m_data_in.Push(buf[1]);
|
||||||
|
|
||||||
|
if (m_ctrl.RXINTEN)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Setting RX interrupt");
|
||||||
|
SetInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_stat.DTRINPUTLEVEL && buf[0] & STATE_DTR_LEVEL)
|
||||||
|
Log_WarningPrintf("DTR active");
|
||||||
|
else if (m_stat.DTRINPUTLEVEL && !(buf[0] & STATE_DTR_LEVEL))
|
||||||
|
Log_WarningPrintf("DTR inactive");
|
||||||
|
if (!m_stat.CTSINPUTLEVEL && buf[0] & STATE_CTS_LEVEL)
|
||||||
|
Log_WarningPrintf("CTS active");
|
||||||
|
else if (m_stat.CTSINPUTLEVEL && !(buf[0] & STATE_CTS_LEVEL))
|
||||||
|
Log_WarningPrintf("CTS inactive");
|
||||||
|
if (!m_sync_remote_rts && buf[0] & STATE_RTS_LEVEL)
|
||||||
|
Log_WarningPrintf("Remote RTS active");
|
||||||
|
else if (m_sync_remote_rts && !(buf[0] & STATE_RTS_LEVEL))
|
||||||
|
Log_WarningPrintf("Remote RTS inactive");
|
||||||
|
|
||||||
|
m_stat.DTRINPUTLEVEL = (buf[0] & STATE_DTR_LEVEL) != 0;
|
||||||
|
m_stat.CTSINPUTLEVEL = (buf[0] & STATE_CTS_LEVEL) != 0;
|
||||||
|
m_sync_remote_rts = (buf[0] & STATE_RTS_LEVEL) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool cts_level = m_sync_remote_rts && !m_data_in.IsFull();
|
||||||
|
const bool dtr_level = m_ctrl.DTROUTPUT;
|
||||||
|
const bool rts_level = m_ctrl.RTSOUTPUT;
|
||||||
|
const bool tx = (m_ctrl.TXEN || m_latched_txen) && m_stat.CTSINPUTLEVEL && m_data_out_full;
|
||||||
|
m_latched_txen = m_ctrl.TXEN;
|
||||||
|
if (cts_level != m_sync_last_cts || dtr_level != m_sync_last_dtr || rts_level != m_sync_last_rts || tx)
|
||||||
|
{
|
||||||
|
m_sync_last_cts = cts_level;
|
||||||
|
m_sync_last_dtr = dtr_level;
|
||||||
|
m_sync_last_rts = rts_level;
|
||||||
|
|
||||||
|
buf[0] = cts_level ? STATE_CTS_LEVEL : 0;
|
||||||
|
buf[0] |= dtr_level ? STATE_DTR_LEVEL : 0;
|
||||||
|
buf[0] |= rts_level ? STATE_RTS_LEVEL : 0;
|
||||||
|
|
||||||
|
buf[1] = 0;
|
||||||
|
if (tx)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Sending: %02X", m_data_out);
|
||||||
|
buf[0] |= STATE_HAS_DATA;
|
||||||
|
buf[1] = m_data_out;
|
||||||
|
m_data_out_full = false;
|
||||||
|
|
||||||
|
if (m_ctrl.TXINTEN)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Setting TX interrupt");
|
||||||
|
SetInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_InfoPrintf("Out: %02X %02X", buf[0], buf[1]);
|
||||||
|
if (m_connection->Write(buf, sizeof(buf)) != sizeof(buf))
|
||||||
|
Log_WarningPrintf("Write failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTXRX();
|
||||||
|
}
|
|
@ -3,12 +3,14 @@
|
||||||
#include "common/fifo_queue.h"
|
#include "common/fifo_queue.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
class TimingEvent;
|
||||||
|
|
||||||
class Controller;
|
class SIOConnection;
|
||||||
class MemoryCard;
|
|
||||||
|
|
||||||
class SIO
|
class SIO
|
||||||
{
|
{
|
||||||
|
@ -24,7 +26,14 @@ public:
|
||||||
u32 ReadRegister(u32 offset);
|
u32 ReadRegister(u32 offset);
|
||||||
void WriteRegister(u32 offset, u32 value);
|
void WriteRegister(u32 offset, u32 value);
|
||||||
|
|
||||||
|
void DrawDebugStateWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
RX_FIFO_SIZE = 8
|
||||||
|
};
|
||||||
|
|
||||||
union SIO_CTRL
|
union SIO_CTRL
|
||||||
{
|
{
|
||||||
u16 bits;
|
u16 bits;
|
||||||
|
@ -39,7 +48,7 @@ private:
|
||||||
BitField<u16, u8, 8, 2> RXIMODE;
|
BitField<u16, u8, 8, 2> RXIMODE;
|
||||||
BitField<u16, bool, 10, 1> TXINTEN;
|
BitField<u16, bool, 10, 1> TXINTEN;
|
||||||
BitField<u16, bool, 11, 1> RXINTEN;
|
BitField<u16, bool, 11, 1> RXINTEN;
|
||||||
BitField<u16, bool, 12, 1> ACKINTEN;
|
BitField<u16, bool, 12, 1> DTRINTEN;
|
||||||
};
|
};
|
||||||
|
|
||||||
union SIO_STAT
|
union SIO_STAT
|
||||||
|
@ -53,7 +62,7 @@ private:
|
||||||
BitField<u32, bool, 4, 1> RXFIFOOVERRUN;
|
BitField<u32, bool, 4, 1> RXFIFOOVERRUN;
|
||||||
BitField<u32, bool, 5, 1> RXBADSTOPBIT;
|
BitField<u32, bool, 5, 1> RXBADSTOPBIT;
|
||||||
BitField<u32, bool, 6, 1> RXINPUTLEVEL;
|
BitField<u32, bool, 6, 1> RXINPUTLEVEL;
|
||||||
BitField<u32, bool, 7, 1> DSRINPUTLEVEL;
|
BitField<u32, bool, 7, 1> DTRINPUTLEVEL;
|
||||||
BitField<u32, bool, 8, 1> CTSINPUTLEVEL;
|
BitField<u32, bool, 8, 1> CTSINPUTLEVEL;
|
||||||
BitField<u32, bool, 9, 1> INTR;
|
BitField<u32, bool, 9, 1> INTR;
|
||||||
BitField<u32, u32, 11, 15> TMR;
|
BitField<u32, u32, 11, 15> TMR;
|
||||||
|
@ -70,12 +79,56 @@ private:
|
||||||
BitField<u16, u8, 6, 2> stop_bit_length;
|
BitField<u16, u8, 6, 2> stop_bit_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TickCount GetTicksBetweenTransfers() const;
|
||||||
|
|
||||||
void SoftReset();
|
void SoftReset();
|
||||||
|
|
||||||
SIO_CTRL m_SIO_CTRL = {};
|
void UpdateTXRX();
|
||||||
SIO_STAT m_SIO_STAT = {};
|
void SetInterrupt();
|
||||||
SIO_MODE m_SIO_MODE = {};
|
|
||||||
u16 m_SIO_BAUD = 0;
|
void UpdateEvent();
|
||||||
|
void TransferEvent();
|
||||||
|
void TransferWithoutSync();
|
||||||
|
void TransferWithSync();
|
||||||
|
|
||||||
|
std::unique_ptr<SIOConnection> m_connection;
|
||||||
|
std::unique_ptr<TimingEvent> m_transfer_event;
|
||||||
|
|
||||||
|
SIO_CTRL m_ctrl = {};
|
||||||
|
SIO_STAT m_stat = {};
|
||||||
|
SIO_MODE m_mode = {};
|
||||||
|
u16 m_baud_rate = 0;
|
||||||
|
|
||||||
|
InlineFIFOQueue<u8, RX_FIFO_SIZE> m_data_in;
|
||||||
|
|
||||||
|
u8 m_data_out = 0;
|
||||||
|
bool m_data_out_full = false;
|
||||||
|
bool m_latched_txen = false;
|
||||||
|
|
||||||
|
bool m_sync_mode = true;
|
||||||
|
bool m_sync_last_cts = false;
|
||||||
|
bool m_sync_last_dtr = false;
|
||||||
|
bool m_sync_last_rts = false;
|
||||||
|
bool m_sync_remote_rts = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SIOConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~SIOConnection() = default;
|
||||||
|
|
||||||
|
static std::unique_ptr<SIOConnection> CreateSocketServer(std::string hostname, u32 port);
|
||||||
|
static std::unique_ptr<SIOConnection> CreateSocketClient(std::string hostname, u32 port);
|
||||||
|
|
||||||
|
ALWAYS_INLINE bool HasData() const { return m_data_ready.load(); }
|
||||||
|
ALWAYS_INLINE bool IsConnected() const { return m_connected.load(); }
|
||||||
|
|
||||||
|
virtual u32 Read(void* buffer, u32 buffer_size, u32 min_size) = 0;
|
||||||
|
virtual u32 Write(const void* buffer, u32 buffer_size) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::atomic_bool m_connected{false};
|
||||||
|
std::atomic_bool m_data_ready{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SIO g_sio;
|
extern SIO g_sio;
|
|
@ -0,0 +1,499 @@
|
||||||
|
#include "sio_connection.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/string.h"
|
||||||
|
#include <cstdarg>
|
||||||
|
Log_SetChannel(SIOConnection);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
#define SOCKET_ERROR_WOULD_BLOCK WSAEWOULDBLOCK
|
||||||
|
#else
|
||||||
|
#define SOCKET_ERROR_WOULD_BLOCK EWOULDBLOCK
|
||||||
|
#define INVALID_SOCKET -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void CloseSocket(SocketType fd)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(fd);
|
||||||
|
#else
|
||||||
|
close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetSocketError()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return WSAGetLastError();
|
||||||
|
#else
|
||||||
|
return errno;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintSocketError(const char* format, ...)
|
||||||
|
{
|
||||||
|
std::va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
|
||||||
|
SmallString str;
|
||||||
|
str.FormatVA(format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
Log_ErrorPrintf("%s: %d", str.GetCharArray(), GetSocketError());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SetSocketNonblocking(SocketType socket, bool nonblocking)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
u_long value = nonblocking ? 1 : 0;
|
||||||
|
if (ioctlsocket(socket, FIONBIO, &value) != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("ioctlsocket(%s)", nonblocking ? "nonblocking" : "blocking");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SIOSocketConnection::SIOSocketConnection(std::string hostname, u32 port)
|
||||||
|
: m_hostname(std::move(hostname)), m_port(port), m_client_fd(INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SIOSocketConnection::~SIOSocketConnection()
|
||||||
|
{
|
||||||
|
if (m_client_fd != INVALID_SOCKET)
|
||||||
|
CloseSocket(m_client_fd);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
if (m_client_event != NULL)
|
||||||
|
WSACloseEvent(m_client_event);
|
||||||
|
|
||||||
|
if (m_want_write_event != NULL)
|
||||||
|
CloseHandle(m_want_write_event);
|
||||||
|
|
||||||
|
if (m_sockets_initialized)
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SIOSocketConnection::Initialize()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wd = {};
|
||||||
|
if (WSAStartup(MAKEWORD(2, 0), &wd) != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("WSAStartup() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sockets_initialized = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
m_client_event = WSACreateEvent();
|
||||||
|
m_want_write_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
if (m_client_event == NULL || m_want_write_event == NULL)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 SIOSocketConnection::Read(void* buffer, u32 buffer_size, u32 min_size)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_buffer_mutex);
|
||||||
|
if (m_read_buffer.empty() || m_client_fd < 0)
|
||||||
|
{
|
||||||
|
m_data_ready.store(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_read_buffer.size() < min_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const u32 to_read = std::min<u32>(static_cast<u32>(m_read_buffer.size()), buffer_size);
|
||||||
|
if (to_read > 0)
|
||||||
|
{
|
||||||
|
std::memcpy(buffer, m_read_buffer.data(), to_read);
|
||||||
|
if (to_read == m_read_buffer.size())
|
||||||
|
{
|
||||||
|
m_read_buffer.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const size_t new_size = m_read_buffer.size() - to_read;
|
||||||
|
std::memmove(&m_read_buffer[0], &m_read_buffer[to_read], new_size);
|
||||||
|
m_read_buffer.resize(new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_data_ready.store(!m_read_buffer.empty());
|
||||||
|
return to_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 SIOSocketConnection::Write(const void* buffer, u32 buffer_size)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_buffer_mutex);
|
||||||
|
if (m_client_fd < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// TODO: Max buffer size
|
||||||
|
const u32 to_write = buffer_size;
|
||||||
|
const size_t current_size = m_write_buffer.size();
|
||||||
|
m_write_buffer.resize(m_write_buffer.size() + buffer_size);
|
||||||
|
std::memcpy(&m_write_buffer[current_size], buffer, buffer_size);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetEvent(m_want_write_event);
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return to_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketConnection::StartThread()
|
||||||
|
{
|
||||||
|
m_thread = std::thread([this]() { SocketThread(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketConnection::ShutdownThread()
|
||||||
|
{
|
||||||
|
if (!m_thread.joinable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_thread_shutdown.store(true);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetEvent(m_want_write_event);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketConnection::HandleRead()
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_buffer_mutex);
|
||||||
|
|
||||||
|
size_t current_size = m_read_buffer.size();
|
||||||
|
size_t buffer_size = std::max<size_t>(m_read_buffer.size() * 2, 128);
|
||||||
|
m_read_buffer.resize(buffer_size);
|
||||||
|
|
||||||
|
int nbytes = recv(m_client_fd, reinterpret_cast<char*>(&m_read_buffer[current_size]),
|
||||||
|
static_cast<int>(buffer_size - current_size), 0);
|
||||||
|
if (nbytes <= 0)
|
||||||
|
{
|
||||||
|
m_read_buffer.resize(current_size);
|
||||||
|
if (GetSocketError() == SOCKET_ERROR_WOULD_BLOCK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PrintSocketError("recv() failed");
|
||||||
|
Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (nbytes == 0)
|
||||||
|
{
|
||||||
|
Log_InfoPrint("Client disconnected.");
|
||||||
|
Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_read_buffer.resize(current_size + static_cast<size_t>(nbytes));
|
||||||
|
m_data_ready.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketConnection::HandleWrite()
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_buffer_mutex);
|
||||||
|
if (m_write_buffer.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int nbytes =
|
||||||
|
send(m_client_fd, reinterpret_cast<const char*>(m_write_buffer.data()), static_cast<int>(m_write_buffer.size()), 0);
|
||||||
|
if (nbytes < 0)
|
||||||
|
{
|
||||||
|
if (GetSocketError() == SOCKET_ERROR_WOULD_BLOCK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PrintSocketError("send() failed");
|
||||||
|
Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbytes == static_cast<int>(m_write_buffer.size()))
|
||||||
|
{
|
||||||
|
m_write_buffer.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t new_size = m_write_buffer.size() - static_cast<size_t>(nbytes);
|
||||||
|
std::memmove(&m_write_buffer[0], &m_write_buffer[static_cast<size_t>(nbytes)], new_size);
|
||||||
|
m_write_buffer.resize(new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketConnection::HandleClose()
|
||||||
|
{
|
||||||
|
Log_InfoPrint("Client disconnected.");
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketConnection::Disconnect()
|
||||||
|
{
|
||||||
|
CloseSocket(m_client_fd);
|
||||||
|
m_client_fd = INVALID_SOCKET;
|
||||||
|
m_read_buffer.clear();
|
||||||
|
m_write_buffer.clear();
|
||||||
|
m_connected.store(false);
|
||||||
|
m_data_ready.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SIOConnection> SIOConnection::CreateSocketServer(std::string hostname, u32 port)
|
||||||
|
{
|
||||||
|
std::unique_ptr<SIOSocketServerConnection> server(new SIOSocketServerConnection(std::move(hostname), port));
|
||||||
|
if (!server->Initialize())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
SIOSocketServerConnection::SIOSocketServerConnection(std::string hostname, u32 port)
|
||||||
|
: SIOSocketConnection(std::move(hostname), port), m_accept_fd(INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SIOSocketServerConnection::~SIOSocketServerConnection()
|
||||||
|
{
|
||||||
|
ShutdownThread();
|
||||||
|
|
||||||
|
if (m_accept_fd != INVALID_SOCKET)
|
||||||
|
CloseSocket(m_accept_fd);
|
||||||
|
|
||||||
|
if (m_accept_event != NULL)
|
||||||
|
WSACloseEvent(m_accept_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SIOSocketServerConnection::Initialize()
|
||||||
|
{
|
||||||
|
if (!SIOSocketConnection::Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sockaddr_in addr = {};
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(static_cast<u16>(m_port));
|
||||||
|
|
||||||
|
m_accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (m_accept_fd == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
PrintSocketError("socket() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(m_accept_fd, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("bind() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(m_accept_fd, 1) != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("listen() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetSocketNonblocking(m_accept_fd, true);
|
||||||
|
|
||||||
|
m_accept_event = WSACreateEvent();
|
||||||
|
if (m_accept_event == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (WSAEventSelect(m_accept_fd, m_accept_event, FD_ACCEPT) != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("WSAEventSelect(FD_ACCEPT) failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
StartThread();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketServerConnection::SocketThread()
|
||||||
|
{
|
||||||
|
while (!m_thread_shutdown.load())
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
const HANDLE event_handles[] = {m_want_write_event, m_accept_event, m_client_event};
|
||||||
|
const DWORD res = WSAWaitForMultipleEvents(countof(event_handles), event_handles, FALSE, 1000, FALSE);
|
||||||
|
if (res == WAIT_TIMEOUT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
WSANETWORKEVENTS ev;
|
||||||
|
if (WSAEnumNetworkEvents(m_accept_fd, m_accept_event, &ev) == 0)
|
||||||
|
{
|
||||||
|
if (ev.lNetworkEvents & FD_ACCEPT)
|
||||||
|
HandleAccept();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_client_fd != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
if (WSAEnumNetworkEvents(m_client_fd, m_client_event, &ev) == 0)
|
||||||
|
{
|
||||||
|
if (ev.lNetworkEvents & FD_READ)
|
||||||
|
HandleRead();
|
||||||
|
if (ev.lNetworkEvents & FD_WRITE)
|
||||||
|
HandleWrite();
|
||||||
|
if (ev.lNetworkEvents & FD_CLOSE)
|
||||||
|
HandleClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_client_fd != INVALID_SOCKET && res == WSA_WAIT_EVENT_0)
|
||||||
|
HandleWrite();
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketServerConnection::HandleAccept()
|
||||||
|
{
|
||||||
|
sockaddr client_address = {};
|
||||||
|
int client_address_len = sizeof(client_address);
|
||||||
|
SocketType new_socket = accept(m_accept_fd, &client_address, &client_address_len);
|
||||||
|
if (new_socket == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
if (GetSocketError() != SOCKET_ERROR_WOULD_BLOCK)
|
||||||
|
PrintSocketError("accept() failed");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_client_fd != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
static const char error[] = "Client already connected.";
|
||||||
|
Log_WarningPrint("Dropping client connection because we're already connected");
|
||||||
|
|
||||||
|
// we already have a client
|
||||||
|
SetSocketNonblocking(new_socket, false);
|
||||||
|
send(new_socket, error, sizeof(error) - 1, 0);
|
||||||
|
CloseSocket(new_socket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSocketNonblocking(new_socket, true);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (WSAEventSelect(new_socket, m_client_event, FD_READ | FD_WRITE | FD_CLOSE) != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("WSAEventSelect(FD_READ | FD_WRITE | FD_CLOSE) failed");
|
||||||
|
CloseSocket(new_socket);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::unique_lock lock(m_buffer_mutex);
|
||||||
|
Log_InfoPrintf("Client connection accepted: %d", new_socket);
|
||||||
|
m_client_fd = new_socket;
|
||||||
|
m_connected.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SIOSocketClientConnection::SIOSocketClientConnection(std::string hostname, u32 port)
|
||||||
|
: SIOSocketConnection(std::move(hostname), port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SIOSocketClientConnection::~SIOSocketClientConnection()
|
||||||
|
{
|
||||||
|
ShutdownThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SIOSocketClientConnection::Initialize()
|
||||||
|
{
|
||||||
|
if (!SIOSocketConnection::Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct addrinfo hints = {};
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
struct addrinfo* ai;
|
||||||
|
int err = getaddrinfo(m_hostname.c_str(), TinyString::FromFormat("%u", m_port), &hints, &ai);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("getaddrinfo(%s:%u) failed: %d", m_hostname.c_str(), m_port, err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_client_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||||
|
if (m_client_fd == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
PrintSocketError("socket() failed");
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = connect(m_client_fd, ai->ai_addr, static_cast<int>(ai->ai_addrlen));
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("connect() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSocketNonblocking(m_client_fd, true);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (WSAEventSelect(m_client_fd, m_client_event, FD_READ | FD_WRITE | FD_CLOSE) != 0)
|
||||||
|
{
|
||||||
|
PrintSocketError("WSAEventSelect(FD_READ | FD_WRITE | FD_CLOSE) failed");
|
||||||
|
CloseSocket(m_client_fd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_connected.store(true);
|
||||||
|
StartThread();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SIOSocketClientConnection::SocketThread()
|
||||||
|
{
|
||||||
|
while (!m_thread_shutdown.load())
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE event_handles[] = {m_want_write_event, m_client_event};
|
||||||
|
DWORD res = WSAWaitForMultipleEvents(countof(event_handles), event_handles, FALSE, 1000, FALSE);
|
||||||
|
if (res == WAIT_TIMEOUT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
WSANETWORKEVENTS ev;
|
||||||
|
if (m_client_fd != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
if (WSAEnumNetworkEvents(m_client_fd, m_client_event, &ev) == 0)
|
||||||
|
{
|
||||||
|
if (ev.lNetworkEvents & FD_READ)
|
||||||
|
HandleRead();
|
||||||
|
if (ev.lNetworkEvents & FD_WRITE)
|
||||||
|
HandleWrite();
|
||||||
|
if (ev.lNetworkEvents & FD_CLOSE)
|
||||||
|
HandleClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_client_fd != INVALID_SOCKET && res == WSA_WAIT_EVENT_0)
|
||||||
|
HandleWrite();
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SIOConnection> SIOConnection::CreateSocketClient(std::string hostname, u32 port)
|
||||||
|
{
|
||||||
|
std::unique_ptr<SIOSocketClientConnection> server(new SIOSocketClientConnection(std::move(hostname), port));
|
||||||
|
if (!server->Initialize())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
#pragma once
|
||||||
|
#include "sio.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "common/windows_headers.h"
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
using SocketType = SOCKET;
|
||||||
|
#else
|
||||||
|
using SocketType = int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SIOSocketConnection : public SIOConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SIOSocketConnection(std::string hostname, u32 port);
|
||||||
|
~SIOSocketConnection() override;
|
||||||
|
|
||||||
|
virtual bool Initialize();
|
||||||
|
|
||||||
|
u32 Read(void* buffer, u32 buffer_size, u32 min_size) override;
|
||||||
|
u32 Write(const void* buffer, u32 buffer_size) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void SocketThread() = 0;
|
||||||
|
|
||||||
|
void StartThread();
|
||||||
|
void ShutdownThread();
|
||||||
|
|
||||||
|
void HandleRead();
|
||||||
|
void HandleWrite();
|
||||||
|
void HandleClose();
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
std::string m_hostname;
|
||||||
|
std::thread m_thread;
|
||||||
|
std::atomic_bool m_thread_shutdown{false};
|
||||||
|
u32 m_port = 0;
|
||||||
|
SocketType m_client_fd;
|
||||||
|
|
||||||
|
std::mutex m_buffer_mutex;
|
||||||
|
std::vector<u8> m_read_buffer;
|
||||||
|
std::vector<u8> m_write_buffer;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE m_client_event = NULL;
|
||||||
|
HANDLE m_want_write_event = NULL;
|
||||||
|
bool m_sockets_initialized = false;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class SIOSocketServerConnection : public SIOSocketConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SIOSocketServerConnection(std::string hostname, u32 port);
|
||||||
|
~SIOSocketServerConnection() override;
|
||||||
|
|
||||||
|
bool Initialize() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SocketThread() override;
|
||||||
|
|
||||||
|
void HandleAccept();
|
||||||
|
|
||||||
|
SocketType m_accept_fd;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE m_accept_event = NULL;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class SIOSocketClientConnection : public SIOSocketConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SIOSocketClientConnection(std::string hostname, u32 port);
|
||||||
|
~SIOSocketClientConnection() override;
|
||||||
|
|
||||||
|
bool Initialize() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SocketThread() override;
|
||||||
|
};
|
|
@ -1133,6 +1133,7 @@ void MainWindow::connectSignals()
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowMDECState, "Debug",
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowMDECState, "Debug",
|
||||||
"ShowMDECState");
|
"ShowMDECState");
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowDMAState, "Debug", "ShowDMAState");
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowDMAState, "Debug", "ShowDMAState");
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowSIOState, "Debug", "ShowSIOState");
|
||||||
|
|
||||||
addThemeToMenu(tr("Default"), QStringLiteral("default"));
|
addThemeToMenu(tr("Default"), QStringLiteral("default"));
|
||||||
addThemeToMenu(tr("Fusion"), QStringLiteral("fusion"));
|
addThemeToMenu(tr("Fusion"), QStringLiteral("fusion"));
|
||||||
|
|
|
@ -193,6 +193,7 @@
|
||||||
<addaction name="actionDebugShowTimersState"/>
|
<addaction name="actionDebugShowTimersState"/>
|
||||||
<addaction name="actionDebugShowMDECState"/>
|
<addaction name="actionDebugShowMDECState"/>
|
||||||
<addaction name="actionDebugShowDMAState"/>
|
<addaction name="actionDebugShowDMAState"/>
|
||||||
|
<addaction name="actionDebugShowSIOState"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_View">
|
<widget class="QMenu" name="menu_View">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -701,6 +702,14 @@
|
||||||
<string>Show DMA State</string>
|
<string>Show DMA State</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionDebugShowSIOState">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Show SIO State</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionScreenshot">
|
<action name="actionScreenshot">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="resources/resources.qrc">
|
<iconset resource="resources/resources.qrc">
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "core/mdec.h"
|
#include "core/mdec.h"
|
||||||
#include "core/pgxp.h"
|
#include "core/pgxp.h"
|
||||||
#include "core/save_state_version.h"
|
#include "core/save_state_version.h"
|
||||||
|
#include "core/sio.h"
|
||||||
#include "core/spu.h"
|
#include "core/spu.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
#include "core/texture_replacements.h"
|
#include "core/texture_replacements.h"
|
||||||
|
@ -1254,6 +1255,8 @@ void CommonHostInterface::DrawDebugWindows()
|
||||||
g_mdec.DrawDebugStateWindow();
|
g_mdec.DrawDebugStateWindow();
|
||||||
if (g_settings.debugging.show_dma_state)
|
if (g_settings.debugging.show_dma_state)
|
||||||
g_dma.DrawDebugStateWindow();
|
g_dma.DrawDebugStateWindow();
|
||||||
|
if (g_settings.debugging.show_sio_state)
|
||||||
|
g_sio.DrawDebugStateWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommonHostInterface::IsCheevosChallengeModeActive() const
|
bool CommonHostInterface::IsCheevosChallengeModeActive() const
|
||||||
|
@ -2655,6 +2658,7 @@ void CommonHostInterface::FixIncompatibleSettings(bool display_osd_messages)
|
||||||
g_settings.debugging.show_timers_state = false;
|
g_settings.debugging.show_timers_state = false;
|
||||||
g_settings.debugging.show_mdec_state = false;
|
g_settings.debugging.show_mdec_state = false;
|
||||||
g_settings.debugging.show_dma_state = false;
|
g_settings.debugging.show_dma_state = false;
|
||||||
|
g_settings.debugging.show_sio_state = false;
|
||||||
g_settings.debugging.dump_cpu_to_vram_copies = false;
|
g_settings.debugging.dump_cpu_to_vram_copies = false;
|
||||||
g_settings.debugging.dump_vram_to_cpu_copies = false;
|
g_settings.debugging.dump_vram_to_cpu_copies = false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue