mirror of https://github.com/PCSX2/pcsx2.git
Pad/Multitap: Improve PS2-side ejection detection
This commit is contained in:
parent
f0bf525cf4
commit
eb1d93d4e5
|
@ -88,7 +88,18 @@ void MemoryCardProtocol::Probe()
|
|||
{
|
||||
MC_LOG.WriteLn("%s", __FUNCTION__);
|
||||
PS1_FAIL();
|
||||
The2bTerminator(4);
|
||||
|
||||
if (!mcd->IsPresent())
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
The2bTerminator(4);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryCardProtocol::UnknownWriteDeleteEnd()
|
||||
|
@ -253,6 +264,12 @@ void MemoryCardProtocol::ReadData()
|
|||
u8 MemoryCardProtocol::PS1Read(u8 data)
|
||||
{
|
||||
MC_LOG.WriteLn("%s", __FUNCTION__);
|
||||
|
||||
if (!mcd->IsPresent())
|
||||
{
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
bool sendAck = true;
|
||||
u8 ret = 0;
|
||||
|
||||
|
@ -489,7 +506,18 @@ void MemoryCardProtocol::AuthF3()
|
|||
{
|
||||
MC_LOG.WriteLn("%s", __FUNCTION__);
|
||||
PS1_FAIL();
|
||||
The2bTerminator(5);
|
||||
|
||||
if (!mcd->IsPresent())
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
The2bTerminator(5);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryCardProtocol::AuthF7()
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "SIO/Multitap/MultitapProtocol.h"
|
||||
|
||||
#include "SIO/Sio2.h"
|
||||
|
@ -23,35 +25,78 @@
|
|||
#define MT_LOG_ENABLE 0
|
||||
#define MT_LOG if (MT_LOG_ENABLE) DevCon
|
||||
|
||||
MultitapProtocol g_MultitapProtocol;
|
||||
MultitapProtocol g_MultitapPort0;
|
||||
MultitapProtocol g_MultitapPort1;
|
||||
|
||||
void MultitapProtocol::SupportCheck()
|
||||
{
|
||||
MT_LOG.WriteLn("%s", __FUNCTION__);
|
||||
g_Sio2FifoOut.push_back(0x5a);
|
||||
g_Sio2FifoOut.push_back(0x04);
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
g_Sio2FifoOut.push_back(0x5a);
|
||||
|
||||
if (EmuConfig.Pad.IsMultitapPortEnabled(g_Sio2.port))
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0x80);
|
||||
g_Sio2FifoOut.push_back(0x5a);
|
||||
g_Sio2FifoOut.push_back(0x04);
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
g_Sio2FifoOut.push_back(0x5a);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
}
|
||||
}
|
||||
|
||||
void MultitapProtocol::Select()
|
||||
void MultitapProtocol::Select(MultitapMode mode)
|
||||
{
|
||||
MT_LOG.WriteLn("%s", __FUNCTION__);
|
||||
const u8 newSlot = g_Sio2FifoIn.front();
|
||||
g_Sio2FifoIn.pop_front();
|
||||
const bool isInBounds = (newSlot < SIO::SLOTS);
|
||||
|
||||
if (isInBounds)
|
||||
|
||||
if (EmuConfig.Pad.IsMultitapPortEnabled(g_Sio2.port))
|
||||
{
|
||||
g_Sio2.slot = newSlot;
|
||||
MT_LOG.WriteLn("Slot changed to %d", g_Sio2.slot);
|
||||
}
|
||||
const u8 newSlot = g_Sio2FifoIn.front();
|
||||
g_Sio2FifoIn.pop_front();
|
||||
const bool isInBounds = (newSlot < SIO::SLOTS);
|
||||
|
||||
g_Sio2FifoOut.push_back(0x5a);
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
g_Sio2FifoOut.push_back(isInBounds ? newSlot : 0xff);
|
||||
g_Sio2FifoOut.push_back(isInBounds ? 0x5a : 0x66);
|
||||
if (isInBounds)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case MultitapMode::SELECT_PAD:
|
||||
this->currentPadSlot = newSlot;
|
||||
break;
|
||||
case MultitapMode::SELECT_MEMCARD:
|
||||
this->currentMemcardSlot = newSlot;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MT_LOG.WriteLn("Slot changed to %d", newSlot);
|
||||
}
|
||||
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0x80);
|
||||
g_Sio2FifoOut.push_back(0x5a);
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
g_Sio2FifoOut.push_back(isInBounds ? newSlot : 0xff);
|
||||
g_Sio2FifoOut.push_back(isInBounds ? 0x5a : 0x66);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
}
|
||||
}
|
||||
|
||||
MultitapProtocol::MultitapProtocol() = default;
|
||||
|
@ -65,14 +110,24 @@ void MultitapProtocol::FullReset()
|
|||
{
|
||||
SoftReset();
|
||||
|
||||
g_Sio2.slot = 0;
|
||||
this->currentPadSlot = 0;
|
||||
this->currentMemcardSlot = 0;
|
||||
}
|
||||
|
||||
u8 MultitapProtocol::GetPadSlot()
|
||||
{
|
||||
return this->currentPadSlot;
|
||||
}
|
||||
|
||||
u8 MultitapProtocol::GetMemcardSlot()
|
||||
{
|
||||
return this->currentMemcardSlot;
|
||||
}
|
||||
|
||||
void MultitapProtocol::SendToMultitap()
|
||||
{
|
||||
const u8 commandByte = g_Sio2FifoIn.front();
|
||||
g_Sio2FifoIn.pop_front();
|
||||
g_Sio2FifoOut.push_back(0x80);
|
||||
|
||||
switch (static_cast<MultitapMode>(commandByte))
|
||||
{
|
||||
|
@ -81,8 +136,10 @@ void MultitapProtocol::SendToMultitap()
|
|||
SupportCheck();
|
||||
break;
|
||||
case MultitapMode::SELECT_PAD:
|
||||
Select(MultitapMode::SELECT_PAD);
|
||||
break;
|
||||
case MultitapMode::SELECT_MEMCARD:
|
||||
Select();
|
||||
Select(MultitapMode::SELECT_MEMCARD);
|
||||
break;
|
||||
default:
|
||||
DevCon.Warning("%s() Unhandled MultitapMode (%02X)", __FUNCTION__, commandByte);
|
||||
|
|
|
@ -27,8 +27,11 @@ enum class MultitapMode
|
|||
class MultitapProtocol
|
||||
{
|
||||
private:
|
||||
u8 currentPadSlot = 0;
|
||||
u8 currentMemcardSlot = 0;
|
||||
|
||||
void SupportCheck();
|
||||
void Select();
|
||||
void Select(MultitapMode mode);
|
||||
|
||||
public:
|
||||
MultitapProtocol();
|
||||
|
@ -37,7 +40,12 @@ public:
|
|||
void SoftReset();
|
||||
void FullReset();
|
||||
|
||||
u8 GetPadSlot();
|
||||
u8 GetMemcardSlot();
|
||||
|
||||
void SendToMultitap();
|
||||
};
|
||||
|
||||
extern MultitapProtocol g_MultitapProtocol;
|
||||
extern MultitapProtocol g_MultitapPort0;
|
||||
extern MultitapProtocol g_MultitapPort1;
|
||||
|
||||
|
|
|
@ -117,5 +117,5 @@ u8 PadNotConnected::GetPressure(u32 index) const
|
|||
|
||||
u8 PadNotConnected::SendCommandByte(u8 commandByte)
|
||||
{
|
||||
return 0x00;
|
||||
return 0xff;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,6 @@ bool Sio2::Initialize()
|
|||
iStat = 0;
|
||||
|
||||
port = 0;
|
||||
slot = 0;
|
||||
|
||||
while (!g_Sio2FifoOut.empty())
|
||||
{
|
||||
|
@ -108,6 +107,9 @@ void Sio2::SoftReset()
|
|||
{
|
||||
g_Sio2FifoIn.pop_front();
|
||||
}
|
||||
|
||||
// RECV1 should always be reassembled based on the devices being probed by the packet.
|
||||
recv1 = 0;
|
||||
}
|
||||
|
||||
void Sio2::Interrupt()
|
||||
|
@ -142,12 +144,38 @@ void Sio2::SetRecv1(u32 value)
|
|||
|
||||
void Sio2::Pad()
|
||||
{
|
||||
// Send PAD our current port, and get back whatever it says the first response byte should be.
|
||||
PadBase* pad = Pad::GetPad(port, slot);
|
||||
MultitapProtocol& mtap = (port ? g_MultitapPort1 : g_MultitapPort0);
|
||||
|
||||
// Send PAD our current port, and get back whatever it says the first response byte should be.
|
||||
PadBase* pad = Pad::GetPad(port, mtap.GetPadSlot());
|
||||
|
||||
// Update the third nibble with which ports have been accessed
|
||||
if (this->recv1 & Recv1::ONE_PORT_OPEN)
|
||||
{
|
||||
this->recv1 &= ~(Recv1::ONE_PORT_OPEN);
|
||||
this->recv1 |= Recv1::TWO_PORTS_OPEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->recv1 |= Recv1::ONE_PORT_OPEN;
|
||||
}
|
||||
|
||||
// This bit is always set, whether the pad is present or missing
|
||||
this->recv1 |= Recv1::NO_DEVICES_MISSING;
|
||||
|
||||
// If the currently accessed pad is missing, also tick those bits
|
||||
if (pad->GetType() == Pad::ControllerType::NotConnected)
|
||||
{
|
||||
if (!port)
|
||||
{
|
||||
this->recv1 |= Recv1::PORT_1_MISSING;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->recv1 |= Recv1::PORT_2_MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
// RECV1 is set once per DMA; if any device is present at all, it should be set to connected.
|
||||
// For now, we will always report connected for pads.
|
||||
SetRecv1(Recv1::CONNECTED);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
|
||||
// Then for every byte in g_Sio2FifoIn, pass to PAD and see what it kicks back to us.
|
||||
|
@ -164,21 +192,48 @@ void Sio2::Pad()
|
|||
|
||||
void Sio2::Multitap()
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
|
||||
const bool multitapEnabled = EmuConfig.Pad.IsMultitapPortEnabled(port);
|
||||
SetRecv1(multitapEnabled ? Recv1::CONNECTED : Recv1::DISCONNECTED);
|
||||
|
||||
if (multitapEnabled)
|
||||
const bool multitapEnabled = EmuConfig.Pad.IsMultitapPortEnabled(this->port);
|
||||
|
||||
// Update the third nibble with which ports have been accessed
|
||||
if (this->recv1 & Recv1::ONE_PORT_OPEN)
|
||||
{
|
||||
g_MultitapProtocol.SendToMultitap();
|
||||
this->recv1 &= ~(Recv1::ONE_PORT_OPEN);
|
||||
this->recv1 |= Recv1::TWO_PORTS_OPEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (g_Sio2FifoOut.size() < commandLength)
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
}
|
||||
this->recv1 |= Recv1::ONE_PORT_OPEN;
|
||||
}
|
||||
|
||||
// This bit is always set, whether the pad is present or missing
|
||||
this->recv1 |= Recv1::NO_DEVICES_MISSING;
|
||||
|
||||
// If the currently accessed pad is missing, also tick those bits.
|
||||
// MTAPMAN is special - at least, the variant found in Gauntlet: Dark Legacy.
|
||||
//
|
||||
// For PADMAN and pads, the bits represented by PORT_1_MISSING and PORT_2_MISSING
|
||||
// are always faithful - suppose your game only opened port 2 for some reason,
|
||||
// then a disconnect value would look like 0x0002D100.
|
||||
//
|
||||
// MTAPMAN however does not check the bit set by 0x00020000. It only checks the bit
|
||||
// set by 0x00010000. So even if port 2 is being addressed, RECV1 should be 0x0001D100
|
||||
// (or 0x0001D200 if there are both ports being accessed in that packet).
|
||||
if (!multitapEnabled)
|
||||
{
|
||||
this->recv1 |= Recv1::PORT_1_MISSING;
|
||||
}
|
||||
|
||||
switch (this->port)
|
||||
{
|
||||
case 0:
|
||||
g_MultitapPort0.SendToMultitap();
|
||||
break;
|
||||
case 1:
|
||||
g_MultitapPort1.SendToMultitap();
|
||||
break;
|
||||
default:
|
||||
Console.Warning("%s() Port value from SEND3 out of bounds! (%d)", __FUNCTION__, this->port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +252,9 @@ void Sio2::Infrared()
|
|||
|
||||
void Sio2::Memcard()
|
||||
{
|
||||
mcd = &mcds[port][slot];
|
||||
MultitapProtocol& mtap = (port ? g_MultitapPort1 : g_MultitapPort0);
|
||||
|
||||
mcd = &mcds[port][mtap.GetMemcardSlot()];
|
||||
|
||||
// Check if auto ejection is active. If so, set RECV1 to DISCONNECTED,
|
||||
// and zero out the fifo to simulate dead air over the wire.
|
||||
|
@ -221,10 +278,7 @@ void Sio2::Memcard()
|
|||
g_Sio2FifoIn.pop_front();
|
||||
const u8 responseByte = mcd->IsPresent() ? 0x00 : 0xff;
|
||||
g_Sio2FifoOut.push_back(responseByte);
|
||||
// Technically, the FLAG byte is only for PS1 memcards. However,
|
||||
// since this response byte is still a dud on PS2 memcards, we can
|
||||
// basically just cheat and always make this our second response byte for memcards.
|
||||
g_Sio2FifoOut.push_back(mcd->FLAG);
|
||||
g_Sio2FifoOut.push_back(responseByte);
|
||||
u8 ps1Input = 0;
|
||||
u8 ps1Output = 0;
|
||||
|
||||
|
@ -398,7 +452,7 @@ void Sio2::Write(u8 data)
|
|||
break;
|
||||
default:
|
||||
Console.Error("%s(%02X) Unhandled SIO mode %02X", __FUNCTION__, data, sioMode);
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
SetRecv1(Recv1::DISCONNECTED);
|
||||
break;
|
||||
}
|
||||
|
@ -423,7 +477,7 @@ void Sio2::Write(u8 data)
|
|||
|
||||
u8 Sio2::Read()
|
||||
{
|
||||
u8 ret = 0x00;
|
||||
u8 ret = 0xff;
|
||||
|
||||
if (!g_Sio2FifoOut.empty())
|
||||
{
|
||||
|
@ -432,7 +486,7 @@ u8 Sio2::Read()
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.Warning("%s() g_Sio2FifoOut underflow! Returning 0x00.", __FUNCTION__);
|
||||
Console.Warning("%s() g_Sio2FifoOut underflow! Returning 0xff.", __FUNCTION__);
|
||||
}
|
||||
|
||||
Sio2Log.WriteLn("%s() SIO2 DATA Read (%02X)", __FUNCTION__, ret);
|
||||
|
|
|
@ -130,10 +130,21 @@ namespace Sio2Ctrl
|
|||
static constexpr u32 SIO2MAN_RESET = 0x000003bc;
|
||||
} // namespace Sio2Ctrl
|
||||
|
||||
// TODO: Remove deprecated options once memcards are no longer using them.
|
||||
namespace Recv1
|
||||
{
|
||||
// Deprecated
|
||||
static constexpr u32 DISCONNECTED = 0x1d100;
|
||||
// Deprecated
|
||||
static constexpr u32 CONNECTED = 0x1100;
|
||||
|
||||
static constexpr u32 NO_DEVICES_MISSING = 0x1000;
|
||||
static constexpr u32 PORT_1_MISSING = 0x1D000;
|
||||
static constexpr u32 PORT_2_MISSING = 0x2D000;
|
||||
static constexpr u32 BOTH_PORTS_MISSING = 0x3D000;
|
||||
static constexpr u32 ONE_PORT_OPEN = 0x100;
|
||||
static constexpr u32 TWO_PORTS_OPEN = 0x200;
|
||||
|
||||
} // namespace Recv1
|
||||
|
||||
namespace Recv2
|
||||
|
|
Loading…
Reference in New Issue