#include "pad.h" #include "common/log.h" #include "common/state_wrapper.h" #include "controller.h" #include "host_interface.h" #include "interrupt_controller.h" #include "memory_card.h" #include "multitap.h" #include "system.h" Log_SetChannel(Pad); Pad g_pad; Pad::Pad() = default; Pad::~Pad() = default; void Pad::Initialize() { m_transfer_event = TimingEvents::CreateTimingEvent( "Pad Serial Transfer", 1, 1, [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->TransferEvent(ticks_late); }, this, false); Reset(); } void Pad::Shutdown() { m_transfer_event.reset(); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { m_controllers[i].reset(); m_memory_cards[i].reset(); } } void Pad::Reset() { SoftReset(); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (m_controllers[i]) m_controllers[i]->Reset(); if (m_memory_cards[i]) m_memory_cards[i]->Reset(); } for (u32 i = 0; i < NUM_MULTITAPS; i++) m_multitaps[i].Reset(); } bool Pad::DoStateController(StateWrapper& sw, u32 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 (controller_type != state_controller_type) { Assert(sw.GetMode() == StateWrapper::Mode::Read); // UI notification portion is separated from emulation portion (intentional condition check redundancy) 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)); } 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); } // dev-friendly untranslated console log. Log_DevPrintf("Controller type mismatch in slot %u: state=%s(%u) ui=%s(%u) load_from_state=%s", i + 1u, Settings::GetControllerTypeName(state_controller_type), state_controller_type, Settings::GetControllerTypeName(controller_type), controller_type, g_settings.load_devices_from_save_states ? "yes" : "no"); if (g_settings.load_devices_from_save_states) { m_controllers[i].reset(); if (state_controller_type != ControllerType::None) m_controllers[i] = Controller::Create(state_controller_type, i); } else { // mismatched controller states prevents us from loading the state into the user's preferred controller. // just doing a reset here is a little dodgy. If there's an active xfer on the state-saved controller // then who knows what might happen as the rest of the packet streams in. (possibly the SIO xfer will // timeout and the controller will just correct itself on the next frame's read attempt -- after all on // physical HW removing a controller is allowed and could happen in the middle of SIO comms) if (m_controllers[i]) m_controllers[i]->Reset(); } } // we still need to read/write the save state controller state even if the controller does not exist. // the marker is only expected for valid controller types. if (state_controller_type == ControllerType::None) return true; if (!sw.DoMarker("Controller")) return false; if (auto& controller = m_controllers[i]; controller && controller->GetType() == state_controller_type) return controller->DoState(sw, g_settings.load_devices_from_save_states); else if (auto dummy = Controller::Create(state_controller_type, i); dummy) return dummy->DoState(sw, g_settings.load_devices_from_save_states); return true; } bool Pad::DoStateMemcard(StateWrapper& sw, u32 i) { bool card_present_in_state = static_cast(m_memory_cards[i]); sw.Do(&card_present_in_state); if (card_present_in_state && !m_memory_cards[i] && g_settings.load_devices_from_save_states) { 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); m_memory_cards[i] = MemoryCard::Create(); } MemoryCard* card_ptr = m_memory_cards[i].get(); std::unique_ptr card_from_state; if (card_present_in_state) { if (sw.IsReading() && !g_settings.load_devices_from_save_states) { // load memcard into a temporary: If the card datas match, take the one from the savestate // since it has other useful non-data state information. Otherwise take the user's card // and perform a re-plugging. card_from_state = std::make_unique(); card_ptr = card_from_state.get(); } if (!sw.DoMarker("MemoryCard") || !card_ptr->DoState(sw)) return false; } if (sw.IsWriting()) return true; // all done as far as writes concerned. if (card_from_state) { if (m_memory_cards[i]) { if (m_memory_cards[i]->GetData() == card_from_state->GetData()) { 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); // this is a potentially serious issue - some games cache info from memcards and jumping around // with savestates can lead to card corruption on the next save attempts (and may not be obvious // until much later). One workaround is to forcibly eject the card for 30+ frames, long enough // for the game to decide it was removed and purge its cache. Once implemented, this could be // described as deferred re-plugging in the log. Log_WarningPrintf("Memory card %u data mismatch. Using current data via instant-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); } return true; } if (!card_present_in_state && m_memory_cards[i]) { if (g_settings.load_devices_from_save_states) { 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); m_memory_cards[i].reset(); } else { g_host_interface->AddFormattedOSDMessage( 20.0f, g_host_interface->TranslateString("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."), i + 1u); m_memory_cards[i]->Reset(); } } return true; } bool Pad::DoState(StateWrapper& sw) { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if ((sw.GetVersion() < 50) && (i >= 2)) { // loading from old savestate which only had max 2 controllers. // honoring load_devices_from_save_states in this case seems debatable, but might as well... if (m_controllers[i]) { if (g_settings.load_devices_from_save_states) m_controllers[i].reset(); else m_controllers[i]->Reset(); } if (m_memory_cards[i]) { if (g_settings.load_devices_from_save_states) m_memory_cards[i].reset(); else m_memory_cards[i]->Reset(); } // ... and make sure to skip trying to read controller_type / card_present flags which don't exist in old states. continue; } if (!DoStateController(sw, i)) return false; if (!DoStateMemcard(sw, i)) return false; } if (sw.GetVersion() >= 50) { for (u32 i = 0; i < NUM_MULTITAPS; i++) { if (!m_multitaps[i].DoState(sw)) return false; } } sw.Do(&m_state); sw.Do(&m_JOY_CTRL.bits); sw.Do(&m_JOY_STAT.bits); sw.Do(&m_JOY_MODE.bits); 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); if (sw.IsReading() && IsTransmitting()) m_transfer_event->Activate(); return !sw.HasError(); } void Pad::SetController(u32 slot, std::unique_ptr dev) { m_controllers[slot] = std::move(dev); } void Pad::SetMemoryCard(u32 slot, std::unique_ptr dev) { m_memory_cards[slot] = std::move(dev); } std::unique_ptr Pad::RemoveMemoryCard(u32 slot) { std::unique_ptr ret = std::move(m_memory_cards[slot]); if (ret) ret->Reset(); return ret; } u32 Pad::ReadRegister(u32 offset) { switch (offset) { case 0x00: // JOY_DATA { if (IsTransmitting()) m_transfer_event->InvokeEarly(); 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(); return (ZeroExtend32(value) | (ZeroExtend32(value) << 8) | (ZeroExtend32(value) << 16) | (ZeroExtend32(value) << 24)); } case 0x04: // JOY_STAT { if (IsTransmitting()) m_transfer_event->InvokeEarly(); 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); 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; 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; } if (!m_JOY_CTRL.SELECT) ResetDeviceTransferState(); 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: { Log_DebugPrintf("JOY_BAUD <- 0x%08X", value); m_JOY_BAUD = Truncate16(value); return; } default: Log_ErrorPrintf("Unknown register write: 0x%X <- 0x%08X", offset, value); return; } } void Pad::SoftReset() { 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; ResetDeviceTransferState(); UpdateJoyStat(); } void Pad::UpdateJoyStat() { m_JOY_STAT.RXFIFONEMPTY = m_receive_buffer_full; 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(); } 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; // 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()); } void Pad::DoTransfer(TickCount ticks_late) { Log_DebugPrintf("Transferring slot %d", m_JOY_CTRL.SLOT.GetValue()); const u8 device_index = m_multitaps[0].IsEnabled() ? 4u : m_JOY_CTRL.SLOT; Controller* const controller = m_controllers[device_index].get(); MemoryCard* const memory_card = m_memory_cards[device_index].get(); // set rx? m_JOY_CTRL.RXEN = true; const u8 data_out = m_transmit_value; u8 data_in = 0xFF; bool ack = false; switch (m_active_device) { case ActiveDevice::None: { if (m_multitaps[m_JOY_CTRL.SLOT].IsEnabled()) { if ((ack = m_multitaps[m_JOY_CTRL.SLOT].Transfer(data_out, &data_in)) == true) { Log_TracePrintf("Active device set to tap %d, sent 0x%02X, received 0x%02X", static_cast(m_JOY_CTRL.SLOT), data_out, data_in); m_active_device = ActiveDevice::Multitap; } } else { if (!controller || (ack = controller->Transfer(data_out, &data_in)) == false) { if (!memory_card || (ack = memory_card->Transfer(data_out, &data_in)) == false) { // nothing connected to this port Log_TracePrintf("Nothing connected or ACK'ed"); } 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); 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); m_active_device = ActiveDevice::Controller; } } } break; case ActiveDevice::Controller: { if (controller) { 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; case ActiveDevice::MemoryCard: { if (memory_card) { 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; case ActiveDevice::Multitap: { if (m_multitaps[m_JOY_CTRL.SLOT].IsEnabled()) { ack = m_multitaps[m_JOY_CTRL.SLOT].Transfer(data_out, &data_in); Log_TracePrintf("Transfer tap %d, sent 0x%02X, received 0x%02X, acked: %s", static_cast(m_JOY_CTRL.SLOT), data_out, data_in, ack ? "true" : "false"); } } break; } m_receive_buffer = data_in; m_receive_buffer_full = true; // device no longer active? if (!ack) { m_active_device = ActiveDevice::None; EndTransfer(); } else { const bool memcard_transfer = m_active_device == ActiveDevice::MemoryCard || (m_active_device == ActiveDevice::Multitap && m_multitaps[m_JOY_CTRL.SLOT].IsReadingMemoryCard()); const TickCount ack_timer = GetACKTicks(memcard_transfer); 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; g_interrupt_controller.InterruptRequest(InterruptController::IRQ::IRQ7); } EndTransfer(); UpdateJoyStat(); if (CanTransfer()) BeginTransfer(); } void Pad::EndTransfer() { DebugAssert(m_state == State::Transmitting || m_state == State::WaitingForACK); Log_DebugPrintf("Ending transfer"); m_state = State::Idle; m_transfer_event->Deactivate(); } void Pad::ResetDeviceTransferState() { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (m_controllers[i]) m_controllers[i]->ResetTransferState(); if (m_memory_cards[i]) m_memory_cards[i]->ResetTransferState(); } for (u32 i = 0; i < NUM_MULTITAPS; i++) m_multitaps[i].ResetTransferState(); m_active_device = ActiveDevice::None; }