// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin and contributors. // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "pad.h" #include "controller.h" #include "host.h" #include "interrupt_controller.h" #include "memory_card.h" #include "multitap.h" #include "save_state_version.h" #include "system.h" #include "types.h" #include "util/imgui_manager.h" #include "util/state_wrapper.h" #include "common/bitfield.h" #include "common/bitutils.h" #include "common/fifo_queue.h" #include "common/log.h" #include #include Log_SetChannel(Pad); namespace Pad { enum class State : u32 { Idle, Transmitting, WaitingForACK }; enum class ActiveDevice : u8 { None, Controller, MemoryCard, Multitap }; union JOY_CTRL { u16 bits; BitField TXEN; BitField SELECT; BitField RXEN; BitField ACK; BitField RESET; BitField RXIMODE; BitField TXINTEN; BitField RXINTEN; BitField ACKINTEN; BitField SLOT; }; union JOY_STAT { u32 bits; BitField TXRDY; BitField RXFIFONEMPTY; BitField TXDONE; BitField ACKINPUT; BitField INTR; BitField TMR; }; union JOY_MODE { u16 bits; BitField reload_factor; BitField character_length; BitField parity_enable; BitField parity_type; BitField clk_polarity; }; static bool CanTransfer(); static bool ShouldAvoidSavingToState(); static u32 GetMaximumRollbackFrames(); static TickCount GetTransferTicks(); // From @JaCzekanski // ACK lasts ~96 ticks or approximately 2.84us at master clock (not implemented). // ACK delay is between 6.8us-13.7us, or ~338 ticks at master clock for approximately 9.98us. // Memory card responds faster, approximately 5us or ~170 ticks. static constexpr TickCount GetACKTicks(bool memory_card) { return memory_card ? 170 : 450; } static void SoftReset(); static void UpdateJoyStat(); static void TransferEvent(void*, TickCount ticks, TickCount ticks_late); static void BeginTransfer(); static void DoTransfer(TickCount ticks_late); static void DoACK(); static void EndTransfer(); static void ResetDeviceTransferState(); static bool DoStateController(StateWrapper& sw, u32 i); static bool DoStateMemcard(StateWrapper& sw, u32 i, bool is_memory_state); static MemoryCard* GetDummyMemcard(); static void BackupMemoryCardState(); static void RestoreMemoryCardState(); static std::array, NUM_CONTROLLER_AND_CARD_PORTS> s_controllers; static std::array, NUM_CONTROLLER_AND_CARD_PORTS> s_memory_cards; static std::array s_multitaps; static std::unique_ptr s_transfer_event; static State s_state = State::Idle; static JOY_CTRL s_JOY_CTRL = {}; static JOY_STAT s_JOY_STAT = {}; static JOY_MODE s_JOY_MODE = {}; static u16 s_JOY_BAUD = 0; static ActiveDevice s_active_device = ActiveDevice::None; static u8 s_receive_buffer = 0; static u8 s_transmit_buffer = 0; static u8 s_transmit_value = 0; static bool s_receive_buffer_full = false; static bool s_transmit_buffer_full = false; static u32 s_last_memory_card_transfer_frame = 0; static std::unique_ptr s_memory_card_backup; static std::unique_ptr s_dummy_card; } // namespace Pad void Pad::Initialize() { s_transfer_event = TimingEvents::CreateTimingEvent("Pad Serial Transfer", 1, 1, &Pad::TransferEvent, nullptr, false); Reset(); } void Pad::Shutdown() { s_memory_card_backup.reset(); s_transfer_event.reset(); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { s_controllers[i].reset(); s_memory_cards[i].reset(); } } void Pad::Reset() { SoftReset(); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (s_controllers[i]) s_controllers[i]->Reset(); if (s_memory_cards[i]) s_memory_cards[i]->Reset(); } for (u32 i = 0; i < NUM_MULTITAPS; i++) s_multitaps[i].Reset(); } bool Pad::ShouldAvoidSavingToState() { // Currently only runahead, will also be used for netplay. return g_settings.IsRunaheadEnabled(); } u32 Pad::GetMaximumRollbackFrames() { return g_settings.runahead_frames; } bool Pad::DoStateController(StateWrapper& sw, u32 i) { ControllerType controller_type = s_controllers[i] ? s_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) { Host::AddFormattedOSDMessage( 10.0f, TRANSLATE("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 { Host::AddFormattedOSDMessage(10.0f, TRANSLATE("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), static_cast(state_controller_type), Settings::GetControllerTypeName(controller_type), static_cast(controller_type), g_settings.load_devices_from_save_states ? "yes" : "no"); if (g_settings.load_devices_from_save_states) { s_controllers[i].reset(); if (state_controller_type != ControllerType::None) s_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 (s_controllers[i]) s_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 = s_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 is_memory_state) { bool card_present_in_state = static_cast(s_memory_cards[i]); sw.Do(&card_present_in_state); if (card_present_in_state && !s_memory_cards[i] && g_settings.load_devices_from_save_states) { Host::AddFormattedOSDMessage( 20.0f, TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Creating temporary card."), i + 1u); s_memory_cards[i] = MemoryCard::Create(); } MemoryCard* card_ptr = s_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_ptr = GetDummyMemcard(); } 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 (s_memory_cards[i]) { if (s_memory_cards[i]->GetData() == card_from_state->GetData()) { card_from_state->SetFilename(s_memory_cards[i]->GetFilename()); s_memory_cards[i] = std::move(card_from_state); } else { Host::AddFormattedOSDMessage( 20.0f, TRANSLATE("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); s_memory_cards[i]->Reset(); } } else { Host::AddFormattedOSDMessage( 20.0f, TRANSLATE("OSDMessage", "Memory card %u present in save state but not in system. Ignoring card."), i + 1u); } return true; } if (!card_present_in_state && s_memory_cards[i]) { if (g_settings.load_devices_from_save_states) { Host::AddFormattedOSDMessage( 20.0f, TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Removing card."), i + 1u); s_memory_cards[i].reset(); } else { Host::AddFormattedOSDMessage( 20.0f, TRANSLATE("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."), i + 1u); s_memory_cards[i]->Reset(); } } return true; } MemoryCard* Pad::GetDummyMemcard() { if (!s_dummy_card) s_dummy_card = MemoryCard::Create(); return s_dummy_card.get(); } void Pad::BackupMemoryCardState() { Log_DevPrintf("Backing up memory card state."); if (!s_memory_card_backup) { s_memory_card_backup = std::make_unique(nullptr, MemoryCard::STATE_SIZE * NUM_CONTROLLER_AND_CARD_PORTS); } s_memory_card_backup->SeekAbsolute(0); StateWrapper sw(s_memory_card_backup.get(), StateWrapper::Mode::Write, SAVE_STATE_VERSION); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (s_memory_cards[i]) s_memory_cards[i]->DoState(sw); } } void Pad::RestoreMemoryCardState() { DebugAssert(s_memory_card_backup); Log_VerbosePrintf("Restoring backed up memory card state."); s_memory_card_backup->SeekAbsolute(0); StateWrapper sw(s_memory_card_backup.get(), StateWrapper::Mode::Read, SAVE_STATE_VERSION); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (s_memory_cards[i]) s_memory_cards[i]->DoState(sw); } } bool Pad::DoState(StateWrapper& sw, bool is_memory_state) { if (is_memory_state && ShouldAvoidSavingToState()) { // We do a bit of trickery for memory states here to avoid writing 128KB * num_cards to the state. // Profiling shows that the card write scan be up to 17% of overall CPU time, so it's definitely worth skipping. // However, we can't roll back past a transfer boundary, because that'll corrupt our cards. So, we have to be smart // about this. // // There's three main scenarios: // (1) No transfers occurring before or after the rollback point. // (2) A transfer was started before the rollback point. // (3) A transfer was started after the rollback point. // // For (1), it's easy, we don't have to do anything. Just skip saving and continue on our merry way. // // For (2), we serialize the state whenever there's a transfer within the last N_ROLLBACK frames. Easy-ish. // // For (3), it gets messy. We didn't know that a transfer was going to start, and our rollback state doesn't // contain the state of the memory cards, because we were cheeky and skipped it. So, instead, we back up // the state of memory cards when any transfer begins, assuming it's not within the last N_ROLLBACK frames, in // DoTransfer(). That way, when we do have to roll back past this boundary, we can just restore the known good "pre // transfer" state. Any memory saves created after the transfer begun will go through the same path as (2), so we // don't risk corrupting that way. // // Hopefully that's everything. // bool process_memcard_state = true; const u32 frame_number = System::GetFrameNumber(); const u32 frames_since_transfer = frame_number - s_last_memory_card_transfer_frame; const u32 prev_transfer_frame = s_last_memory_card_transfer_frame; bool state_has_memcards = false; sw.Do(&s_last_memory_card_transfer_frame); // If there's been a transfer within the last N_ROLLBACK frames, include the memory card state when saving. state_has_memcards = (frames_since_transfer <= GetMaximumRollbackFrames()); sw.Do(&state_has_memcards); if (sw.IsReading()) { // If no transfers have occurred, no need to reload state. if (s_last_memory_card_transfer_frame != frame_number && s_last_memory_card_transfer_frame == prev_transfer_frame) { process_memcard_state = false; } else if (!state_has_memcards) { // If the memory state doesn't have card data (i.e. rolling back past a transfer start), reload the backed up // state created when the transfer initially begun. RestoreMemoryCardState(); process_memcard_state = false; } } // Still have to parse through the data if it's present. if (state_has_memcards) { MemoryCard* dummy_card = process_memcard_state ? nullptr : GetDummyMemcard(); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (s_memory_cards[i]) { MemoryCard* const mc = process_memcard_state ? s_memory_cards[i].get() : dummy_card; mc->DoState(sw); } } } // Always save controller state. for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (s_controllers[i]) { // Ignore input state, use the current. I think we want this? s_controllers[i]->DoState(sw, false); } } } else { 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 (s_controllers[i]) { if (g_settings.load_devices_from_save_states) s_controllers[i].reset(); else s_controllers[i]->Reset(); } if (s_memory_cards[i]) { if (g_settings.load_devices_from_save_states) s_memory_cards[i].reset(); else s_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, is_memory_state)) return false; } } if (sw.GetVersion() >= 50) { for (u32 i = 0; i < NUM_MULTITAPS; i++) { if (!s_multitaps[i].DoState(sw)) return false; } } sw.Do(&s_state); sw.Do(&s_JOY_CTRL.bits); sw.Do(&s_JOY_STAT.bits); sw.Do(&s_JOY_MODE.bits); sw.Do(&s_JOY_BAUD); sw.Do(&s_receive_buffer); sw.Do(&s_transmit_buffer); sw.Do(&s_receive_buffer_full); sw.Do(&s_transmit_buffer_full); if (sw.IsReading() && IsTransmitting()) s_transfer_event->Activate(); return !sw.HasError(); } Controller* Pad::GetController(u32 slot) { return s_controllers[slot].get(); } void Pad::SetController(u32 slot, std::unique_ptr dev) { s_controllers[slot] = std::move(dev); } MemoryCard* Pad::GetMemoryCard(u32 slot) { return s_memory_cards[slot].get(); } void Pad::SetMemoryCard(u32 slot, std::unique_ptr dev) { Log_InfoPrintf("Memory card slot %u: %s", slot, dev ? (dev->GetFilename().empty() ? "" : dev->GetFilename().c_str()) : ""); s_memory_cards[slot] = std::move(dev); } std::unique_ptr Pad::RemoveMemoryCard(u32 slot) { std::unique_ptr ret = std::move(s_memory_cards[slot]); if (ret) ret->Reset(); return ret; } Multitap* Pad::GetMultitap(u32 slot) { return &s_multitaps[slot]; } u32 Pad::ReadRegister(u32 offset) { switch (offset) { case 0x00: // JOY_DATA { if (IsTransmitting()) s_transfer_event->InvokeEarly(); const u8 value = s_receive_buffer_full ? s_receive_buffer : 0xFF; Log_DebugPrintf("JOY_DATA (R) -> 0x%02X%s", ZeroExtend32(value), s_receive_buffer_full ? "" : "(EMPTY)"); s_receive_buffer_full = false; UpdateJoyStat(); return (ZeroExtend32(value) | (ZeroExtend32(value) << 8) | (ZeroExtend32(value) << 16) | (ZeroExtend32(value) << 24)); } case 0x04: // JOY_STAT { if (IsTransmitting()) s_transfer_event->InvokeEarly(); const u32 bits = s_JOY_STAT.bits; s_JOY_STAT.ACKINPUT = false; return bits; } case 0x08: // JOY_MODE return ZeroExtend32(s_JOY_MODE.bits); case 0x0A: // JOY_CTRL return ZeroExtend32(s_JOY_CTRL.bits); case 0x0E: // JOY_BAUD return ZeroExtend32(s_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 (s_transmit_buffer_full) Log_WarningPrint("TX FIFO overrun"); s_transmit_buffer = Truncate8(value); s_transmit_buffer_full = true; if (!IsTransmitting() && CanTransfer()) BeginTransfer(); return; } case 0x0A: // JOY_CTRL { Log_DebugPrintf("JOY_CTRL <- 0x%04X", value); s_JOY_CTRL.bits = Truncate16(value); if (s_JOY_CTRL.RESET) SoftReset(); if (s_JOY_CTRL.ACK) { // reset stat bits s_JOY_STAT.INTR = false; } if (!s_JOY_CTRL.SELECT) ResetDeviceTransferState(); if (!s_JOY_CTRL.SELECT || !s_JOY_CTRL.TXEN) { if (IsTransmitting()) EndTransfer(); } else { if (!IsTransmitting() && CanTransfer()) BeginTransfer(); } UpdateJoyStat(); return; } case 0x08: // JOY_MODE { Log_DebugPrintf("JOY_MODE <- 0x%08X", value); s_JOY_MODE.bits = Truncate16(value); return; } case 0x0E: { Log_DebugPrintf("JOY_BAUD <- 0x%08X", value); s_JOY_BAUD = Truncate16(value); return; } default: Log_ErrorPrintf("Unknown register write: 0x%X <- 0x%08X", offset, value); return; } } bool Pad::IsTransmitting() { return s_state != State::Idle; } bool Pad::CanTransfer() { return s_transmit_buffer_full && s_JOY_CTRL.SELECT && s_JOY_CTRL.TXEN; } TickCount Pad::GetTransferTicks() { return static_cast(ZeroExtend32(s_JOY_BAUD) * 8); } void Pad::SoftReset() { if (IsTransmitting()) EndTransfer(); s_JOY_CTRL.bits = 0; s_JOY_STAT.bits = 0; s_JOY_MODE.bits = 0; s_receive_buffer = 0; s_receive_buffer_full = false; s_transmit_buffer = 0; s_transmit_buffer_full = false; ResetDeviceTransferState(); UpdateJoyStat(); } void Pad::UpdateJoyStat() { s_JOY_STAT.RXFIFONEMPTY = s_receive_buffer_full; s_JOY_STAT.TXDONE = !s_transmit_buffer_full && s_state != State::Transmitting; s_JOY_STAT.TXRDY = !s_transmit_buffer_full; } void Pad::TransferEvent(void*, TickCount ticks, TickCount ticks_late) { if (s_state == State::Transmitting) DoTransfer(ticks_late); else DoACK(); } void Pad::BeginTransfer() { DebugAssert(s_state == State::Idle && CanTransfer()); Log_DebugPrintf("Starting transfer"); s_JOY_CTRL.RXEN = true; s_transmit_value = s_transmit_buffer; s_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. s_state = State::Transmitting; s_transfer_event->SetPeriodAndSchedule(GetTransferTicks()); } void Pad::DoTransfer(TickCount ticks_late) { Log_DebugPrintf("Transferring slot %d", s_JOY_CTRL.SLOT.GetValue()); const u8 device_index = s_multitaps[0].IsEnabled() ? 4u : s_JOY_CTRL.SLOT; Controller* const controller = s_controllers[device_index].get(); MemoryCard* const memory_card = s_memory_cards[device_index].get(); // set rx? s_JOY_CTRL.RXEN = true; const u8 data_out = s_transmit_value; u8 data_in = 0xFF; bool ack = false; switch (s_active_device) { case ActiveDevice::None: { if (s_multitaps[s_JOY_CTRL.SLOT].IsEnabled()) { if ((ack = s_multitaps[s_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(s_JOY_CTRL.SLOT), data_out, data_in); s_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); s_active_device = ActiveDevice::MemoryCard; // back up memory card state in case we roll back to before this transfer begun const u32 frame_number = System::GetFrameNumber(); // consider u32 overflow case if (ShouldAvoidSavingToState() && (frame_number - s_last_memory_card_transfer_frame) > GetMaximumRollbackFrames()) BackupMemoryCardState(); s_last_memory_card_transfer_frame = frame_number; } } 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); s_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) { s_last_memory_card_transfer_frame = System::GetFrameNumber(); 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 (s_multitaps[s_JOY_CTRL.SLOT].IsEnabled()) { ack = s_multitaps[s_JOY_CTRL.SLOT].Transfer(data_out, &data_in); Log_TracePrintf("Transfer tap %d, sent 0x%02X, received 0x%02X, acked: %s", static_cast(s_JOY_CTRL.SLOT), data_out, data_in, ack ? "true" : "false"); } } break; } s_receive_buffer = data_in; s_receive_buffer_full = true; // device no longer active? if (!ack) { s_active_device = ActiveDevice::None; EndTransfer(); } else { const bool memcard_transfer = s_active_device == ActiveDevice::MemoryCard || (s_active_device == ActiveDevice::Multitap && s_multitaps[s_JOY_CTRL.SLOT].IsReadingMemoryCard()); const TickCount ack_timer = GetACKTicks(memcard_transfer); Log_DebugPrintf("Delaying ACK for %d ticks", ack_timer); s_state = State::WaitingForACK; s_transfer_event->SetPeriodAndSchedule(ack_timer); } UpdateJoyStat(); } void Pad::DoACK() { s_JOY_STAT.ACKINPUT = true; if (s_JOY_CTRL.ACKINTEN) { Log_DebugPrintf("Triggering ACK interrupt"); s_JOY_STAT.INTR = true; InterruptController::InterruptRequest(InterruptController::IRQ::IRQ7); } EndTransfer(); UpdateJoyStat(); if (CanTransfer()) BeginTransfer(); } void Pad::EndTransfer() { DebugAssert(s_state == State::Transmitting || s_state == State::WaitingForACK); Log_DebugPrintf("Ending transfer"); s_state = State::Idle; s_transfer_event->Deactivate(); } void Pad::ResetDeviceTransferState() { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (s_controllers[i]) s_controllers[i]->ResetTransferState(); if (s_memory_cards[i]) s_memory_cards[i]->ResetTransferState(); } for (u32 i = 0; i < NUM_MULTITAPS; i++) s_multitaps[i].ResetTransferState(); s_active_device = ActiveDevice::None; }