Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
362 changes: 333 additions & 29 deletions src/core/memorycard.cc

Large diffs are not rendered by default.

158 changes: 129 additions & 29 deletions src/core/memorycard.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@

#include <stdint.h>

#include "core/psxemulator.h"
#include "core/sstate.h"

namespace PCSX {
class SIO;
class Memorycards;

/// <summary>
/// Implements a memory card for SIO
/// </summary>
class MemoryCard {
public:
MemoryCard() : m_sio(nullptr) { memset(m_mcdData, 0, c_cardSize); }
MemoryCard(SIO *parent) : m_sio(parent) { memset(m_mcdData, 0, c_cardSize); }
MemoryCard(uint8_t device_index) : m_deviceIndex(device_index){}

// Hardware events
void acknowledge();

void deselect() {
memset(&m_tempBuffer, 0, c_sectorSize);
m_currentCommand = Commands::None;
Expand All @@ -41,21 +45,16 @@ class MemoryCard {
m_sector = 0;
m_spdr = Responses::IdleHighZ;
}
void reset() {
deselect();
m_directoryFlag = Flags::DirectoryUnread;
}

void setPocketstationEnabled(bool enabled) { m_pocketstationEnabled = enabled; };

// File system / data manipulation
void commit(const PCSX::u8string path) {
if (!m_savedToDisk) {
saveMcd(path);
}
}
void createMcd(PCSX::u8string mcd);
bool dataChanged() { return !m_savedToDisk; }
void disablePocketstation() { m_pocketstationEnabled = false; };
void enablePocketstation() { m_pocketstationEnabled = true; };
void commit();
char *getMcdData() { return m_mcdData; }
void loadMcd(PCSX::u8string mcd);
void saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size);
void saveMcd(PCSX::u8string mcd) { saveMcd(mcd, m_mcdData, 0, c_cardSize); }

private:
enum Commands : uint8_t {
Expand Down Expand Up @@ -97,21 +96,21 @@ class MemoryCard {
friend class SIO;
friend SaveStates::SaveState SaveStates::constructSaveState();

static constexpr size_t c_sectorSize = 8 * 16;
static constexpr size_t c_blockSize = 8192;
static constexpr size_t c_cardSize = 1024 * c_sectorSize;
static constexpr size_t c_sectorSize = 8 * 16; // 80h bytes per sector/frame
static constexpr size_t c_blockSize = c_sectorSize * 64; // 40h sectors per block
static constexpr size_t c_cardSize = c_blockSize * 16; // 16 blocks per frame(directory+15 saves);

// State machine / handlers
uint8_t transceive(uint8_t value);
uint8_t tickReadCommand(uint8_t value);
uint8_t tickWriteCommand(uint8_t value);
uint8_t tickPS_GetDirIndex(uint8_t value); // 5Ah
uint8_t tickPS_GetVersion(uint8_t value); // 58h
uint8_t tickPS_PrepFileExec(uint8_t value); // 59h
uint8_t tickPS_ExecCustom(uint8_t value); // 5Dh

char m_mcdData[c_cardSize];
uint8_t m_tempBuffer[c_sectorSize];
uint8_t transceive(uint8_t value, bool *ack); // *
uint8_t tickReadCommand(uint8_t value, bool *ack); // 52h
uint8_t tickWriteCommand(uint8_t value, bool *ack); // 57h
uint8_t tickPS_GetDirIndex(uint8_t value, bool *ack); // 5Ah
uint8_t tickPS_GetVersion(uint8_t value, bool *ack); // 58h
uint8_t tickPS_PrepFileExec(uint8_t value, bool *ack); // 59h
uint8_t tickPS_ExecCustom(uint8_t value, bool *ack); // 5Dh

char m_mcdData[c_cardSize] = {0};
uint8_t m_tempBuffer[c_blockSize] = {0};
bool m_savedToDisk = false;

uint8_t m_checksumIn = 0, m_checksumOut = 0;
Expand All @@ -128,7 +127,108 @@ class MemoryCard {
bool m_pocketstationEnabled = false;
uint16_t m_directoryIndex = 0;

SIO *m_sio;
SIO *m_sio = nullptr;
uint8_t m_deviceIndex = 0;
};

/// <summary>
/// Helper functions for MemoryCard class, gui, and filesystem
/// </summary>
class MemoryCards {
public:
void deselect() {
m_memoryCard[0].deselect();
m_memoryCard[1].deselect();
}

void reset() {
m_memoryCard[0].reset();
m_memoryCard[1].reset();
}

struct McdBlock {
McdBlock() { reset(); }
int mcd;
int number;
std::string titleAscii;
std::string titleSjis;
std::string titleUtf8;
std::string id;
std::string name;
uint32_t fileSize;
uint32_t iconCount;
uint16_t icon[16 * 16 * 3];
uint32_t allocState;
int16_t nextBlock;
void reset() {
mcd = 0;
number = 0;
titleAscii.clear();
titleSjis.clear();
titleUtf8.clear();
id.clear();
name.clear();
fileSize = 0;
iconCount = 0;
memset(icon, 0, sizeof(icon));
allocState = 0;
nextBlock = -1;
}
bool isErased() const { return (allocState & 0xa0) == 0xa0; }
bool isChained() const { return (allocState & ~1) == 0x52; }
};

static constexpr size_t c_sectorSize = 8 * 16; // 80h bytes per sector/frame
static constexpr size_t c_blockSize = c_sectorSize * 64; // 40h sectors per block
static constexpr size_t c_cardSize = c_blockSize * 16; // 16 blocks per frame(directory+15 saves)

bool copyMcdFile(McdBlock block);
void eraseMcdFile(const McdBlock &block);
void eraseMcdFile(int mcd, int block) {
McdBlock info;
getMcdBlockInfo(mcd, block, info);
eraseMcdFile(info);
}
int findFirstFree(int mcd);
unsigned getFreeSpace(int mcd);
unsigned getFileBlockCount(McdBlock block);
void getMcdBlockInfo(int mcd, int block, McdBlock &info);
char *getMcdData(int mcd);
char *getMcdData(const McdBlock &block) { return getMcdData(block.mcd); }

// File operations
void createMcd(PCSX::u8string mcd);
void loadMcds(const CommandLine::args &args);
bool saveMcd(int card_index);

bool loadMcd(PCSX::u8string mcd, char *data);
bool saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size);

static constexpr int otherMcd(int mcd) {
if ((mcd != 0) && (mcd != 1)) throw std::runtime_error("Bad memory card number");
if (mcd == 0) return 1;
return 0;
}

PCSX::u8string getMcdPath(int index) {
std::filesystem::path *paths[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1>().value,
&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2>().value};

PCSX::u8string thepath = paths[index]->u8string();
return thepath;
}
bool isCardInserted(int index) {
bool *const inserted_lut[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1Inserted>().value,
&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2Inserted>().value};

return *inserted_lut[index];
}

static constexpr int otherMcd(const McdBlock &block) { return otherMcd(block.mcd); }
void resetCard(int index);
void setPocketstationEnabled(int index, bool enabled);

MemoryCard m_memoryCard[2] = {MemoryCard(0), MemoryCard(1)};
};

} // namespace PCSX
75 changes: 64 additions & 11 deletions src/core/pad.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ class PadsImpl : public PCSX::Pads {
PadsImpl();
void init() override;
void shutdown() override;
uint8_t startPoll(Port port) override;
uint8_t poll(uint8_t value, Port port, uint32_t& padState) override;

json getCfg() override;
void setCfg(const json& j) override;
Expand All @@ -63,10 +61,18 @@ class PadsImpl : public PCSX::Pads {
if (pad > m_pads.size()) {
return false;
} else {
return m_pads[pad - 1].isControllerConnected();
return m_pads[pad].isControllerConnected();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Index change affects isPadConnected behavior.

The pad index used to access m_pads has changed from pad - 1 to pad. This changes the expected indexing for controller access from 1-based to 0-based, which could affect code calling this method.

Verify that all callers of this method are using the correct indexing scheme:

#!/bin/bash
# Find all references to isPadConnected
rg "isPadConnected" -A 1 -B 1

}
}

void deselect() {
for (int i = 0; i < m_pads.size(); i++) {
m_pads[i].deselect();
}
}

uint8_t transceive(int index, uint8_t value, bool* ack) override { return m_pads[index].transceive(value, ack); }

private:
PCSX::EventBus::Listener m_listener;
// This is a list of all of the valid GLFW gamepad IDs that we have found querying GLFW.
Expand Down Expand Up @@ -168,6 +174,12 @@ class PadsImpl : public PCSX::Pads {
bool isControllerButtonPressed(int button, GLFWgamepadstate* state);
bool isControllerConnected() { return m_settings.get<SettingConnected>(); }

void deselect() {
m_bufferIndex = 0;
m_padState = Pads::PAD_STATE_IDLE;
}
uint8_t transceive(uint8_t value, bool* ack);

json getCfg();
void setCfg(const json& j);
void setDefaults(bool firstController);
Expand Down Expand Up @@ -199,6 +211,13 @@ class PadsImpl : public PCSX::Pads {
uint8_t m_stdpar[8] = {0x41, 0x5a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
uint8_t m_mousepar[6] = {0x12, 0x5a, 0xff, 0xff, 0xff, 0xff};
uint8_t m_analogpar[8] = {0x73, 0x5a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

static constexpr size_t c_padBufferSize = 0x1010;

uint8_t m_buffer[c_padBufferSize];
uint32_t m_bufferIndex = 0;
uint32_t m_maxBufferIndex = 0;
uint32_t m_padState = Pads::PAD_STATE_IDLE;
};

std::array<Pad, 2> m_pads;
Expand Down Expand Up @@ -678,15 +697,49 @@ void PadsImpl::Pad::getButtons() {
pad.buttonStatus = result ^ 0xffff; // Controls are inverted, so 0 = pressed
}

uint8_t PadsImpl::startPoll(Port port) {
int index = magic_enum::enum_integer(port);
m_pads[index].getButtons();
return m_pads[index].startPoll();
}
uint8_t PadsImpl::Pad::transceive(uint8_t value, bool* ack) {
uint8_t data_out = 0xff;

switch (m_padState) {
case Pads::PAD_STATE_IDLE: // start pad
getButtons();
m_buffer[0] = startPoll();
m_maxBufferIndex = 2;
m_bufferIndex = 0;
m_padState = Pads::PAD_STATE_READ_COMMAND;
break;

case Pads::PAD_STATE_READ_COMMAND:
m_padState = Pads::PAD_STATE_READ_DATA;
m_bufferIndex = 1;
m_buffer[m_bufferIndex] = poll(value, m_padState);

if (!(m_buffer[m_bufferIndex] & 0x0f)) {
m_maxBufferIndex = 2 + 32;
} else {
m_maxBufferIndex = 2 + (m_buffer[m_bufferIndex] & 0x0f) * 2;
}

break;

case Pads::PAD_STATE_READ_DATA:
m_bufferIndex++;
m_buffer[m_bufferIndex] = poll(value, m_padState);

if (m_bufferIndex == m_maxBufferIndex) {
m_padState = Pads::PAD_STATE_BAD_COMMAND;
}
break;
}

data_out = m_buffer[m_bufferIndex];

if (m_padState == Pads::PAD_STATE_BAD_COMMAND) {
} else {
*ack = true;
}

uint8_t PadsImpl::poll(uint8_t value, Port port, uint32_t& padState) {
int index = magic_enum::enum_integer(port);
return m_pads[index].poll(value, padState);
return data_out;
}

uint8_t PadsImpl::Pad::poll(uint8_t value, uint32_t& padState) {
Expand Down
12 changes: 10 additions & 2 deletions src/core/pad.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,25 @@ using json = nlohmann::json;
namespace PCSX {

class GUI;
class SIO;

class Pads {
public:
enum class Port { Port1 = 0, Port2 };

virtual ~Pads() = default;

class InputDevice {
virtual void* getPadState() = 0;
virtual bool isButtonPressed(int button) = 0;
virtual void updateInput() = 0;
};

virtual void init() = 0;
virtual void shutdown() = 0;
virtual uint8_t startPoll(Port port) = 0;
virtual uint8_t poll(uint8_t value, Port port, uint32_t& padState) = 0;

virtual void deselect() = 0;
virtual uint8_t transceive(int index, uint8_t value, bool* ack) = 0;

virtual json getCfg() = 0;
virtual void setCfg(const json& j) = 0;
Expand Down
1 change: 1 addition & 0 deletions src/core/psxemulator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ PCSX::Emulator::Emulator()
m_lua(new PCSX::Lua()),
m_mdec(new PCSX::MDEC()),
m_mem(new PCSX::Memory()),
m_memoryCards(new PCSX::MemoryCards()),
m_pads(PCSX::Pads::factory()),
m_pioCart(new PCSX::PIOCart),
m_sio(new PCSX::SIO()),
Expand Down
2 changes: 2 additions & 0 deletions src/core/psxemulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class HW;
class Lua;
class MDEC;
class Memory;
class MemoryCards;
class Pads;
class R3000Acpu;
class SIO;
Expand Down Expand Up @@ -248,6 +249,7 @@ class Emulator {
std::unique_ptr<Lua> m_lua;
std::unique_ptr<MDEC> m_mdec;
std::unique_ptr<Memory> m_mem;
std::unique_ptr<MemoryCards> m_memoryCards;
std::unique_ptr<Pads> m_pads;
std::unique_ptr<PIOCart> m_pioCart;
std::unique_ptr<R3000Acpu> m_cpu;
Expand Down
Loading