mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-10-23 23:55:42 +00:00
173 lines
3.4 KiB
C++
173 lines
3.4 KiB
C++
|
#include "pad.h"
|
||
|
#include "YBaseLib/Log.h"
|
||
|
#include "common/state_wrapper.h"
|
||
|
#include "interrupt_controller.h"
|
||
|
#include "pad_device.h"
|
||
|
Log_SetChannel(Pad);
|
||
|
|
||
|
Pad::Pad() = default;
|
||
|
|
||
|
Pad::~Pad() = default;
|
||
|
|
||
|
bool Pad::Initialize(InterruptController* interrupt_controller)
|
||
|
{
|
||
|
m_interrupt_controller = interrupt_controller;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Pad::Reset()
|
||
|
{
|
||
|
SoftReset();
|
||
|
}
|
||
|
|
||
|
bool Pad::DoState(StateWrapper& sw)
|
||
|
{
|
||
|
return !sw.HasError();
|
||
|
}
|
||
|
|
||
|
u32 Pad::ReadRegister(u32 offset)
|
||
|
{
|
||
|
switch (offset)
|
||
|
{
|
||
|
case 0x00: // JOY_DATA
|
||
|
{
|
||
|
if (m_RX_FIFO.IsEmpty())
|
||
|
{
|
||
|
Log_WarningPrint("Read from RX fifo when empty");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const u8 value = m_RX_FIFO.Pop();
|
||
|
UpdateJoyStat();
|
||
|
Log_DebugPrintf("JOY_DATA (R) -> 0x%02X", ZeroExtend32(value));
|
||
|
return ZeroExtend32(value);
|
||
|
}
|
||
|
|
||
|
case 0x04: // JOY_STAT
|
||
|
return m_JOY_STAT.bits;
|
||
|
|
||
|
case 0x08: // JOY_MODE
|
||
|
return ZeroExtend32(m_JOY_MODE.bits);
|
||
|
|
||
|
case 0x0A: // JOY_CTRL
|
||
|
return ZeroExtend32(m_JOY_CTRL.bits);
|
||
|
|
||
|
default:
|
||
|
Log_ErrorPrintf("Unknown register read: 0x%X", offset);
|
||
|
return UINT32_C(0xFFFFFFFF);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Pad::WriteRegister(u32 offset, u32 value)
|
||
|
{
|
||
|
switch (offset)
|
||
|
{
|
||
|
case 0x00: // JOY_DATA
|
||
|
{
|
||
|
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_JOY_CTRL.SELECT)
|
||
|
DoTransfer();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
case 0x0A: // JOY_CTRL
|
||
|
{
|
||
|
Log_DebugPrintf("JOY_CTRL <- 0x%04X", value);
|
||
|
const bool old_select = m_JOY_CTRL.SELECT;
|
||
|
|
||
|
m_JOY_CTRL.bits = Truncate16(value);
|
||
|
if (m_JOY_CTRL.RESET)
|
||
|
SoftReset();
|
||
|
|
||
|
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();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
case 0x08: // JOY_MODE
|
||
|
{
|
||
|
Log_DebugPrintf("JOY_MODE <- 0x%08X", value);
|
||
|
m_JOY_MODE.bits = Truncate16(value);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
case 0x0E:
|
||
|
{
|
||
|
Log_WarningPrintf("JOY_BAUD <- 0x%08X", value);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
Log_ErrorPrintf("Unknown register write: 0x%X <- 0x%08X", offset, value);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Pad::SoftReset()
|
||
|
{
|
||
|
m_JOY_CTRL.bits = 0;
|
||
|
m_JOY_STAT.bits = 0;
|
||
|
m_JOY_MODE.bits = 0;
|
||
|
m_RX_FIFO.Clear();
|
||
|
m_TX_FIFO.Clear();
|
||
|
UpdateJoyStat();
|
||
|
}
|
||
|
|
||
|
void Pad::UpdateJoyStat()
|
||
|
{
|
||
|
m_JOY_STAT.RXFIFONEMPTY = !m_RX_FIFO.IsEmpty();
|
||
|
m_JOY_STAT.TXDONE = m_TX_FIFO.IsEmpty();
|
||
|
m_JOY_STAT.TXRDY = !m_TX_FIFO.IsFull();
|
||
|
}
|
||
|
|
||
|
void Pad::DoTransfer()
|
||
|
{
|
||
|
Log_DebugPrintf("Transferring slot %d", m_JOY_CTRL.SLOT.GetValue());
|
||
|
|
||
|
const std::shared_ptr<PadDevice>& dev = m_devices[m_JOY_CTRL.SLOT];
|
||
|
if (!dev)
|
||
|
{
|
||
|
// 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();
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
if (m_JOY_STAT.ACKINPUTLEVEL && m_JOY_CTRL.ACKINTEN)
|
||
|
{
|
||
|
m_JOY_STAT.INTR = true;
|
||
|
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::IRQ7);
|
||
|
}
|
||
|
|
||
|
UpdateJoyStat();
|
||
|
}
|