Implement memory cards

This commit is contained in:
Connor McLaughlin 2019-09-30 01:07:38 +10:00
parent 314fad27f1
commit 71022e9cca
16 changed files with 386 additions and 34 deletions

View File

@ -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 <cinttypes>
#include <glad.h>
@ -164,6 +165,7 @@ std::unique_ptr<SDLInterface> 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)
{

View File

@ -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<DigitalController> m_controller;
std::shared_ptr<MemoryCard> m_memory_card;
float m_vps = 0.0f;
float m_fps = 0.0f;

View File

@ -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;

View File

@ -14,6 +14,11 @@ void DigitalController::SetButtonState(Button button, bool pressed)
m_button_state |= u16(1) << static_cast<u8>(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> DigitalController::Create()
{
return std::make_shared<DigitalController>();
}

View File

@ -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:

View File

@ -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];

193
src/pse/memory_card.cpp Normal file
View File

@ -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<u32>(old_state), static_cast<u32>(m_state), data_in, *data_out, ack ? "true" : "false");
m_last_byte = data_in;
return ack;
}
std::shared_ptr<MemoryCard> MemoryCard::Create()
{
return std::make_shared<MemoryCard>();
}

70
src/pse/memory_card.h Normal file
View File

@ -0,0 +1,70 @@
#pragma once
#include "common/bitfield.h"
#include "pad_device.h"
#include <memory>
#include <array>
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<MemoryCard> Create();
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
private:
union FLAG
{
u8 bits;
BitField<u8, bool, 3, 1> no_write_yet;
BitField<u8, bool, 2, 1> 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<u8, DATA_SIZE> m_data{};
};

View File

@ -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<PadDevice>& 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<PadDevice>& controller = m_controllers[m_JOY_CTRL.SLOT];
const std::shared_ptr<PadDevice>& 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();
}
}

View File

@ -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<PadDevice> dev) { m_devices[slot] = dev; }
PadDevice* GetController(u32 slot) { return m_controllers[slot].get(); }
void SetController(u32 slot, std::shared_ptr<PadDevice> dev) { m_controllers[slot] = dev; }
PadDevice* GetMemoryCard(u32 slot) { return m_memory_cards[slot].get(); }
void SetMemoryCard(u32 slot, std::shared_ptr<PadDevice> 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<u8, 8> m_RX_FIFO;
InlineFIFOQueue<u8, 2> m_TX_FIFO;
std::array<std::shared_ptr<PadDevice>, NUM_SLOTS> m_devices;
std::array<std::shared_ptr<PadDevice>, NUM_SLOTS> m_controllers;
std::array<std::shared_ptr<PadDevice>, NUM_SLOTS> m_memory_cards;
};

View File

@ -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;

View File

@ -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);
};

View File

@ -48,6 +48,7 @@
<ClCompile Include="host_interface.cpp" />
<ClCompile Include="interrupt_controller.cpp" />
<ClCompile Include="mdec.cpp" />
<ClCompile Include="memory_card.cpp" />
<ClCompile Include="pad.cpp" />
<ClCompile Include="pad_device.cpp" />
<ClCompile Include="spu.cpp" />
@ -70,6 +71,7 @@
<ClInclude Include="host_interface.h" />
<ClInclude Include="interrupt_controller.h" />
<ClInclude Include="mdec.h" />
<ClInclude Include="memory_card.h" />
<ClInclude Include="pad.h" />
<ClInclude Include="pad_device.h" />
<ClInclude Include="save_state_version.h" />

View File

@ -19,6 +19,7 @@
<ClCompile Include="timers.cpp" />
<ClCompile Include="spu.cpp" />
<ClCompile Include="mdec.cpp" />
<ClCompile Include="memory_card.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@ -43,6 +44,7 @@
<ClInclude Include="timers.h" />
<ClInclude Include="spu.h" />
<ClInclude Include="mdec.h" />
<ClInclude Include="memory_card.h" />
</ItemGroup>
<ItemGroup>
<None Include="cpu_core.inl" />

View File

@ -284,9 +284,14 @@ void System::SetDowncount(TickCount downcount)
m_cpu->SetDowncount(downcount);
}
void System::SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev)
void System::SetController(u32 slot, std::shared_ptr<PadDevice> dev)
{
m_pad->SetDevice(slot, std::move(dev));
m_pad->SetController(slot, std::move(dev));
}
void System::SetMemoryCard(u32 slot, std::shared_ptr<PadDevice> dev)
{
m_pad->SetMemoryCard(slot, std::move(dev));
}
bool System::HasMedia() const

View File

@ -51,7 +51,8 @@ public:
void SetDowncount(TickCount downcount);
void Synchronize();
void SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev);
void SetController(u32 slot, std::shared_ptr<PadDevice> dev);
void SetMemoryCard(u32 slot, std::shared_ptr<PadDevice> dev);
bool HasMedia() const;
bool InsertMedia(const char* path);