diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 6d4e6728e..b26aef76c 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -189,6 +189,8 @@ bool DoState(StateWrapper& sw) void SetExpansionROM(std::vector data) { m_exp1_rom = std::move(data); + if (m_exp1_rom.size() < 256 * 1024) + m_exp1_rom.resize(256 * 1024, 0xFF); } void SetBIOS(const std::vector& image) @@ -813,24 +815,38 @@ ALWAYS_INLINE static TickCount DoBIOSAccess(u32 offset, u32& value) return m_bios_access_time[static_cast(size)]; } +static bool flash_unlocked = false; +static bool chip_id_mode = false; +static u8 chip_id[] = {0x1F, 0xA4}; +static u8 flash_command_key[] = {0xAA, 0x55}; +static u8 flash_command_sequence = 0; + template static TickCount DoEXP1Access(u32 offset, u32& value) { + constexpr u32 transfer_size = u32(1) << static_cast(size); + if constexpr (type == MemoryAccessType::Read) { - if (m_exp1_rom.empty()) + if (chip_id_mode) + { + if ((offset + transfer_size) > sizeof(chip_id)) + value = 0; + else + std::memcpy(&value, &chip_id[offset], sizeof(value)); + } + else if (m_exp1_rom.empty()) { // EXP1 not present. value = UINT32_C(0xFFFFFFFF); } - else if (offset == 0x20018) + else if (offset == 0x60000) { // Bit 0 - Action Replay On/Off value = UINT32_C(1); } else { - const u32 transfer_size = u32(1) << static_cast(size); if ((offset + transfer_size) > m_exp1_rom.size()) { value = UINT32_C(0); @@ -861,6 +877,69 @@ static TickCount DoEXP1Access(u32 offset, u32& value) else { Log_WarningPrintf("EXP1 write: 0x%08X <- 0x%08X", EXP1_BASE | offset, value); + + if (offset == 0x5555 && value == 0xAA && flash_command_sequence == 0) + { + flash_command_sequence = 1; + return 0; + } + else if (offset == 0x2AAA && value == 0x55 && flash_command_sequence == 1) + { + flash_command_sequence = 2; + return 0; + } + else if (offset == 0x5555 && flash_command_sequence == 2) + { + flash_command_sequence = 0; + + switch (value) + { + case 0xA0: + { + Log_DevPrintf("Flash unlocked"); + flash_unlocked = true; + } + break; + + case 0x90: + { + Log_DevPrintf("Enter ID mode"); + chip_id_mode = true; + } + break; + + case 0xF0: + { + Log_DevPrintf("Exit ID mode"); + chip_id_mode = false; + } + break; + + case 0x80: + { + Log_WarningPrintf("Erase chip 1"); + } + break; + + case 0x10: + { + Log_WarningPrintf("Erase chip 2"); + } + break; + + default: + { + Log_ErrorPrintf("Unknown flash command 0x%02X", value); + } + break; + } + + return 0; + } + + if ((offset + transfer_size) <= m_exp1_rom.size()) + std::memcpy(&m_exp1_rom[offset], &value, sizeof(value)); + return 0; } } @@ -1294,12 +1373,25 @@ ALWAYS_INLINE_RELEASE bool DoInstructionRead(PhysicalMemoryAddress address, void } else if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE)) { - std::memcpy(data, &g_bios[(address - BIOS_BASE) & BIOS_MASK], sizeof(u32) * word_count); + std::memcpy(data, &g_bios[address - BIOS_BASE], sizeof(u32) * word_count); if constexpr (add_ticks) g_state.pending_ticks += m_bios_access_time[static_cast(MemoryAccessSize::Word)] * word_count; return true; } + else if (address >= EXP1_BASE && address < (EXP1_BASE + EXP1_SIZE)) + { + const u32 offset = (address - EXP1_BASE); + if ((offset + (word_count * sizeof(u32))) <= m_exp1_rom.size()) + std::memcpy(data, m_exp1_rom.data() + offset, sizeof(u32) * word_count); + else + std::memset(data, 0, sizeof(u32) * word_count); + + if constexpr (add_ticks) + g_state.pending_ticks += m_exp1_access_time[static_cast(MemoryAccessSize::Word)] * word_count; + + return true; + } else { if (raise_exceptions) diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index da4198ab8..946086f07 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -399,6 +399,19 @@ bool HostInterface::HasAnyBIOSImages() return (FindBIOSImageInDirectory(ConsoleRegion::Auto, dir.c_str()).has_value()); } +std::optional> HostInterface::GetExpansionROMImage() +{ + std::string filename = GetStringSettingValue("BIOS", "ExpansionROMPath", ""); + if (filename.empty()) + return {}; + + std::optional> ret(FileSystem::ReadBinaryFile(filename.c_str())); + if (!ret) + return {}; + + return ret; +} + bool HostInterface::LoadState(const char* filename) { std::unique_ptr stream = FileSystem::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 945b4fcdc..c9f61dbaa 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -142,6 +142,9 @@ public: /// Returns true if any BIOS images are found in the configured BIOS directory. bool HasAnyBIOSImages(); + /// Loads the configured expansion ROM image, if any. + std::optional> GetExpansionROMImage(); + /// Opens a file in the DuckStation "package". /// This is the APK for Android builds, or the program directory for standalone builds. virtual std::unique_ptr OpenPackageFile(const char* path, u32 flags) = 0; diff --git a/src/core/system.cpp b/src/core/system.cpp index 3693c39d6..c3db75b52 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -64,7 +64,6 @@ static bool SaveMemoryState(MemorySaveState* mss); static bool LoadMemoryState(const MemorySaveState& mss); static bool LoadEXE(const char* filename); -static bool SetExpansionROM(const char* filename); /// Opens CD image, preloading if needed. static std::unique_ptr OpenCDImage(const char* path, Common::Error* error, bool force_preload); @@ -804,6 +803,15 @@ bool Boot(const SystemBootParameters& params) return false; } + // Load expansion ROM image. + std::optional> expansion_rom = g_host_interface->GetExpansionROMImage(); + if (!expansion_rom) + { + g_host_interface->ReportError(g_host_interface->TranslateString("System", "Failed to expansion ROM image.")); + Shutdown(); + return false; + } + // Notify change of disc. UpdateRunningGame(media ? media->GetFileName().c_str() : params.filename.c_str(), media.get()); @@ -831,6 +839,9 @@ bool Boot(const SystemBootParameters& params) } Bus::SetBIOS(*bios_image); + if (expansion_rom && !expansion_rom->empty()) + Bus::SetExpansionROM(std::move(*expansion_rom)); + UpdateControllers(); UpdateMemoryCards(); UpdateMultitaps(); @@ -1683,34 +1694,6 @@ bool InjectEXEFromBuffer(const void* buffer, u32 buffer_size, bool patch_bios) return true; } -bool SetExpansionROM(const char* filename) -{ - std::FILE* fp = FileSystem::OpenCFile(filename, "rb"); - if (!fp) - { - Log_ErrorPrintf("Failed to open '%s'", filename); - return false; - } - - std::fseek(fp, 0, SEEK_END); - const u32 size = static_cast(std::ftell(fp)); - std::fseek(fp, 0, SEEK_SET); - - std::vector data(size); - if (std::fread(data.data(), size, 1, fp) != 1) - { - Log_ErrorPrintf("Failed to read ROM data from '%s'", filename); - std::fclose(fp); - return false; - } - - std::fclose(fp); - - Log_InfoPrintf("Loaded expansion ROM from '%s': %u bytes", filename, size); - Bus::SetExpansionROM(std::move(data)); - return true; -} - void StallCPU(TickCount ticks) { CPU::AddPendingTicks(ticks);