Pad: Only buffer a single byte

Fixes Croc 2 memory card access freezing.
This commit is contained in:
Connor McLaughlin 2019-11-12 01:13:08 +10:00
parent d8452d7d7d
commit 6a82333d8f
2 changed files with 48 additions and 44 deletions

View file

@ -87,8 +87,10 @@ bool Pad::DoState(StateWrapper& sw)
sw.Do(&m_JOY_STAT.bits); sw.Do(&m_JOY_STAT.bits);
sw.Do(&m_JOY_MODE.bits); sw.Do(&m_JOY_MODE.bits);
sw.Do(&m_JOY_BAUD); sw.Do(&m_JOY_BAUD);
sw.Do(&m_RX_FIFO); sw.Do(&m_receive_buffer);
sw.Do(&m_TX_FIFO); sw.Do(&m_transmit_buffer);
sw.Do(&m_receive_buffer_full);
sw.Do(&m_transmit_buffer_full);
return !sw.HasError(); return !sw.HasError();
} }
@ -98,16 +100,16 @@ u32 Pad::ReadRegister(u32 offset)
{ {
case 0x00: // JOY_DATA case 0x00: // JOY_DATA
{ {
if (m_RX_FIFO.IsEmpty()) if (!m_transmit_buffer_full)
{
Log_DevPrintf("Read from RX fifo when empty"); Log_DevPrintf("Read from RX fifo when empty");
return 0;
}
const u8 value = m_RX_FIFO.Pop(); const u8 value = m_receive_buffer;
m_receive_buffer_full = false;
UpdateJoyStat(); UpdateJoyStat();
Log_DebugPrintf("JOY_DATA (R) -> 0x%02X", ZeroExtend32(value)); Log_DebugPrintf("JOY_DATA (R) -> 0x%02X", ZeroExtend32(value));
return ZeroExtend32(value); return (ZeroExtend32(value) | (ZeroExtend32(value) << 8) | (ZeroExtend32(value) << 16) |
(ZeroExtend32(value) << 24));
} }
case 0x04: // JOY_STAT case 0x04: // JOY_STAT
@ -139,13 +141,12 @@ void Pad::WriteRegister(u32 offset, u32 value)
case 0x00: // JOY_DATA case 0x00: // JOY_DATA
{ {
Log_DebugPrintf("JOY_DATA (W) <- 0x%02X", value); Log_DebugPrintf("JOY_DATA (W) <- 0x%02X", value);
if (m_TX_FIFO.IsFull())
{
Log_WarningPrint("TX FIFO overrun");
m_TX_FIFO.RemoveOne();
}
m_TX_FIFO.Push(Truncate8(value)); if (m_transmit_buffer_full)
Log_WarningPrint("TX FIFO overrun");
m_transmit_buffer = Truncate8(value);
m_transmit_buffer_full = true;
if (!IsTransmitting() && CanTransfer()) if (!IsTransmitting() && CanTransfer())
BeginTransfer(); BeginTransfer();
@ -182,6 +183,7 @@ void Pad::WriteRegister(u32 offset, u32 value)
BeginTransfer(); BeginTransfer();
} }
UpdateJoyStat();
return; return;
} }
@ -232,17 +234,19 @@ void Pad::SoftReset()
m_JOY_CTRL.bits = 0; m_JOY_CTRL.bits = 0;
m_JOY_STAT.bits = 0; m_JOY_STAT.bits = 0;
m_JOY_MODE.bits = 0; m_JOY_MODE.bits = 0;
m_RX_FIFO.Clear(); m_receive_buffer = 0;
m_TX_FIFO.Clear(); m_receive_buffer_full = false;
m_transmit_buffer = 0;
m_transmit_buffer_full = false;
ResetDeviceTransferState(); ResetDeviceTransferState();
UpdateJoyStat(); UpdateJoyStat();
} }
void Pad::UpdateJoyStat() void Pad::UpdateJoyStat()
{ {
m_JOY_STAT.RXFIFONEMPTY = !m_RX_FIFO.IsEmpty(); m_JOY_STAT.RXFIFONEMPTY = m_receive_buffer_full;
m_JOY_STAT.TXDONE = m_TX_FIFO.IsEmpty(); m_JOY_STAT.TXDONE = !m_transmit_buffer_full && m_state == State::Idle;
m_JOY_STAT.TXRDY = !m_TX_FIFO.IsFull(); m_JOY_STAT.TXRDY = !m_transmit_buffer_full;
} }
void Pad::BeginTransfer() void Pad::BeginTransfer()
@ -251,6 +255,8 @@ void Pad::BeginTransfer()
Log_DebugPrintf("Starting transfer"); Log_DebugPrintf("Starting transfer");
m_JOY_CTRL.RXEN = true; m_JOY_CTRL.RXEN = true;
m_transmit_value = m_transmit_buffer;
m_transmit_buffer_full = false;
// The transfer or the interrupt must be delayed, otherwise the BIOS thinks there's no device detected. // The transfer or the interrupt must be delayed, otherwise the BIOS thinks there's no device detected.
// It seems to do something resembling the following: // It seems to do something resembling the following:
@ -283,7 +289,8 @@ void Pad::DoTransfer()
// set rx? // set rx?
m_JOY_CTRL.RXEN = true; m_JOY_CTRL.RXEN = true;
const u8 data_out = m_TX_FIFO.Pop(); const u8 data_out = m_transmit_value;
u8 data_in = 0xFF; u8 data_in = 0xFF;
bool ack = false; bool ack = false;
@ -296,19 +303,19 @@ void Pad::DoTransfer()
if (!memory_card || (ack = memory_card->Transfer(data_out, &data_in)) == false) if (!memory_card || (ack = memory_card->Transfer(data_out, &data_in)) == false)
{ {
// nothing connected to this port // nothing connected to this port
Log_DebugPrintf("Nothing connected or ACK'ed"); Log_TracePrintf("Nothing connected or ACK'ed");
} }
else else
{ {
// memory card responded, make it the active device until non-ack // memory card responded, make it the active device until non-ack
Log_DebugPrintf("Transfer to memory card, data_out=0x%02X, data_in=0x%02X", data_in, data_out); Log_TracePrintf("Transfer to memory card, data_out=0x%02X, data_in=0x%02X", data_out, data_in);
m_active_device = ActiveDevice::MemoryCard; m_active_device = ActiveDevice::MemoryCard;
} }
} }
else else
{ {
// controller responded, make it the active device until non-ack // controller responded, make it the active device until non-ack
Log_DebugPrintf("Transfer to controller, data_out=0x%02X, data_in=0x%02X", data_in, data_out); Log_TracePrintf("Transfer to controller, data_out=0x%02X, data_in=0x%02X", data_out, data_in);
m_active_device = ActiveDevice::Controller; m_active_device = ActiveDevice::Controller;
} }
} }
@ -317,19 +324,26 @@ void Pad::DoTransfer()
case ActiveDevice::Controller: case ActiveDevice::Controller:
{ {
if (controller) if (controller)
{
ack = controller->Transfer(data_out, &data_in); ack = controller->Transfer(data_out, &data_in);
Log_TracePrintf("Transfer to controller, data_out=0x%02X, data_in=0x%02X", data_out, data_in);
}
} }
break; break;
case ActiveDevice::MemoryCard: case ActiveDevice::MemoryCard:
{ {
if (memory_card) if (memory_card)
{
ack = memory_card->Transfer(data_out, &data_in); ack = memory_card->Transfer(data_out, &data_in);
Log_TracePrintf("Transfer to memory card, data_out=0x%02X, data_in=0x%02X", data_out, data_in);
}
} }
break; break;
} }
m_RX_FIFO.Push(data_in); m_receive_buffer = data_in;
m_receive_buffer_full = true;
m_JOY_STAT.ACKINPUT |= ack; m_JOY_STAT.ACKINPUT |= ack;
// device no longer active? // device no longer active?
@ -343,17 +357,7 @@ void Pad::DoTransfer()
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::IRQ7); m_interrupt_controller->InterruptRequest(InterruptController::IRQ::IRQ7);
} }
if (m_TX_FIFO.IsEmpty())
{
EndTransfer(); EndTransfer();
}
else
{
// queue the next byte
m_ticks_remaining += GetTransferTicks();
m_system->SetDowncount(m_ticks_remaining);
}
UpdateJoyStat(); UpdateJoyStat();
} }

