duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }