diff --git a/src/pse-sdl/sdl_interface.cpp b/src/pse-sdl/sdl_interface.cpp index d7ec72cc8..50772a0bd 100644 --- a/src/pse-sdl/sdl_interface.cpp +++ b/src/pse-sdl/sdl_interface.cpp @@ -6,6 +6,7 @@ #include "imgui_impl_opengl3.h" #include "imgui_impl_sdl.h" #include "pse/digital_controller.h" +#include "pse/memory_card.h" #include "pse/system.h" #include #include @@ -164,6 +165,7 @@ std::unique_ptr SDLInterface::Create() return nullptr; intf->m_controller = DigitalController::Create(); + intf->m_memory_card = MemoryCard::Create(); return intf; } @@ -285,18 +287,17 @@ bool SDLInterface::HandleSDLEvent(const SDL_Event* event) case SDL_SCANCODE_F6: case SDL_SCANCODE_F7: case SDL_SCANCODE_F8: + { + if (!pressed) { - if (!pressed) - { - auto filename = GetSaveStateFilename(event->key.keysym.scancode - SDL_SCANCODE_F1 + 1); - if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) - SaveState(filename); - else - LoadState(filename); - } + auto filename = GetSaveStateFilename(event->key.keysym.scancode - SDL_SCANCODE_F1 + 1); + if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) + SaveState(filename); + else + LoadState(filename); } - break; - + } + break; case SDL_SCANCODE_TAB: SDL_GL_SetSwapInterval(pressed ? 0 : 1); @@ -593,7 +594,8 @@ void SDLInterface::DoSaveState(u32 index) void SDLInterface::Run() { - m_system->SetPadDevice(0, m_controller); + m_system->SetController(0, m_controller); + m_system->SetMemoryCard(0, m_memory_card); while (m_running) { diff --git a/src/pse-sdl/sdl_interface.h b/src/pse-sdl/sdl_interface.h index 6e29e2c2d..17942179d 100644 --- a/src/pse-sdl/sdl_interface.h +++ b/src/pse-sdl/sdl_interface.h @@ -12,6 +12,7 @@ class System; class DigitalController; +class MemoryCard; class SDLInterface : public HostInterface { @@ -77,6 +78,7 @@ private: std::mutex m_osd_messages_lock; std::shared_ptr m_controller; + std::shared_ptr m_memory_card; float m_vps = 0.0f; float m_fps = 0.0f; diff --git a/src/pse/cpu_core.cpp b/src/pse/cpu_core.cpp index a84721b4f..c08be4641 100644 --- a/src/pse/cpu_core.cpp +++ b/src/pse/cpu_core.cpp @@ -456,8 +456,8 @@ void Core::Execute() { while (m_downcount >= 0) { - m_pending_ticks += 2; - m_downcount -= 2; + m_pending_ticks += 3; + m_downcount -= 3; // now executing the instruction we previously fetched const Instruction inst = m_next_instruction; diff --git a/src/pse/digital_controller.cpp b/src/pse/digital_controller.cpp index b5fc156f7..9c145d6b6 100644 --- a/src/pse/digital_controller.cpp +++ b/src/pse/digital_controller.cpp @@ -14,6 +14,11 @@ void DigitalController::SetButtonState(Button button, bool pressed) m_button_state |= u16(1) << static_cast(button); } +void DigitalController::ResetTransferState() +{ + m_transfer_fifo.Clear(); +} + bool DigitalController::Transfer(const u8 data_in, u8* data_out) { bool ack; @@ -75,3 +80,4 @@ std::shared_ptr DigitalController::Create() { return std::make_shared(); } + diff --git a/src/pse/digital_controller.h b/src/pse/digital_controller.h index 989d2fb97..7f28a7e82 100644 --- a/src/pse/digital_controller.h +++ b/src/pse/digital_controller.h @@ -33,6 +33,7 @@ public: void SetButtonState(Button button, bool pressed); + void ResetTransferState() override; bool Transfer(const u8 data_in, u8* data_out) override; private: diff --git a/src/pse/dma.cpp b/src/pse/dma.cpp index d6c81c66d..592fd5f29 100644 --- a/src/pse/dma.cpp +++ b/src/pse/dma.cpp @@ -84,7 +84,6 @@ u32 DMA::ReadRegister(u32 offset) void DMA::WriteRegister(u32 offset, u32 value) { const u32 channel_index = offset >> 4; - Log_DevPrintf("DMA channel %u offset %u", channel_index, offset); if (channel_index < 7) { ChannelState& state = m_state[channel_index]; diff --git a/src/pse/memory_card.cpp b/src/pse/memory_card.cpp new file mode 100644 index 000000000..4d3ee166a --- /dev/null +++ b/src/pse/memory_card.cpp @@ -0,0 +1,193 @@ +#include "memory_card.h" +#include "YBaseLib/Log.h" +Log_SetChannel(MemoryCard); + +MemoryCard::MemoryCard() +{ + m_FLAG.no_write_yet = true; +} + +MemoryCard::~MemoryCard() = default; + +void MemoryCard::ResetTransferState() +{ + m_state = State::Idle; + m_address = 0; + m_sector_offset = 0; + m_checksum = 0; + m_last_byte = 0; +} + +bool MemoryCard::Transfer(const u8 data_in, u8* data_out) +{ + bool ack = false; + const State old_state = m_state; + + switch (m_state) + { + +#define FIXED_REPLY_STATE(state, reply, ack_value, next_state) \ + case state: \ + { \ + *data_out = reply; \ + ack = ack_value; \ + m_state = next_state; \ + } \ + break; + +#define ADDRESS_STATE_MSB(state, next_state) \ + case state: \ + { \ + *data_out = 0x00; \ + ack = true; \ + m_address = ((m_address & u16(0x00FF)) | (ZeroExtend16(data_in) << 8)) & 0x3FF; \ + m_state = next_state; \ + } \ + break; + +#define ADDRESS_STATE_LSB(state, next_state) \ + case state: \ + { \ + *data_out = m_last_byte; \ + ack = true; \ + m_address = ((m_address & u16(0xFF00)) | ZeroExtend16(data_in)) & 0x3FF; \ + m_sector_offset = 0; \ + m_state = next_state; \ + } \ + break; + + // read state + + FIXED_REPLY_STATE(State::ReadCardID1, 0x5A, true, State::ReadCardID2); + FIXED_REPLY_STATE(State::ReadCardID2, 0x5D, true, State::ReadAddressMSB); + ADDRESS_STATE_MSB(State::ReadAddressMSB, State::ReadAddressLSB); + ADDRESS_STATE_LSB(State::ReadAddressLSB, State::ReadACK1); + FIXED_REPLY_STATE(State::ReadACK1, 0x5C, true, State::ReadACK2); + FIXED_REPLY_STATE(State::ReadACK2, 0x5D, true, State::ReadConfirmAddressMSB); + FIXED_REPLY_STATE(State::ReadConfirmAddressMSB, Truncate8(m_address >> 8), true, State::ReadConfirmAddressLSB); + FIXED_REPLY_STATE(State::ReadConfirmAddressLSB, Truncate8(m_address), true, State::ReadData); + + case State::ReadData: + { + const u8 bits = m_data[ZeroExtend32(m_address) * SECTOR_SIZE + m_sector_offset]; + if (m_sector_offset == 0) + { + Log_DebugPrintf("Reading memory card sector %u", ZeroExtend32(m_address)); + m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ bits; + } + else + { + m_checksum ^= bits; + } + + *data_out = bits; + ack = true; + + m_sector_offset++; + if (m_sector_offset == SECTOR_SIZE) + { + m_state = State::ReadChecksum; + m_sector_offset = 0; + } + } + break; + + FIXED_REPLY_STATE(State::ReadChecksum, m_checksum, true, State::ReadEnd); + FIXED_REPLY_STATE(State::ReadEnd, 0x47, false, State::Idle); + + // write state + + FIXED_REPLY_STATE(State::WriteCardID1, 0x5A, true, State::WriteCardID2); + FIXED_REPLY_STATE(State::WriteCardID2, 0x5D, true, State::WriteAddressMSB); + ADDRESS_STATE_MSB(State::WriteAddressMSB, State::WriteAddressLSB); + ADDRESS_STATE_LSB(State::WriteAddressLSB, State::WriteData); + + case State::WriteData: + { + if (m_sector_offset == 0) + { + Log_DebugPrintf("Writing memory card sector %u", ZeroExtend32(m_address)); + m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ data_in; + } + else + { + m_checksum ^= data_in; + } + + m_data[ZeroExtend32(m_address) * SECTOR_SIZE + m_sector_offset] = data_in; + *data_out = m_last_byte; + ack = true; + + m_sector_offset++; + if (m_sector_offset == SECTOR_SIZE) + { + m_state = State::WriteChecksum; + m_sector_offset = 0; + } + } + break; + + FIXED_REPLY_STATE(State::WriteChecksum, m_checksum, true, State::WriteACK1); + FIXED_REPLY_STATE(State::WriteACK1, 0x5C, true, State::WriteACK2); + FIXED_REPLY_STATE(State::WriteACK2, 0x5D, true, State::WriteEnd); + FIXED_REPLY_STATE(State::WriteEnd, 0x47, false, State::Idle); + + // new command + case State::Idle: + { + switch (data_in) + { + case 0x81: // tests if the controller is present + { + // response is hi-z + *data_out = 0xFF; + ack = true; + } + break; + + case 0x52: // read data + { + *data_out = m_FLAG.bits; + ack = true; + m_state = State::ReadCardID1; + } + break; + + case 0x57: // write data + { + *data_out = m_FLAG.bits; + ack = true; + m_state = State::WriteCardID1; + } + break; + + case 0x53: // get id + { + Panic("implement me"); + } + break; + + default: + { + *data_out = m_FLAG.bits; + ack = false; + } + } + } + break; + + default: + UnreachableCode(); + break; + } + + Log_DebugPrintf("Transfer, old_state=%u, new_state=%u, data_in=0x%02X, data_out=0x%02X, ack=%s", + static_cast(old_state), static_cast(m_state), data_in, *data_out, ack ? "true" : "false"); + m_last_byte = data_in; + return ack; +} + +std::shared_ptr MemoryCard::Create() +{ + return std::make_shared(); +} diff --git a/src/pse/memory_card.h b/src/pse/memory_card.h new file mode 100644 index 000000000..272fbfd9e --- /dev/null +++ b/src/pse/memory_card.h @@ -0,0 +1,70 @@ +#pragma once +#include "common/bitfield.h" +#include "pad_device.h" +#include +#include + +class MemoryCard final : public PadDevice +{ +public: + enum : u32 + { + DATA_SIZE = 128 * 1024, // 1mbit + SECTOR_SIZE = 128, + NUM_SECTORS = DATA_SIZE / SECTOR_SIZE + }; + + MemoryCard(); + ~MemoryCard() override; + + static std::shared_ptr Create(); + + void ResetTransferState() override; + bool Transfer(const u8 data_in, u8* data_out) override; + +private: + union FLAG + { + u8 bits; + + BitField no_write_yet; + BitField write_error; + }; + + FLAG m_FLAG = {}; + + enum class State : u8 + { + Idle, + + ReadCardID1, + ReadCardID2, + ReadAddressMSB, + ReadAddressLSB, + ReadACK1, + ReadACK2, + ReadConfirmAddressMSB, + ReadConfirmAddressLSB, + ReadData, + ReadChecksum, + ReadEnd, + + WriteCardID1, + WriteCardID2, + WriteAddressMSB, + WriteAddressLSB, + WriteData, + WriteChecksum, + WriteACK1, + WriteACK2, + WriteEnd, + }; + + State m_state = State::Idle; + u16 m_address = 0; + u8 m_sector_offset = 0; + u8 m_checksum = 0; + u8 m_last_byte = 0; + + std::array m_data{}; +}; diff --git a/src/pse/pad.cpp b/src/pse/pad.cpp index 9b0538e7c..beea457e0 100644 --- a/src/pse/pad.cpp +++ b/src/pse/pad.cpp @@ -107,6 +107,9 @@ void Pad::WriteRegister(u32 offset, u32 value) m_JOY_STAT.INTR = false; } + if (!m_JOY_CTRL.SELECT) + ResetDeviceTransferState(); + if (!m_JOY_CTRL.SELECT || !m_JOY_CTRL.TXEN) { if (IsTransmitting()) @@ -211,25 +214,62 @@ void Pad::DoTransfer() { Log_DebugPrintf("Transferring slot %d", m_JOY_CTRL.SLOT.GetValue()); - const std::shared_ptr& dev = m_devices[m_JOY_CTRL.SLOT]; - if (!dev || !CanTransfer()) - { - // no device present, don't set ACK and read hi-z - m_TX_FIFO.Clear(); - m_RX_FIFO.Clear(); - m_RX_FIFO.Push(0xFF); - UpdateJoyStat(); - EndTransfer(); - return; - } + const std::shared_ptr& controller = m_controllers[m_JOY_CTRL.SLOT]; + const std::shared_ptr& memory_card = m_memory_cards[m_JOY_CTRL.SLOT]; // set rx? m_JOY_CTRL.RXEN = true; const u8 data_out = m_TX_FIFO.Pop(); - u8 data_in; - m_JOY_STAT.ACKINPUT |= dev->Transfer(data_out, &data_in); + u8 data_in = 0xFF; + bool ack = false; + + switch (m_active_device) + { + case ActiveDevice::None: + { + if (!controller || !(ack = controller->Transfer(data_out, &data_in))) + { + if (!memory_card || !(ack = memory_card->Transfer(data_out, &data_in))) + { + // nothing connected to this port + Log_DebugPrintf("Nothing connected or ACK'ed"); + } + else + { + // memory card responded, make it the active device until non-ack + m_active_device = ActiveDevice::MemoryCard; + } + } + else + { + // controller responded, make it the active device until non-ack + m_active_device = ActiveDevice::Controller; + } + } + break; + + case ActiveDevice::Controller: + { + if (controller) + ack = controller->Transfer(data_out, &data_in); + } + break; + + case ActiveDevice::MemoryCard: + { + if (memory_card) + ack = memory_card->Transfer(data_out, &data_in); + } + break; + } + m_RX_FIFO.Push(data_in); + m_JOY_STAT.ACKINPUT |= ack; + + // device no longer active? + if (!ack) + m_active_device = ActiveDevice::None; if (m_JOY_STAT.ACKINPUT && m_JOY_CTRL.ACKINTEN) { @@ -260,3 +300,14 @@ void Pad::EndTransfer() m_state = State::Idle; m_ticks_remaining = 0; } + +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(); + } +} diff --git a/src/pse/pad.h b/src/pse/pad.h index faf4757bb..3067d9cc0 100644 --- a/src/pse/pad.h +++ b/src/pse/pad.h @@ -21,8 +21,11 @@ public: void Reset(); bool DoState(StateWrapper& sw); - PadDevice* GetDevice(u32 slot) { return m_devices[slot].get(); } - void SetDevice(u32 slot, std::shared_ptr dev) { m_devices[slot] = dev; } + PadDevice* GetController(u32 slot) { return m_controllers[slot].get(); } + void SetController(u32 slot, std::shared_ptr dev) { m_controllers[slot] = dev; } + + PadDevice* GetMemoryCard(u32 slot) { return m_memory_cards[slot].get(); } + void SetMemoryCard(u32 slot, std::shared_ptr dev) { m_memory_cards[slot] = dev; } u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); @@ -31,7 +34,7 @@ public: private: static constexpr u32 NUM_SLOTS = 2; - static constexpr u32 TRANSFER_TICKS = 750; + static constexpr u32 TRANSFER_TICKS = 550; enum class State : u32 { @@ -39,6 +42,13 @@ private: Transmitting }; + enum class ActiveDevice : u8 + { + None, + Controller, + MemoryCard + }; + union JOY_CTRL { u16 bits; @@ -89,6 +99,7 @@ private: void BeginTransfer(); void DoTransfer(); void EndTransfer(); + void ResetDeviceTransferState(); System* m_system = nullptr; InterruptController* m_interrupt_controller = nullptr; @@ -100,8 +111,10 @@ private: JOY_STAT m_JOY_STAT = {}; JOY_MODE m_JOY_MODE = {}; + ActiveDevice m_active_device = ActiveDevice::None; InlineFIFOQueue m_RX_FIFO; InlineFIFOQueue m_TX_FIFO; - std::array, NUM_SLOTS> m_devices; + std::array, NUM_SLOTS> m_controllers; + std::array, NUM_SLOTS> m_memory_cards; }; diff --git a/src/pse/pad_device.cpp b/src/pse/pad_device.cpp index 5b3c67f46..5f114ebad 100644 --- a/src/pse/pad_device.cpp +++ b/src/pse/pad_device.cpp @@ -4,6 +4,8 @@ PadDevice::PadDevice() = default; PadDevice::~PadDevice() = default; +void PadDevice::ResetTransferState() {} + bool PadDevice::Transfer(const u8 data_in, u8* data_out) { *data_out = 0xFF; diff --git a/src/pse/pad_device.h b/src/pse/pad_device.h index 268766439..e3fa55e43 100644 --- a/src/pse/pad_device.h +++ b/src/pse/pad_device.h @@ -7,6 +7,9 @@ public: PadDevice(); virtual ~PadDevice(); + // Resets all state for the transferring to/from the device. + virtual void ResetTransferState(); + // Returns the value of ACK, as well as filling out_data. virtual bool Transfer(const u8 data_in, u8* data_out); }; diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index 444accb8e..4a2f4411b 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -48,6 +48,7 @@ + @@ -70,6 +71,7 @@ + diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index 0f99affda..67d1d3705 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -19,6 +19,7 @@ + @@ -43,6 +44,7 @@ + diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 0b9eee98f..760ad68c0 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -284,9 +284,14 @@ void System::SetDowncount(TickCount downcount) m_cpu->SetDowncount(downcount); } -void System::SetPadDevice(u32 slot, std::shared_ptr dev) +void System::SetController(u32 slot, std::shared_ptr dev) { - m_pad->SetDevice(slot, std::move(dev)); + m_pad->SetController(slot, std::move(dev)); +} + +void System::SetMemoryCard(u32 slot, std::shared_ptr dev) +{ + m_pad->SetMemoryCard(slot, std::move(dev)); } bool System::HasMedia() const diff --git a/src/pse/system.h b/src/pse/system.h index ab065b3d9..afb5f6228 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -51,7 +51,8 @@ public: void SetDowncount(TickCount downcount); void Synchronize(); - void SetPadDevice(u32 slot, std::shared_ptr dev); + void SetController(u32 slot, std::shared_ptr dev); + void SetMemoryCard(u32 slot, std::shared_ptr dev); bool HasMedia() const; bool InsertMedia(const char* path);