multitap.cpp (6630B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors. 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "multitap.h" 5 #include "controller.h" 6 #include "memory_card.h" 7 #include "pad.h" 8 9 #include "util/state_wrapper.h" 10 11 #include "common/log.h" 12 #include "common/types.h" 13 14 Log_SetChannel(Multitap); 15 16 Multitap::Multitap() 17 { 18 Reset(); 19 } 20 21 void Multitap::Reset() 22 { 23 m_transfer_state = TransferState::Idle; 24 m_selected_slot = 0; 25 m_controller_transfer_step = 0; 26 m_transfer_all_controllers = false; 27 m_invalid_transfer_all_command = false; 28 m_current_controller_done = false; 29 m_transfer_buffer.fill(0xFF); 30 } 31 32 void Multitap::SetEnable(bool enable, u32 base_index) 33 { 34 if (m_enabled != enable || m_base_index != base_index) 35 { 36 m_enabled = enable; 37 m_base_index = base_index; 38 Reset(); 39 } 40 } 41 42 bool Multitap::DoState(StateWrapper& sw) 43 { 44 sw.Do(&m_transfer_state); 45 sw.Do(&m_selected_slot); 46 sw.Do(&m_controller_transfer_step); 47 sw.Do(&m_invalid_transfer_all_command); 48 sw.Do(&m_transfer_all_controllers); 49 sw.Do(&m_current_controller_done); 50 sw.Do(&m_transfer_buffer); 51 52 return !sw.HasError(); 53 } 54 55 void Multitap::ResetTransferState() 56 { 57 m_transfer_state = TransferState::Idle; 58 m_selected_slot = 0; 59 m_controller_transfer_step = 0; 60 m_current_controller_done = false; 61 62 // Don't reset m_transfer_all_controllers here, since it's queued up for the next transfer sequence 63 // Controller and memory card transfer resets are handled in the Pad class 64 } 65 66 bool Multitap::TransferController(u32 slot, const u8 data_in, u8* data_out) const 67 { 68 Controller* const selected_controller = Pad::GetController(m_base_index + slot); 69 if (!selected_controller) 70 { 71 *data_out = 0xFF; 72 return false; 73 } 74 75 return selected_controller->Transfer(data_in, data_out); 76 } 77 78 bool Multitap::TransferMemoryCard(u32 slot, const u8 data_in, u8* data_out) const 79 { 80 MemoryCard* const selected_memcard = Pad::GetMemoryCard(m_base_index + slot); 81 if (!selected_memcard) 82 { 83 *data_out = 0xFF; 84 return false; 85 } 86 87 return selected_memcard->Transfer(data_in, data_out); 88 } 89 90 bool Multitap::Transfer(const u8 data_in, u8* data_out) 91 { 92 bool ack = false; 93 switch (m_transfer_state) 94 { 95 case TransferState::Idle: 96 { 97 switch (data_in) 98 { 99 case 0x81: 100 case 0x82: 101 case 0x83: 102 case 0x84: 103 { 104 m_selected_slot = (data_in & 0x0F) - 1u; 105 ack = TransferMemoryCard(m_selected_slot, 0x81, data_out); 106 107 if (ack) 108 m_transfer_state = TransferState::MemoryCard; 109 } 110 break; 111 112 case 0x01: 113 case 0x02: 114 case 0x03: 115 case 0x04: 116 { 117 m_selected_slot = data_in - 1u; 118 ack = TransferController(m_selected_slot, 0x01, data_out); 119 120 if (ack) 121 { 122 m_transfer_state = TransferState::ControllerCommand; 123 124 if (m_transfer_all_controllers) 125 { 126 // Send access byte to remaining controllers for this transfer mode 127 u8 dummy_value; 128 for (u32 i = 0; i < 4; i++) 129 { 130 if (i != m_selected_slot) 131 TransferController(i, 0x01, &dummy_value); 132 } 133 } 134 } 135 } 136 break; 137 138 default: 139 { 140 *data_out = 0xFF; 141 ack = false; 142 } 143 break; 144 } 145 } 146 break; 147 148 case TransferState::MemoryCard: 149 { 150 ack = TransferMemoryCard(m_selected_slot, data_in, data_out); 151 152 if (!ack) 153 { 154 DEV_LOG("Memory card transfer ended"); 155 m_transfer_state = TransferState::Idle; 156 } 157 } 158 break; 159 160 case TransferState::ControllerCommand: 161 { 162 if (m_controller_transfer_step == 0) // Command byte 163 { 164 if (m_transfer_all_controllers) 165 { 166 // Unknown if 0x42 is the only valid command byte here, but other tested command bytes cause early aborts 167 *data_out = GetMultitapIDByte(); 168 m_invalid_transfer_all_command = (data_in != 0x42); 169 ack = true; 170 } 171 else 172 { 173 ack = TransferController(m_selected_slot, data_in, data_out); 174 } 175 m_controller_transfer_step++; 176 } 177 else if (m_controller_transfer_step == 1) // Request byte 178 { 179 if (m_transfer_all_controllers) 180 { 181 *data_out = GetStatusByte(); 182 183 ack = !m_invalid_transfer_all_command; 184 m_selected_slot = 0; 185 m_transfer_state = TransferState::AllControllers; 186 } 187 else 188 { 189 ack = TransferController(m_selected_slot, 0x00, data_out); 190 m_transfer_state = TransferState::SingleController; 191 } 192 193 // Queue up request for next transfer cycle (not sure if this is always queued on invalid commands) 194 m_transfer_all_controllers = (data_in & 0x01); 195 m_controller_transfer_step = 0; 196 } 197 else 198 { 199 UnreachableCode(); 200 } 201 } 202 break; 203 204 case TransferState::SingleController: 205 { 206 // TODO: Check if the transfer buffer gets wiped when transitioning to/from this mode 207 208 ack = TransferController(m_selected_slot, data_in, data_out); 209 210 if (!ack) 211 { 212 DEV_LOG("Controller transfer ended"); 213 m_transfer_state = TransferState::Idle; 214 } 215 } 216 break; 217 218 case TransferState::AllControllers: 219 { 220 // In this mode, we transfer until reaching 8 bytes or the controller finishes its response (no ack is returned). 221 // The hardware is probably either latching the controller info halfword count or waiting for a transfer timeout 222 // (timeouts might be possible due to buffered responses in this mode, and if the controllers are transferred in 223 // parallel rather than sequentially like we're doing here). We'll just simplify this and check the ack return 224 // value since our controller implementations are deterministic. 225 226 *data_out = m_transfer_buffer[m_controller_transfer_step]; 227 ack = true; 228 229 if (m_current_controller_done) 230 m_transfer_buffer[m_controller_transfer_step] = 0xFF; 231 else 232 m_current_controller_done = 233 !TransferController(m_selected_slot, data_in, &m_transfer_buffer[m_controller_transfer_step]); 234 235 m_controller_transfer_step++; 236 if (m_controller_transfer_step % 8 == 0) 237 { 238 m_current_controller_done = false; 239 m_selected_slot = (m_selected_slot + 1) % 4; 240 if (m_selected_slot == 0) 241 ack = false; 242 } 243 } 244 break; 245 246 DefaultCaseIsUnreachable(); 247 } 248 return ack; 249 }