mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-25 07:21:14 +01:00
Merge pull request #9268 from leoetlino/devicememcard-minor-cleanup
EXI_DeviceMemoryCard: Medium cleanup
This commit is contained in:
commit
140daf5960
@ -34,7 +34,6 @@
|
||||
#include "Core/HW/Sram.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "DiscIO/Enums.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
@ -48,7 +47,7 @@ namespace ExpansionInterface
|
||||
#define SIZE_TO_Mb (1024 * 8 * 16)
|
||||
|
||||
static const u32 MC_TRANSFER_RATE_READ = 512 * 1024;
|
||||
static const u32 MC_TRANSFER_RATE_WRITE = (u32)(96.125f * 1024.0f);
|
||||
static const auto MC_TRANSFER_RATE_WRITE = static_cast<u32>(96.125f * 1024.0f);
|
||||
|
||||
static std::array<CoreTiming::EventType*, 2> s_et_cmd_done;
|
||||
static std::array<CoreTiming::EventType*, 2> s_et_transfer_complete;
|
||||
@ -59,24 +58,25 @@ void CEXIMemoryCard::EventCompleteFindInstance(u64 userdata,
|
||||
std::function<void(CEXIMemoryCard*)> callback)
|
||||
{
|
||||
int card_index = (int)userdata;
|
||||
CEXIMemoryCard* pThis =
|
||||
(CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index);
|
||||
if (pThis == nullptr)
|
||||
auto* self = static_cast<CEXIMemoryCard*>(
|
||||
ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index));
|
||||
if (self == nullptr)
|
||||
{
|
||||
pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARDFOLDER, card_index);
|
||||
self = static_cast<CEXIMemoryCard*>(
|
||||
ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARDFOLDER, card_index));
|
||||
}
|
||||
if (pThis)
|
||||
if (self)
|
||||
{
|
||||
callback(pThis);
|
||||
callback(self);
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::CmdDoneCallback(u64 userdata, s64 cyclesLate)
|
||||
void CEXIMemoryCard::CmdDoneCallback(u64 userdata, s64)
|
||||
{
|
||||
EventCompleteFindInstance(userdata, [](CEXIMemoryCard* instance) { instance->CmdDone(); });
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::TransferCompleteCallback(u64 userdata, s64 cyclesLate)
|
||||
void CEXIMemoryCard::TransferCompleteCallback(u64 userdata, s64)
|
||||
{
|
||||
EventCompleteFindInstance(userdata,
|
||||
[](CEXIMemoryCard* instance) { instance->TransferComplete(); });
|
||||
@ -106,9 +106,9 @@ void CEXIMemoryCard::Shutdown()
|
||||
s_et_transfer_complete.fill(nullptr);
|
||||
}
|
||||
|
||||
CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder,
|
||||
CEXIMemoryCard::CEXIMemoryCard(const int index, bool gci_folder,
|
||||
const Memcard::HeaderData& header_data)
|
||||
: card_index(index)
|
||||
: m_card_index(index)
|
||||
{
|
||||
ASSERT_MSG(EXPANSIONINTERFACE, static_cast<std::size_t>(index) < s_et_cmd_done.size(),
|
||||
"Trying to create invalid memory card index %d.", index);
|
||||
@ -116,12 +116,12 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder,
|
||||
// NOTE: When loading a save state, DMA completion callbacks (s_et_transfer_complete) and such
|
||||
// may have been restored, we need to anticipate those arriving.
|
||||
|
||||
interruptSwitch = 0;
|
||||
m_bInterruptSet = 0;
|
||||
command = 0;
|
||||
status = MC_STATUS_BUSY | MC_STATUS_UNLOCKED | MC_STATUS_READY;
|
||||
m_uPosition = 0;
|
||||
memset(programming_buffer, 0, sizeof(programming_buffer));
|
||||
m_interrupt_switch = 0;
|
||||
m_interrupt_set = false;
|
||||
m_command = Command::NintendoID;
|
||||
m_status = MC_STATUS_BUSY | MC_STATUS_UNLOCKED | MC_STATUS_READY;
|
||||
m_position = 0;
|
||||
m_programming_buffer.fill(0);
|
||||
// Nintendo Memory Card EXI IDs
|
||||
// 0x00000004 Memory Card 59 4Mbit
|
||||
// 0x00000008 Memory Card 123 8Mb
|
||||
@ -132,9 +132,9 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder,
|
||||
|
||||
// 0x00000510 16Mb "bigben" card
|
||||
// card_id = 0xc243;
|
||||
card_id = 0xc221; // It's a Nintendo brand memcard
|
||||
m_card_id = 0xc221; // It's a Nintendo brand memcard
|
||||
|
||||
if (gciFolder)
|
||||
if (gci_folder)
|
||||
{
|
||||
SetupGciFolder(header_data);
|
||||
}
|
||||
@ -143,10 +143,10 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder,
|
||||
SetupRawMemcard(header_data.m_size_mb);
|
||||
}
|
||||
|
||||
memory_card_size = memorycard->GetCardId() * SIZE_TO_Mb;
|
||||
m_memory_card_size = m_memory_card->GetCardId() * SIZE_TO_Mb;
|
||||
std::array<u8, 20> header{};
|
||||
memorycard->Read(0, static_cast<s32>(header.size()), header.data());
|
||||
SetCardFlashID(header.data(), card_index);
|
||||
m_memory_card->Read(0, static_cast<s32>(header.size()), header.data());
|
||||
SetCardFlashID(header.data(), m_card_index);
|
||||
}
|
||||
|
||||
std::pair<std::string /* path */, bool /* migrate */>
|
||||
@ -178,57 +178,57 @@ CEXIMemoryCard::GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_fo
|
||||
void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data)
|
||||
{
|
||||
const std::string& game_id = SConfig::GetInstance().GetGameID();
|
||||
u32 CurrentGameId = 0;
|
||||
u32 current_game_id = 0;
|
||||
if (game_id.length() >= 4 && game_id != "00000000" &&
|
||||
SConfig::GetInstance().GetTitleID() != Titles::SYSTEM_MENU)
|
||||
{
|
||||
CurrentGameId = Common::swap32(reinterpret_cast<const u8*>(game_id.c_str()));
|
||||
current_game_id = Common::swap32(reinterpret_cast<const u8*>(game_id.c_str()));
|
||||
}
|
||||
|
||||
// TODO(C++20): Use structured bindings when we can use C++20 and refer to structured bindings
|
||||
// in lambda captures
|
||||
const auto folder_path = GetGCIFolderPath(card_index, AllowMovieFolder::Yes);
|
||||
const auto& strDirectoryName = folder_path.first;
|
||||
const bool migrate = folder_path.second;
|
||||
const auto folder_path_pair = GetGCIFolderPath(m_card_index, AllowMovieFolder::Yes);
|
||||
const std::string& dir_path = folder_path_pair.first;
|
||||
const bool migrate = folder_path_pair.second;
|
||||
|
||||
const File::FileInfo file_info(strDirectoryName);
|
||||
const File::FileInfo file_info(dir_path);
|
||||
if (!file_info.Exists())
|
||||
{
|
||||
if (migrate) // first use of memcard folder, migrate automatically
|
||||
MigrateFromMemcardFile(strDirectoryName + DIR_SEP, card_index);
|
||||
MigrateFromMemcardFile(dir_path + DIR_SEP, m_card_index);
|
||||
else
|
||||
File::CreateFullPath(strDirectoryName + DIR_SEP);
|
||||
File::CreateFullPath(dir_path + DIR_SEP);
|
||||
}
|
||||
else if (!file_info.IsDirectory())
|
||||
{
|
||||
if (File::Rename(strDirectoryName, strDirectoryName + ".original"))
|
||||
if (File::Rename(dir_path, dir_path + ".original"))
|
||||
{
|
||||
PanicAlertFmtT("{0} was not a directory, moved to *.original", strDirectoryName);
|
||||
PanicAlertFmtT("{0} was not a directory, moved to *.original", dir_path);
|
||||
if (migrate)
|
||||
MigrateFromMemcardFile(strDirectoryName + DIR_SEP, card_index);
|
||||
MigrateFromMemcardFile(dir_path + DIR_SEP, m_card_index);
|
||||
else
|
||||
File::CreateFullPath(strDirectoryName + DIR_SEP);
|
||||
File::CreateFullPath(dir_path + DIR_SEP);
|
||||
}
|
||||
else // we tried but the user wants to crash
|
||||
{
|
||||
// TODO more user friendly abort
|
||||
PanicAlertFmtT("{0} is not a directory, failed to move to *.original.\n Verify your "
|
||||
"write permissions or move the file outside of Dolphin",
|
||||
strDirectoryName);
|
||||
dir_path);
|
||||
std::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
memorycard = std::make_unique<GCMemcardDirectory>(strDirectoryName + DIR_SEP, card_index,
|
||||
header_data, CurrentGameId);
|
||||
m_memory_card = std::make_unique<GCMemcardDirectory>(dir_path + DIR_SEP, m_card_index,
|
||||
header_data, current_game_id);
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb)
|
||||
void CEXIMemoryCard::SetupRawMemcard(u16 size_mb)
|
||||
{
|
||||
const bool is_slot_a = card_index == 0;
|
||||
const bool is_slot_a = m_card_index == 0;
|
||||
std::string filename = is_slot_a ? Config::Get(Config::MAIN_MEMCARD_A_PATH) :
|
||||
Config::Get(Config::MAIN_MEMCARD_B_PATH);
|
||||
if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(card_index) &&
|
||||
if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(m_card_index) &&
|
||||
Movie::IsStartingFromClearSave())
|
||||
filename = File::GetUserPath(D_GCUSER_IDX) + fmt::format("Movie{}.raw", is_slot_a ? 'A' : 'B');
|
||||
|
||||
@ -236,16 +236,16 @@ void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb)
|
||||
SConfig::GetDirectoryForRegion(SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region));
|
||||
MemoryCard::CheckPath(filename, region_dir, is_slot_a);
|
||||
|
||||
if (sizeMb == Memcard::MBIT_SIZE_MEMORY_CARD_251)
|
||||
filename.insert(filename.find_last_of("."), ".251");
|
||||
if (size_mb == Memcard::MBIT_SIZE_MEMORY_CARD_251)
|
||||
filename.insert(filename.find_last_of('.'), ".251");
|
||||
|
||||
memorycard = std::make_unique<MemoryCard>(filename, card_index, sizeMb);
|
||||
m_memory_card = std::make_unique<MemoryCard>(filename, m_card_index, size_mb);
|
||||
}
|
||||
|
||||
CEXIMemoryCard::~CEXIMemoryCard()
|
||||
{
|
||||
CoreTiming::RemoveEvent(s_et_cmd_done[card_index]);
|
||||
CoreTiming::RemoveEvent(s_et_transfer_complete[card_index]);
|
||||
CoreTiming::RemoveEvent(s_et_cmd_done[m_card_index]);
|
||||
CoreTiming::RemoveEvent(s_et_transfer_complete[m_card_index]);
|
||||
}
|
||||
|
||||
bool CEXIMemoryCard::UseDelayedTransferCompletion() const
|
||||
@ -260,41 +260,41 @@ bool CEXIMemoryCard::IsPresent() const
|
||||
|
||||
void CEXIMemoryCard::CmdDone()
|
||||
{
|
||||
status |= MC_STATUS_READY;
|
||||
status &= ~MC_STATUS_BUSY;
|
||||
m_status |= MC_STATUS_READY;
|
||||
m_status &= ~MC_STATUS_BUSY;
|
||||
|
||||
m_bInterruptSet = 1;
|
||||
m_interrupt_set = true;
|
||||
ExpansionInterface::UpdateInterrupts();
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::TransferComplete()
|
||||
{
|
||||
// Transfer complete, send interrupt
|
||||
ExpansionInterface::GetChannel(card_index)->SendTransferComplete();
|
||||
ExpansionInterface::GetChannel(m_card_index)->SendTransferComplete();
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::CmdDoneLater(u64 cycles)
|
||||
{
|
||||
CoreTiming::RemoveEvent(s_et_cmd_done[card_index]);
|
||||
CoreTiming::ScheduleEvent((int)cycles, s_et_cmd_done[card_index], (u64)card_index);
|
||||
CoreTiming::RemoveEvent(s_et_cmd_done[m_card_index]);
|
||||
CoreTiming::ScheduleEvent(cycles, s_et_cmd_done[m_card_index], m_card_index);
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::SetCS(int cs)
|
||||
{
|
||||
if (cs) // not-selected to selected
|
||||
{
|
||||
m_uPosition = 0;
|
||||
m_position = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (command)
|
||||
switch (m_command)
|
||||
{
|
||||
case cmdSectorErase:
|
||||
if (m_uPosition > 2)
|
||||
case Command::SectorErase:
|
||||
if (m_position > 2)
|
||||
{
|
||||
memorycard->ClearBlock(address & (memory_card_size - 1));
|
||||
status |= MC_STATUS_BUSY;
|
||||
status &= ~MC_STATUS_READY;
|
||||
m_memory_card->ClearBlock(m_address & (m_memory_card_size - 1));
|
||||
m_status |= MC_STATUS_BUSY;
|
||||
m_status &= ~MC_STATUS_READY;
|
||||
|
||||
//???
|
||||
|
||||
@ -302,189 +302,192 @@ void CEXIMemoryCard::SetCS(int cs)
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdChipErase:
|
||||
if (m_uPosition > 2)
|
||||
case Command::ChipErase:
|
||||
if (m_position > 2)
|
||||
{
|
||||
// TODO: Investigate on HW, I (LPFaint99) believe that this only
|
||||
// erases the system area (Blocks 0-4)
|
||||
memorycard->ClearAll();
|
||||
status &= ~MC_STATUS_BUSY;
|
||||
m_memory_card->ClearAll();
|
||||
m_status &= ~MC_STATUS_BUSY;
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdPageProgram:
|
||||
if (m_uPosition >= 5)
|
||||
case Command::PageProgram:
|
||||
if (m_position >= 5)
|
||||
{
|
||||
int count = m_uPosition - 5;
|
||||
int count = m_position - 5;
|
||||
int i = 0;
|
||||
status &= ~MC_STATUS_BUSY;
|
||||
m_status &= ~MC_STATUS_BUSY;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
memorycard->Write(address, 1, &(programming_buffer[i++]));
|
||||
m_memory_card->Write(m_address, 1, &(m_programming_buffer[i++]));
|
||||
i &= 127;
|
||||
address = (address & ~0x1FF) | ((address + 1) & 0x1FF);
|
||||
m_address = (m_address & ~0x1FF) | ((m_address + 1) & 0x1FF);
|
||||
}
|
||||
|
||||
CmdDoneLater(5000);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CEXIMemoryCard::IsInterruptSet()
|
||||
{
|
||||
if (interruptSwitch)
|
||||
return m_bInterruptSet;
|
||||
if (m_interrupt_switch)
|
||||
return m_interrupt_set;
|
||||
return false;
|
||||
}
|
||||
|
||||
void CEXIMemoryCard::TransferByte(u8& byte)
|
||||
{
|
||||
DEBUG_LOG_FMT(EXPANSIONINTERFACE, "EXI MEMCARD: > {:02x}", byte);
|
||||
if (m_uPosition == 0)
|
||||
if (m_position == 0)
|
||||
{
|
||||
command = byte; // first byte is command
|
||||
byte = 0xFF; // would be tristate, but we don't care.
|
||||
m_command = static_cast<Command>(byte); // first byte is command
|
||||
byte = 0xFF; // would be tristate, but we don't care.
|
||||
|
||||
switch (command) // This seems silly, do we really need it?
|
||||
switch (m_command) // This seems silly, do we really need it?
|
||||
{
|
||||
case cmdNintendoID:
|
||||
case cmdReadArray:
|
||||
case cmdArrayToBuffer:
|
||||
case cmdSetInterrupt:
|
||||
case cmdWriteBuffer:
|
||||
case cmdReadStatus:
|
||||
case cmdReadID:
|
||||
case cmdReadErrorBuffer:
|
||||
case cmdWakeUp:
|
||||
case cmdSleep:
|
||||
case cmdClearStatus:
|
||||
case cmdSectorErase:
|
||||
case cmdPageProgram:
|
||||
case cmdExtraByteProgram:
|
||||
case cmdChipErase:
|
||||
case Command::NintendoID:
|
||||
case Command::ReadArray:
|
||||
case Command::ArrayToBuffer:
|
||||
case Command::SetInterrupt:
|
||||
case Command::WriteBuffer:
|
||||
case Command::ReadStatus:
|
||||
case Command::ReadID:
|
||||
case Command::ReadErrorBuffer:
|
||||
case Command::WakeUp:
|
||||
case Command::Sleep:
|
||||
case Command::ClearStatus:
|
||||
case Command::SectorErase:
|
||||
case Command::PageProgram:
|
||||
case Command::ExtraByteProgram:
|
||||
case Command::ChipErase:
|
||||
DEBUG_LOG_FMT(EXPANSIONINTERFACE, "EXI MEMCARD: command {:02x} at position 0. seems normal.",
|
||||
command);
|
||||
m_command);
|
||||
break;
|
||||
default:
|
||||
WARN_LOG_FMT(EXPANSIONINTERFACE, "EXI MEMCARD: command {:02x} at position 0", command);
|
||||
WARN_LOG_FMT(EXPANSIONINTERFACE, "EXI MEMCARD: command {:02x} at position 0", m_command);
|
||||
break;
|
||||
}
|
||||
if (command == cmdClearStatus)
|
||||
if (m_command == Command::ClearStatus)
|
||||
{
|
||||
status &= ~MC_STATUS_PROGRAMEERROR;
|
||||
status &= ~MC_STATUS_ERASEERROR;
|
||||
m_status &= ~MC_STATUS_PROGRAMEERROR;
|
||||
m_status &= ~MC_STATUS_ERASEERROR;
|
||||
|
||||
status |= MC_STATUS_READY;
|
||||
m_status |= MC_STATUS_READY;
|
||||
|
||||
m_bInterruptSet = 0;
|
||||
m_interrupt_set = false;
|
||||
|
||||
byte = 0xFF;
|
||||
m_uPosition = 0;
|
||||
m_position = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (command)
|
||||
switch (m_command)
|
||||
{
|
||||
case cmdNintendoID:
|
||||
case Command::NintendoID:
|
||||
//
|
||||
// Nintendo card:
|
||||
// 00 | 80 00 00 00 10 00 00 00
|
||||
// "bigben" card:
|
||||
// 00 | ff 00 00 05 10 00 00 00 00 00 00 00 00 00 00
|
||||
// we do it the Nintendo way.
|
||||
if (m_uPosition == 1)
|
||||
if (m_position == 1)
|
||||
byte = 0x80; // dummy cycle
|
||||
else
|
||||
byte = (u8)(memorycard->GetCardId() >> (24 - (((m_uPosition - 2) & 3) * 8)));
|
||||
byte = static_cast<u8>(m_memory_card->GetCardId() >> (24 - (((m_position - 2) & 3) * 8)));
|
||||
break;
|
||||
|
||||
case cmdReadArray:
|
||||
switch (m_uPosition)
|
||||
case Command::ReadArray:
|
||||
switch (m_position)
|
||||
{
|
||||
case 1: // AD1
|
||||
address = byte << 17;
|
||||
m_address = byte << 17;
|
||||
byte = 0xFF;
|
||||
break;
|
||||
case 2: // AD2
|
||||
address |= byte << 9;
|
||||
m_address |= byte << 9;
|
||||
break;
|
||||
case 3: // AD3
|
||||
address |= (byte & 3) << 7;
|
||||
m_address |= (byte & 3) << 7;
|
||||
break;
|
||||
case 4: // BA
|
||||
address |= (byte & 0x7F);
|
||||
m_address |= (byte & 0x7F);
|
||||
break;
|
||||
}
|
||||
if (m_uPosition > 1) // not specified for 1..8, anyway
|
||||
if (m_position > 1) // not specified for 1..8, anyway
|
||||
{
|
||||
memorycard->Read(address & (memory_card_size - 1), 1, &byte);
|
||||
m_memory_card->Read(m_address & (m_memory_card_size - 1), 1, &byte);
|
||||
// after 9 bytes, we start incrementing the address,
|
||||
// but only the sector offset - the pointer wraps around
|
||||
if (m_uPosition >= 9)
|
||||
address = (address & ~0x1FF) | ((address + 1) & 0x1FF);
|
||||
if (m_position >= 9)
|
||||
m_address = (m_address & ~0x1FF) | ((m_address + 1) & 0x1FF);
|
||||
}
|
||||
break;
|
||||
|
||||
case cmdReadStatus:
|
||||
case Command::ReadStatus:
|
||||
// (unspecified for byte 1)
|
||||
byte = status;
|
||||
byte = m_status;
|
||||
break;
|
||||
|
||||
case cmdReadID:
|
||||
if (m_uPosition == 1) // (unspecified)
|
||||
byte = (u8)(card_id >> 8);
|
||||
case Command::ReadID:
|
||||
if (m_position == 1) // (unspecified)
|
||||
byte = static_cast<u8>(m_card_id >> 8);
|
||||
else
|
||||
byte = (u8)((m_uPosition & 1) ? (card_id) : (card_id >> 8));
|
||||
byte = static_cast<u8>((m_position & 1) ? (m_card_id) : (m_card_id >> 8));
|
||||
break;
|
||||
|
||||
case cmdSectorErase:
|
||||
switch (m_uPosition)
|
||||
case Command::SectorErase:
|
||||
switch (m_position)
|
||||
{
|
||||
case 1: // AD1
|
||||
address = byte << 17;
|
||||
m_address = byte << 17;
|
||||
break;
|
||||
case 2: // AD2
|
||||
address |= byte << 9;
|
||||
m_address |= byte << 9;
|
||||
break;
|
||||
}
|
||||
byte = 0xFF;
|
||||
break;
|
||||
|
||||
case cmdSetInterrupt:
|
||||
if (m_uPosition == 1)
|
||||
case Command::SetInterrupt:
|
||||
if (m_position == 1)
|
||||
{
|
||||
interruptSwitch = byte;
|
||||
m_interrupt_switch = byte;
|
||||
}
|
||||
byte = 0xFF;
|
||||
break;
|
||||
|
||||
case cmdChipErase:
|
||||
case Command::ChipErase:
|
||||
byte = 0xFF;
|
||||
break;
|
||||
|
||||
case cmdPageProgram:
|
||||
switch (m_uPosition)
|
||||
case Command::PageProgram:
|
||||
switch (m_position)
|
||||
{
|
||||
case 1: // AD1
|
||||
address = byte << 17;
|
||||
m_address = byte << 17;
|
||||
break;
|
||||
case 2: // AD2
|
||||
address |= byte << 9;
|
||||
m_address |= byte << 9;
|
||||
break;
|
||||
case 3: // AD3
|
||||
address |= (byte & 3) << 7;
|
||||
m_address |= (byte & 3) << 7;
|
||||
break;
|
||||
case 4: // BA
|
||||
address |= (byte & 0x7F);
|
||||
m_address |= (byte & 0x7F);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_uPosition >= 5)
|
||||
programming_buffer[((m_uPosition - 5) & 0x7F)] = byte; // wrap around after 128 bytes
|
||||
if (m_position >= 5)
|
||||
m_programming_buffer[((m_position - 5) & 0x7F)] = byte; // wrap around after 128 bytes
|
||||
|
||||
byte = 0xFF;
|
||||
break;
|
||||
@ -494,7 +497,7 @@ void CEXIMemoryCard::TransferByte(u8& byte)
|
||||
byte = 0xFF;
|
||||
}
|
||||
}
|
||||
m_uPosition++;
|
||||
m_position++;
|
||||
DEBUG_LOG_FMT(EXPANSIONINTERFACE, "EXI MEMCARD: < {:02x}", byte);
|
||||
}
|
||||
|
||||
@ -509,56 +512,56 @@ void CEXIMemoryCard::DoState(PointerWrap& p)
|
||||
|
||||
if (storeContents)
|
||||
{
|
||||
p.Do(interruptSwitch);
|
||||
p.Do(m_bInterruptSet);
|
||||
p.Do(command);
|
||||
p.Do(status);
|
||||
p.Do(m_uPosition);
|
||||
p.Do(programming_buffer);
|
||||
p.Do(address);
|
||||
memorycard->DoState(p);
|
||||
p.Do(card_index);
|
||||
p.Do(m_interrupt_switch);
|
||||
p.Do(m_interrupt_set);
|
||||
p.Do(m_command);
|
||||
p.Do(m_status);
|
||||
p.Do(m_position);
|
||||
p.Do(m_programming_buffer);
|
||||
p.Do(m_address);
|
||||
m_memory_card->DoState(p);
|
||||
p.Do(m_card_index);
|
||||
}
|
||||
}
|
||||
|
||||
IEXIDevice* CEXIMemoryCard::FindDevice(TEXIDevices device_type, int customIndex)
|
||||
IEXIDevice* CEXIMemoryCard::FindDevice(TEXIDevices device_type, int custom_index)
|
||||
{
|
||||
if (device_type != m_device_type)
|
||||
return nullptr;
|
||||
if (customIndex != card_index)
|
||||
if (custom_index != m_card_index)
|
||||
return nullptr;
|
||||
return this;
|
||||
}
|
||||
|
||||
// DMA reads are preceded by all of the necessary setup via IMMRead
|
||||
// read all at once instead of single byte at a time as done by IEXIDevice::DMARead
|
||||
void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize)
|
||||
void CEXIMemoryCard::DMARead(u32 addr, u32 size)
|
||||
{
|
||||
memorycard->Read(address, _uSize, Memory::GetPointer(_uAddr));
|
||||
m_memory_card->Read(m_address, size, Memory::GetPointer(addr));
|
||||
|
||||
if ((address + _uSize) % Memcard::BLOCK_SIZE == 0)
|
||||
if ((m_address + size) % Memcard::BLOCK_SIZE == 0)
|
||||
{
|
||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "reading from block: {:x}", address / Memcard::BLOCK_SIZE);
|
||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "reading from block: {:x}", m_address / Memcard::BLOCK_SIZE);
|
||||
}
|
||||
|
||||
// Schedule transfer complete later based on read speed
|
||||
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ),
|
||||
s_et_transfer_complete[card_index], (u64)card_index);
|
||||
CoreTiming::ScheduleEvent(size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ),
|
||||
s_et_transfer_complete[m_card_index], m_card_index);
|
||||
}
|
||||
|
||||
// DMA write are preceded by all of the necessary setup via IMMWrite
|
||||
// write all at once instead of single byte at a time as done by IEXIDevice::DMAWrite
|
||||
void CEXIMemoryCard::DMAWrite(u32 _uAddr, u32 _uSize)
|
||||
void CEXIMemoryCard::DMAWrite(u32 addr, u32 size)
|
||||
{
|
||||
memorycard->Write(address, _uSize, Memory::GetPointer(_uAddr));
|
||||
m_memory_card->Write(m_address, size, Memory::GetPointer(addr));
|
||||
|
||||
if (((address + _uSize) % Memcard::BLOCK_SIZE) == 0)
|
||||
if (((m_address + size) % Memcard::BLOCK_SIZE) == 0)
|
||||
{
|
||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "writing to block: {:x}", address / Memcard::BLOCK_SIZE);
|
||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "writing to block: {:x}", m_address / Memcard::BLOCK_SIZE);
|
||||
}
|
||||
|
||||
// Schedule transfer complete later based on write speed
|
||||
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE),
|
||||
s_et_transfer_complete[card_index], (u64)card_index);
|
||||
CoreTiming::ScheduleEvent(size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE),
|
||||
s_et_transfer_complete[m_card_index], m_card_index);
|
||||
}
|
||||
} // namespace ExpansionInterface
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -30,16 +31,16 @@ enum class AllowMovieFolder
|
||||
class CEXIMemoryCard : public IEXIDevice
|
||||
{
|
||||
public:
|
||||
CEXIMemoryCard(const int index, bool gciFolder, const Memcard::HeaderData& header_data);
|
||||
virtual ~CEXIMemoryCard();
|
||||
CEXIMemoryCard(int index, bool gci_folder, const Memcard::HeaderData& header_data);
|
||||
~CEXIMemoryCard() override;
|
||||
void SetCS(int cs) override;
|
||||
bool IsInterruptSet() override;
|
||||
bool UseDelayedTransferCompletion() const override;
|
||||
bool IsPresent() const override;
|
||||
void DoState(PointerWrap& p) override;
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex = -1) override;
|
||||
void DMARead(u32 _uAddr, u32 _uSize) override;
|
||||
void DMAWrite(u32 _uAddr, u32 _uSize) override;
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int custom_index) override;
|
||||
void DMARead(u32 addr, u32 size) override;
|
||||
void DMAWrite(u32 addr, u32 size) override;
|
||||
|
||||
// CoreTiming events need to be registered during boot since CoreTiming is DoState()-ed
|
||||
// before ExpansionInterface so we'll lose the save stated events if the callbacks are
|
||||
@ -52,7 +53,7 @@ public:
|
||||
|
||||
private:
|
||||
void SetupGciFolder(const Memcard::HeaderData& header_data);
|
||||
void SetupRawMemcard(u16 sizeMb);
|
||||
void SetupRawMemcard(u16 size_mb);
|
||||
static void EventCompleteFindInstance(u64 userdata,
|
||||
std::function<void(CEXIMemoryCard*)> callback);
|
||||
|
||||
@ -71,40 +72,40 @@ private:
|
||||
// Variant of CmdDone which schedules an event later in the future to complete the command.
|
||||
void CmdDoneLater(u64 cycles);
|
||||
|
||||
enum
|
||||
enum class Command
|
||||
{
|
||||
cmdNintendoID = 0x00,
|
||||
cmdReadArray = 0x52,
|
||||
cmdArrayToBuffer = 0x53,
|
||||
cmdSetInterrupt = 0x81,
|
||||
cmdWriteBuffer = 0x82,
|
||||
cmdReadStatus = 0x83,
|
||||
cmdReadID = 0x85,
|
||||
cmdReadErrorBuffer = 0x86,
|
||||
cmdWakeUp = 0x87,
|
||||
cmdSleep = 0x88,
|
||||
cmdClearStatus = 0x89,
|
||||
cmdSectorErase = 0xF1,
|
||||
cmdPageProgram = 0xF2,
|
||||
cmdExtraByteProgram = 0xF3,
|
||||
cmdChipErase = 0xF4,
|
||||
NintendoID = 0x00,
|
||||
ReadArray = 0x52,
|
||||
ArrayToBuffer = 0x53,
|
||||
SetInterrupt = 0x81,
|
||||
WriteBuffer = 0x82,
|
||||
ReadStatus = 0x83,
|
||||
ReadID = 0x85,
|
||||
ReadErrorBuffer = 0x86,
|
||||
WakeUp = 0x87,
|
||||
Sleep = 0x88,
|
||||
ClearStatus = 0x89,
|
||||
SectorErase = 0xF1,
|
||||
PageProgram = 0xF2,
|
||||
ExtraByteProgram = 0xF3,
|
||||
ChipErase = 0xF4,
|
||||
};
|
||||
|
||||
int card_index;
|
||||
int m_card_index;
|
||||
//! memory card state
|
||||
|
||||
// STATE_TO_SAVE
|
||||
int interruptSwitch;
|
||||
bool m_bInterruptSet;
|
||||
int command;
|
||||
int status;
|
||||
u32 m_uPosition;
|
||||
u8 programming_buffer[128];
|
||||
int m_interrupt_switch;
|
||||
bool m_interrupt_set;
|
||||
Command m_command;
|
||||
int m_status;
|
||||
u32 m_position;
|
||||
std::array<u8, 128> m_programming_buffer;
|
||||
//! memory card parameters
|
||||
unsigned int card_id;
|
||||
unsigned int address;
|
||||
u32 memory_card_size;
|
||||
std::unique_ptr<MemoryCardBase> memorycard;
|
||||
unsigned int m_card_id;
|
||||
unsigned int m_address;
|
||||
u32 m_memory_card_size;
|
||||
std::unique_ptr<MemoryCardBase> m_memory_card;
|
||||
|
||||
protected:
|
||||
void TransferByte(u8& byte) override;
|
||||
|
Loading…
x
Reference in New Issue
Block a user