View file

@ -89,10 +89,7 @@ private:
}; };
bool IsTransmitting() const { return m_state == State::Transmitting; } bool IsTransmitting() const { return m_state == State::Transmitting; }
bool CanTransfer() const bool CanTransfer() const { return m_transmit_buffer_full && m_JOY_CTRL.SELECT && m_JOY_CTRL.TXEN; }
{
return !m_TX_FIFO.IsEmpty() && !m_RX_FIFO.IsFull() && m_JOY_CTRL.SELECT && m_JOY_CTRL.TXEN;
}
TickCount GetTransferTicks() const { return static_cast<TickCount>(ZeroExtend32(m_JOY_BAUD) * 8); } TickCount GetTransferTicks() const { return static_cast<TickCount>(ZeroExtend32(m_JOY_BAUD) * 8); }
@ -106,6 +103,9 @@ private:
System* m_system = nullptr; System* m_system = nullptr;
InterruptController* m_interrupt_controller = nullptr; InterruptController* m_interrupt_controller = nullptr;
std::array<std::shared_ptr<PadDevice>, NUM_SLOTS> m_controllers;
std::array<std::shared_ptr<MemoryCard>, NUM_SLOTS> m_memory_cards;
State m_state = State::Idle; State m_state = State::Idle;
TickCount m_ticks_remaining = 0; TickCount m_ticks_remaining = 0;
@ -115,9 +115,9 @@ private:
u16 m_JOY_BAUD = 0; u16 m_JOY_BAUD = 0;
ActiveDevice m_active_device = ActiveDevice::None; ActiveDevice m_active_device = ActiveDevice::None;
InlineFIFOQueue<u8, 8> m_RX_FIFO; u8 m_receive_buffer = 0;
InlineFIFOQueue<u8, 2> m_TX_FIFO; u8 m_transmit_buffer = 0;
u8 m_transmit_value = 0;
std::array<std::shared_ptr<PadDevice>, NUM_SLOTS> m_controllers; bool m_receive_buffer_full = false;
std::array<std::shared_ptr<MemoryCard>, NUM_SLOTS> m_memory_cards; bool m_transmit_buffer_full = false;
}; };