diff --git a/src/pse/digital_controller.cpp b/src/pse/digital_controller.cpp index d2a550031..b5fc156f7 100644 --- a/src/pse/digital_controller.cpp +++ b/src/pse/digital_controller.cpp @@ -25,7 +25,7 @@ bool DigitalController::Transfer(const u8 data_in, u8* data_out) Log_DebugPrintf("Access"); // response is hi-z - *data_out = data_in; + *data_out = 0xFF; ack = true; } break; diff --git a/src/pse/pad.cpp b/src/pse/pad.cpp index d05c2ccca..1658b03ff 100644 --- a/src/pse/pad.cpp +++ b/src/pse/pad.cpp @@ -3,14 +3,16 @@ #include "common/state_wrapper.h" #include "interrupt_controller.h" #include "pad_device.h" +#include "system.h" Log_SetChannel(Pad); Pad::Pad() = default; Pad::~Pad() = default; -bool Pad::Initialize(InterruptController* interrupt_controller) +bool Pad::Initialize(System* system, InterruptController* interrupt_controller) { + m_system = system; m_interrupt_controller = interrupt_controller; return true; } @@ -22,6 +24,8 @@ void Pad::Reset() bool Pad::DoState(StateWrapper& sw) { + sw.Do(&m_state); + sw.Do(&m_ticks_remaining); sw.Do(&m_JOY_CTRL.bits); sw.Do(&m_JOY_STAT.bits); sw.Do(&m_JOY_MODE.bits); @@ -49,7 +53,11 @@ u32 Pad::ReadRegister(u32 offset) } case 0x04: // JOY_STAT - return m_JOY_STAT.bits; + { + const u32 bits = m_JOY_STAT.bits; + m_JOY_STAT.ACKINPUT = false; + return bits; + } case 0x08: // JOY_MODE return ZeroExtend32(m_JOY_MODE.bits); @@ -78,8 +86,8 @@ void Pad::WriteRegister(u32 offset, u32 value) m_TX_FIFO.Push(Truncate8(value)); - if (m_JOY_CTRL.SELECT) - DoTransfer(); + if (!IsTransmitting() && CanTransfer()) + BeginTransfer(); return; } @@ -96,13 +104,19 @@ void Pad::WriteRegister(u32 offset, u32 value) if (m_JOY_CTRL.ACK) { // reset stat bits - m_JOY_STAT.ACKINPUTLEVEL = false; m_JOY_STAT.INTR = false; - m_JOY_CTRL.ACK = true; } - if (!old_select && m_JOY_CTRL.SELECT && !m_TX_FIFO.IsEmpty()) - DoTransfer(); + if (!m_JOY_CTRL.SELECT || !m_JOY_CTRL.TXEN) + { + if (IsTransmitting()) + EndTransfer(); + } + else + { + if (!IsTransmitting() && CanTransfer()) + BeginTransfer(); + } return; } @@ -126,8 +140,28 @@ void Pad::WriteRegister(u32 offset, u32 value) } } +void Pad::Execute(TickCount ticks) +{ + switch (m_state) + { + case State::Idle: + break; + + case State::Transmitting: + { + m_ticks_remaining -= ticks; + if (m_ticks_remaining <= 0) + DoTransfer(); + } + break; + } +} + void Pad::SoftReset() { + if (IsTransmitting()) + EndTransfer(); + m_JOY_CTRL.bits = 0; m_JOY_STAT.bits = 0; m_JOY_MODE.bits = 0; @@ -143,35 +177,84 @@ void Pad::UpdateJoyStat() m_JOY_STAT.TXRDY = !m_TX_FIFO.IsFull(); } +void Pad::BeginTransfer() +{ + DebugAssert(m_state == State::Idle && CanTransfer()); + Log_DebugPrintf("Starting transfer"); + + m_JOY_CTRL.RXEN = true; + + // 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: + // 1) Sets the control register up for transmitting, interrupt on ACK. + // 2) Writes 0x01 to the TX FIFO. + // 3) Delays for a bit. + // 4) Writes ACK to the control register, clearing the interrupt flag. + // 5) Clears IRQ7 in the interrupt controller. + // 6) Waits until the RX FIFO is not empty, reads the first byte to $zero. + // 7) Checks if the interrupt status register had IRQ7 set. If not, no device connected. + // + // Performing the transfer immediately will result in both the INTR bit and the bit in the interrupt + // controller being discarded in (4)/(5), but this bit was set by the *new* transfer. Therefore, the + // test in (7) will fail, and it won't send any more data. So, the transfer/interrupt must be delayed + // until after (4) and (5) have been completed. + + m_system->Synchronize(); + m_state = State::Transmitting; + m_ticks_remaining = TRANSFER_TICKS; + m_system->SetDowncount(m_ticks_remaining); +} + void Pad::DoTransfer() { Log_DebugPrintf("Transferring slot %d", m_JOY_CTRL.SLOT.GetValue()); const std::shared_ptr& dev = m_devices[m_JOY_CTRL.SLOT]; - if (!dev) + if (!dev || !CanTransfer()) { // no device present, don't set ACK and read hi-z m_TX_FIFO.Clear(); m_RX_FIFO.Clear(); m_RX_FIFO.Push(0xFF); UpdateJoyStat(); + EndTransfer(); return; } - while (!m_TX_FIFO.IsEmpty()) - { - const u8 data_out = m_TX_FIFO.Pop(); - u8 data_in; - m_JOY_STAT.ACKINPUTLEVEL |= dev->Transfer(data_out, &data_in); - m_RX_FIFO.Push(data_in); - m_JOY_CTRL.RXEN = true; - } + // set rx? + m_JOY_CTRL.RXEN = true; - if (m_JOY_STAT.ACKINPUTLEVEL && m_JOY_CTRL.ACKINTEN) + const u8 data_out = m_TX_FIFO.Pop(); + u8 data_in; + m_JOY_STAT.ACKINPUT |= dev->Transfer(data_out, &data_in); + m_RX_FIFO.Push(data_in); + + if (m_JOY_STAT.ACKINPUT && m_JOY_CTRL.ACKINTEN) { + Log_DebugPrintf("Triggering interrupt"); m_JOY_STAT.INTR = true; m_interrupt_controller->InterruptRequest(InterruptController::IRQ::IRQ7); } + if (m_TX_FIFO.IsEmpty()) + { + EndTransfer(); + } + else + { + // queue the next byte + m_ticks_remaining += TRANSFER_TICKS; + m_system->SetDowncount(m_ticks_remaining); + } + UpdateJoyStat(); } + +void Pad::EndTransfer() +{ + DebugAssert(m_state == State::Transmitting); + Log_DebugPrintf("Ending transfer"); + + m_state = State::Idle; + m_ticks_remaining = 0; +} diff --git a/src/pse/pad.h b/src/pse/pad.h index 23235ba65..e45b492f4 100644 --- a/src/pse/pad.h +++ b/src/pse/pad.h @@ -7,6 +7,7 @@ class StateWrapper; +class System; class InterruptController; class PadDevice; @@ -16,7 +17,7 @@ public: Pad(); ~Pad(); - bool Initialize(InterruptController* interrupt_controller); + bool Initialize(System* system, InterruptController* interrupt_controller); void Reset(); bool DoState(StateWrapper& sw); @@ -26,8 +27,17 @@ public: u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); + void Execute(TickCount ticks); + private: static constexpr u32 NUM_SLOTS = 2; + static constexpr u32 TRANSFER_TICKS = 500; + + enum class State : u32 + { + Idle, + Transmitting + }; union JOY_CTRL { @@ -52,7 +62,6 @@ private: BitField TXRDY; BitField RXFIFONEMPTY; BitField TXDONE; - BitField ACKINPUTLEVEL; BitField ACKINPUT; BitField INTR; BitField TMR; @@ -69,12 +78,24 @@ private: BitField clk_polarity; }; + bool IsTransmitting() const { return m_state == State::Transmitting; } + bool CanTransfer() const + { + return !m_TX_FIFO.IsEmpty() && !m_RX_FIFO.IsFull() && m_JOY_CTRL.SELECT && m_JOY_CTRL.TXEN; + } + void SoftReset(); void UpdateJoyStat(); + void BeginTransfer(); void DoTransfer(); + void EndTransfer(); + System* m_system = nullptr; InterruptController* m_interrupt_controller = nullptr; + State m_state = State::Idle; + TickCount m_ticks_remaining = 0; + JOY_CTRL m_JOY_CTRL = {}; JOY_STAT m_JOY_STAT = {}; JOY_MODE m_JOY_MODE = {}; diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 35f21ced6..18bbccad4 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -49,7 +49,7 @@ bool System::Initialize() if (!m_cdrom->Initialize(this, m_dma.get(), m_interrupt_controller.get())) return false; - if (!m_pad->Initialize(m_interrupt_controller.get())) + if (!m_pad->Initialize(this, m_interrupt_controller.get())) return false; if (!m_timers->Initialize(this, m_interrupt_controller.get())) @@ -219,6 +219,7 @@ void System::Synchronize() m_gpu->Execute(pending_ticks); m_timers->AddSystemTicks(pending_ticks); m_cdrom->Execute(pending_ticks); + m_pad->Execute(pending_ticks); } void System::SetDowncount(TickCount downcount)