Duckstation/src/core/pad.cpp

530 lines
14 KiB
C++
Raw Normal View History

#include "pad.h"
2020-01-10 03:31:12 +00:00
#include "common/log.h"
#include "common/state_wrapper.h"
#include "controller.h"
2019-10-27 06:45:23 +00:00
#include "host_interface.h"
#include "interrupt_controller.h"
2019-10-27 06:45:23 +00:00
#include "memory_card.h"
2019-09-22 15:25:58 +00:00
#include "system.h"
Log_SetChannel(Pad);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
Pad g_pad;
Pad::Pad() = default;
Pad::~Pad() = default;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
void Pad::Initialize()
{
m_transfer_event = TimingEvents::CreateTimingEvent(
"Pad Serial Transfer", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<Pad*>(param)->TransferEvent(ticks_late); },
this, false);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
Reset();
}
void Pad::Shutdown()
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
m_transfer_event.reset();
for (u32 i = 0; i < NUM_SLOTS; i++)
{
m_controllers[i].reset();
m_memory_cards[i].reset();
}
}
void Pad::Reset()
{
SoftReset();
2019-09-29 15:59:35 +00:00
for (u32 i = 0; i < NUM_SLOTS; i++)
{
if (m_controllers[i])
m_controllers[i]->Reset();
if (m_memory_cards[i])
m_memory_cards[i]->Reset();
}
}
bool Pad::DoState(StateWrapper& sw)
{
2019-09-29 15:59:35 +00:00
for (u32 i = 0; i < NUM_SLOTS; i++)
{
ControllerType controller_type = m_controllers[i] ? m_controllers[i]->GetType() : ControllerType::None;
ControllerType state_controller_type = controller_type;
sw.Do(&state_controller_type);
if (g_settings.load_devices_from_save_states)
2019-09-29 15:59:35 +00:00
{
if (controller_type != state_controller_type)
{
if (g_settings.load_devices_from_save_states)
{
g_host_interface->AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString(
"OSDMessage", "Save state contains controller type %s in port %u, but %s is used. Switching."),
Settings::GetControllerTypeName(state_controller_type), i + 1u,
Settings::GetControllerTypeName(controller_type));
m_controllers[i].reset();
if (state_controller_type != ControllerType::None)
{
m_controllers[i] = Controller::Create(state_controller_type, i);
if (!sw.DoMarker("Controller") || !m_controllers[i]->DoState(sw, false))
return false;
}
}
else
{
g_host_interface->AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
Settings::GetControllerTypeName(state_controller_type), i + 1u);
// we still need to read the save state controller state
if (state_controller_type != ControllerType::None)
{
std::unique_ptr<Controller> dummy_controller = Controller::Create(state_controller_type, i);
if (dummy_controller)
{
if (!sw.DoMarker("Controller") || !dummy_controller->DoState(sw, true))
return false;
}
}
}
}
else
{
if (m_controllers[i])
{
if (!sw.DoMarker("Controller") || !m_controllers[i]->DoState(sw, true))
return false;
}
}
}
else
{
if (state_controller_type != ControllerType::None)
{
if (controller_type != state_controller_type)
{
g_host_interface->AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
Settings::GetControllerTypeName(state_controller_type), i + 1u);
// we still need to read the save state controller state
std::unique_ptr<Controller> dummy_controller = Controller::Create(state_controller_type, i);
if (dummy_controller)
{
if (!sw.DoMarker("Controller") || !dummy_controller->DoState(sw, true))
return false;
}
}
else
{
// we still need to load some things from the state, e.g. configuration mode, analog mode
if (m_controllers[i])
{
if (!sw.DoMarker("Controller") || !m_controllers[i]->DoState(sw, false))
return false;
}
}
}
2019-09-29 15:59:35 +00:00
}
2019-10-27 06:45:23 +00:00
bool card_present = static_cast<bool>(m_memory_cards[i]);
sw.Do(&card_present);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
if (sw.IsReading() && card_present && !g_settings.load_devices_from_save_states)
{
Log_WarningPrintf("Skipping loading memory card %u from save state.", i + 1u);
std::unique_ptr<MemoryCard> card_from_state = std::make_unique<MemoryCard>();
if (!sw.DoMarker("MemoryCard") || !card_from_state->DoState(sw))
return false;
// does the content of the memory card match?
if (m_memory_cards[i])
{
if (m_memory_cards[i]->GetData() == card_from_state->GetData())
{
Log_DevPrintf("Using memory card %u state from save state", i);
card_from_state->SetFilename(m_memory_cards[i]->GetFilename());
m_memory_cards[i] = std::move(card_from_state);
}
else
{
g_host_interface->AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString(
"OSDMessage", "Memory card %u from save state does match current card data. Simulating replugging."),
i + 1u);
m_memory_cards[i]->Reset();
}
}
else
{
g_host_interface->AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in save state but not in system. Ignoring card."),
i + 1u);
}
continue;
}
2019-10-27 06:45:23 +00:00
if (card_present && !m_memory_cards[i])
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
g_host_interface->AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString(
"OSDMessage", "Memory card %u present in save state but not in system. Creating temporary card."),
i + 1u);
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
m_memory_cards[i] = MemoryCard::Create();
2019-10-27 06:45:23 +00:00
}
else if (!card_present && m_memory_cards[i])
{
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
g_host_interface->AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in system but not in save state. Removing card."),
i + 1u);
2019-10-27 06:45:23 +00:00
m_memory_cards[i].reset();
}
2019-09-29 15:59:35 +00:00
if (m_memory_cards[i])
{
if (!sw.DoMarker("MemoryCard") || !m_memory_cards[i]->DoState(sw))
return false;
}
2019-09-29 15:59:35 +00:00
}
2019-09-22 15:25:58 +00:00
sw.Do(&m_state);
2019-09-20 09:21:45 +00:00
sw.Do(&m_JOY_CTRL.bits);
sw.Do(&m_JOY_STAT.bits);
sw.Do(&m_JOY_MODE.bits);
2019-10-18 13:10:41 +00:00
sw.Do(&m_JOY_BAUD);
sw.Do(&m_receive_buffer);
sw.Do(&m_transmit_buffer);
sw.Do(&m_receive_buffer_full);
sw.Do(&m_transmit_buffer_full);
2019-12-06 14:02:47 +00:00
if (sw.IsReading() && IsTransmitting())
m_transfer_event->Activate();
return !sw.HasError();
}
void Pad::SetController(u32 slot, std::unique_ptr<Controller> dev)
{
m_controllers[slot] = std::move(dev);
}
void Pad::SetMemoryCard(u32 slot, std::unique_ptr<MemoryCard> dev)
{
m_memory_cards[slot] = std::move(dev);
}
u32 Pad::ReadRegister(u32 offset)
{
switch (offset)
{
case 0x00: // JOY_DATA
{
if (IsTransmitting())
m_transfer_event->InvokeEarly();
2020-01-11 03:29:30 +00:00
const u8 value = m_receive_buffer_full ? m_receive_buffer : 0xFF;
Log_DebugPrintf("JOY_DATA (R) -> 0x%02X%s", ZeroExtend32(value), m_receive_buffer_full ? "" : "(EMPTY)");
m_receive_buffer_full = false;
UpdateJoyStat();
2020-01-11 03:29:30 +00:00
return (ZeroExtend32(value) | (ZeroExtend32(value) << 8) | (ZeroExtend32(value) << 16) |
(ZeroExtend32(value) << 24));
}
case 0x04: // JOY_STAT
2019-09-22 15:25:58 +00:00
{
if (IsTransmitting())
m_transfer_event->InvokeEarly();
2019-09-22 15:25:58 +00:00
const u32 bits = m_JOY_STAT.bits;
m_JOY_STAT.ACKINPUT = false;
return bits;
}
case 0x08: // JOY_MODE
return ZeroExtend32(m_JOY_MODE.bits);
case 0x0A: // JOY_CTRL
return ZeroExtend32(m_JOY_CTRL.bits);
2019-10-18 13:10:41 +00:00
case 0x0E: // JOY_BAUD
return ZeroExtend32(m_JOY_BAUD);
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_transmit_buffer_full)
Log_WarningPrint("TX FIFO overrun");
m_transmit_buffer = Truncate8(value);
m_transmit_buffer_full = true;
2019-09-22 15:25:58 +00:00
if (!IsTransmitting() && CanTransfer())
BeginTransfer();
return;
}
case 0x0A: // JOY_CTRL
{
Log_DebugPrintf("JOY_CTRL <- 0x%04X", value);
m_JOY_CTRL.bits = Truncate16(value);
if (m_JOY_CTRL.RESET)
SoftReset();
if (m_JOY_CTRL.ACK)
{
// reset stat bits
m_JOY_STAT.INTR = false;
}
2019-09-29 15:07:38 +00:00
if (!m_JOY_CTRL.SELECT)
ResetDeviceTransferState();
2019-09-22 15:25:58 +00:00
if (!m_JOY_CTRL.SELECT || !m_JOY_CTRL.TXEN)
{
if (IsTransmitting())
EndTransfer();
}
else
{
if (!IsTransmitting() && CanTransfer())
BeginTransfer();
}
UpdateJoyStat();
return;
}
case 0x08: // JOY_MODE
{
Log_DebugPrintf("JOY_MODE <- 0x%08X", value);
m_JOY_MODE.bits = Truncate16(value);
return;
}
case 0x0E:
{
2019-10-18 13:10:41 +00:00
Log_DebugPrintf("JOY_BAUD <- 0x%08X", value);
2019-10-26 15:52:18 +00:00
m_JOY_BAUD = Truncate16(value);
return;
}
default:
Log_ErrorPrintf("Unknown register write: 0x%X <- 0x%08X", offset, value);
return;
}
}
void Pad::SoftReset()
{
2019-09-22 15:25:58 +00:00
if (IsTransmitting())
EndTransfer();
m_JOY_CTRL.bits = 0;
m_JOY_STAT.bits = 0;
m_JOY_MODE.bits = 0;
m_receive_buffer = 0;
m_receive_buffer_full = false;
m_transmit_buffer = 0;
m_transmit_buffer_full = false;
2019-09-29 15:59:35 +00:00
ResetDeviceTransferState();
UpdateJoyStat();
}
void Pad::UpdateJoyStat()
{
m_JOY_STAT.RXFIFONEMPTY = m_receive_buffer_full;
2019-12-06 14:02:47 +00:00
m_JOY_STAT.TXDONE = !m_transmit_buffer_full && m_state != State::Transmitting;
m_JOY_STAT.TXRDY = !m_transmit_buffer_full;
}
void Pad::TransferEvent(TickCount ticks_late)
{
if (m_state == State::Transmitting)
DoTransfer(ticks_late);
else
DoACK();
}
2019-09-22 15:25:58 +00:00
void Pad::BeginTransfer()
{
DebugAssert(m_state == State::Idle && CanTransfer());
Log_DebugPrintf("Starting transfer");
m_JOY_CTRL.RXEN = true;
m_transmit_value = m_transmit_buffer;
m_transmit_buffer_full = false;
2019-09-22 15:25:58 +00:00
// 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_state = State::Transmitting;
m_transfer_event->SetPeriodAndSchedule(GetTransferTicks());
2019-09-22 15:25:58 +00:00
}
void Pad::DoTransfer(TickCount ticks_late)
{
Log_DebugPrintf("Transferring slot %d", m_JOY_CTRL.SLOT.GetValue());
Controller* const controller = m_controllers[m_JOY_CTRL.SLOT].get();
MemoryCard* const memory_card = m_memory_cards[m_JOY_CTRL.SLOT].get();
2019-09-22 15:25:58 +00:00
// set rx?
m_JOY_CTRL.RXEN = true;
const u8 data_out = m_transmit_value;
2019-09-29 15:07:38 +00:00
u8 data_in = 0xFF;
bool ack = false;
switch (m_active_device)
{
case ActiveDevice::None:
{
2019-10-26 15:52:18 +00:00
if (!controller || (ack = controller->Transfer(data_out, &data_in)) == false)
2019-09-29 15:07:38 +00:00
{
2019-10-26 15:52:18 +00:00
if (!memory_card || (ack = memory_card->Transfer(data_out, &data_in)) == false)
2019-09-29 15:07:38 +00:00
{
// nothing connected to this port
Log_TracePrintf("Nothing connected or ACK'ed");
2019-09-29 15:07:38 +00:00
}
else
{
// memory card responded, make it the active device until non-ack
Log_TracePrintf("Transfer to memory card, data_out=0x%02X, data_in=0x%02X", data_out, data_in);
2019-09-29 15:07:38 +00:00
m_active_device = ActiveDevice::MemoryCard;
}
}
else
{
// controller responded, make it the active device until non-ack
Log_TracePrintf("Transfer to controller, data_out=0x%02X, data_in=0x%02X", data_out, data_in);
2019-09-29 15:07:38 +00:00
m_active_device = ActiveDevice::Controller;
}
}
break;
case ActiveDevice::Controller:
{
if (controller)
{
2019-09-29 15:07:38 +00:00
ack = controller->Transfer(data_out, &data_in);
Log_TracePrintf("Transfer to controller, data_out=0x%02X, data_in=0x%02X", data_out, data_in);
}
2019-09-29 15:07:38 +00:00
}
break;
case ActiveDevice::MemoryCard:
{
if (memory_card)
{
2019-09-29 15:07:38 +00:00
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);
}
2019-09-29 15:07:38 +00:00
}
break;
}
m_receive_buffer = data_in;
m_receive_buffer_full = true;
2019-09-29 15:07:38 +00:00
// device no longer active?
if (!ack)
{
2019-09-29 15:07:38 +00:00
m_active_device = ActiveDevice::None;
EndTransfer();
}
else
{
const TickCount ack_timer = GetACKTicks(m_active_device == ActiveDevice::MemoryCard);
Log_DebugPrintf("Delaying ACK for %d ticks", ack_timer);
m_state = State::WaitingForACK;
m_transfer_event->SetPeriodAndSchedule(ack_timer);
}
UpdateJoyStat();
}
void Pad::DoACK()
{
m_JOY_STAT.ACKINPUT = true;
if (m_JOY_CTRL.ACKINTEN)
{
Log_DebugPrintf("Triggering ACK interrupt");
m_JOY_STAT.INTR = true;
JIT optimizations and refactoring (#675) * CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
2020-07-31 07:09:18 +00:00
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::IRQ7);
}
EndTransfer();
2019-12-06 14:02:47 +00:00
UpdateJoyStat();
if (CanTransfer())
BeginTransfer();
}
2019-09-22 15:25:58 +00:00
void Pad::EndTransfer()
{
DebugAssert(m_state == State::Transmitting || m_state == State::WaitingForACK);
2019-09-22 15:25:58 +00:00
Log_DebugPrintf("Ending transfer");
m_state = State::Idle;
m_transfer_event->Deactivate();
2019-09-22 15:25:58 +00:00
}
2019-09-29 15:07:38 +00:00
void Pad::ResetDeviceTransferState()
{
for (u32 i = 0; i < NUM_SLOTS; i++)
{
if (m_controllers[i])
m_controllers[i]->ResetTransferState();
if (m_memory_cards[i])
m_memory_cards[i]->ResetTransferState();
m_active_device = ActiveDevice::None;
2019-09-29 15:07:38 +00:00
}
}