mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 14:25:38 +00:00
System: Scaffolding for multi-system/multi-bios
This commit is contained in:
parent
d6209937fb
commit
246c97ccb3
|
@ -1,4 +1,6 @@
|
||||||
add_library(core
|
add_library(core
|
||||||
|
bios.cpp
|
||||||
|
bios.h
|
||||||
bus.cpp
|
bus.cpp
|
||||||
bus.h
|
bus.h
|
||||||
bus.inl
|
bus.inl
|
||||||
|
|
178
src/core/bios.cpp
Normal file
178
src/core/bios.cpp
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
#include "bios.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "YBaseLib/MD5Digest.h"
|
||||||
|
#include "cpu_disasm.h"
|
||||||
|
Log_SetChannel(BIOS);
|
||||||
|
|
||||||
|
namespace BIOS {
|
||||||
|
static constexpr Hash MakeHashFromString(const char str[])
|
||||||
|
{
|
||||||
|
Hash h{};
|
||||||
|
for (int i = 0; str[i] != '\0'; i++)
|
||||||
|
{
|
||||||
|
u8 nibble = 0;
|
||||||
|
char ch = str[i];
|
||||||
|
if (ch >= '0' && ch <= '9')
|
||||||
|
nibble = str[i] - '0';
|
||||||
|
else if (ch >= 'a' && ch <= 'z')
|
||||||
|
nibble = 0xA + (str[i] - 'a');
|
||||||
|
else if (ch >= 'A' && ch <= 'Z')
|
||||||
|
nibble = 0xA + (str[i] - 'A');
|
||||||
|
|
||||||
|
h.bytes[i / 2] |= nibble << (((i & 1) ^ 1) * 4);
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Hash::ToString() const
|
||||||
|
{
|
||||||
|
char str[33];
|
||||||
|
std::snprintf(str, sizeof(str), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", bytes[0],
|
||||||
|
bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10],
|
||||||
|
bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Hash SCPH_1000_HASH = MakeHashFromString("239665b1a3dade1b5a52c06338011044");
|
||||||
|
static constexpr Hash SCPH_1001_HASH = MakeHashFromString("924e392ed05558ffdb115408c263dccf");
|
||||||
|
static constexpr Hash SCPH_1002_HASH = MakeHashFromString("54847e693405ffeb0359c6287434cbef");
|
||||||
|
static constexpr Hash SCPH_5500_HASH = MakeHashFromString("8dd7d5296a650fac7319bce665a6a53c");
|
||||||
|
static constexpr Hash SCPH_5501_HASH = MakeHashFromString("490f666e1afb15b7362b406ed1cea246");
|
||||||
|
static constexpr Hash SCPH_5502_HASH = MakeHashFromString("32736f17079d0b2b7024407c39bd3050");
|
||||||
|
|
||||||
|
Hash GetHash(const Image& image)
|
||||||
|
{
|
||||||
|
Hash hash;
|
||||||
|
MD5Digest digest;
|
||||||
|
digest.Update(image.data(), static_cast<u32>(image.size()));
|
||||||
|
digest.Final(hash.bytes);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Image> LoadImageFromFile(std::string_view filename)
|
||||||
|
{
|
||||||
|
Image ret(BIOS_SIZE);
|
||||||
|
std::string filename_str(filename);
|
||||||
|
std::FILE* fp = std::fopen(filename_str.c_str(), "rb");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to open BIOS image '%s'", filename_str.c_str());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fseek(fp, 0, SEEK_END);
|
||||||
|
const u32 size = static_cast<u32>(std::ftell(fp));
|
||||||
|
std::fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
if (size != BIOS_SIZE)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("BIOS image '%s' mismatch, expecting %u bytes, got %u bytes", filename_str.c_str(), BIOS_SIZE,
|
||||||
|
size);
|
||||||
|
std::fclose(fp);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::fread(ret.data(), 1, ret.size(), fp) != ret.size())
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to read BIOS image '%s'", filename_str.c_str());
|
||||||
|
std::fclose(fp);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Hash> GetHashForFile(const std::string_view filename)
|
||||||
|
{
|
||||||
|
auto image = LoadImageFromFile(filename);
|
||||||
|
if (!image)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return GetHash(*image);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash)
|
||||||
|
{
|
||||||
|
switch (region)
|
||||||
|
{
|
||||||
|
case ConsoleRegion::NTSC_J:
|
||||||
|
return (hash == SCPH_1000_HASH || hash == SCPH_5500_HASH);
|
||||||
|
|
||||||
|
case ConsoleRegion::NTSC_U:
|
||||||
|
return (hash == SCPH_1001_HASH || hash == SCPH_5501_HASH);
|
||||||
|
|
||||||
|
case ConsoleRegion::PAL:
|
||||||
|
return (hash == SCPH_1002_HASH || hash == SCPH_5502_HASH);
|
||||||
|
|
||||||
|
case ConsoleRegion::Auto:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatchBIOS(Image& bios, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/)
|
||||||
|
{
|
||||||
|
const u32 phys_address = address & UINT32_C(0x1FFFFFFF);
|
||||||
|
const u32 offset = phys_address - BIOS_BASE;
|
||||||
|
Assert(phys_address >= BIOS_BASE && offset < BIOS_SIZE);
|
||||||
|
|
||||||
|
u32 existing_value;
|
||||||
|
std::memcpy(&existing_value, &bios[offset], sizeof(existing_value));
|
||||||
|
u32 new_value = (existing_value & ~mask) | value;
|
||||||
|
std::memcpy(&bios[offset], &new_value, sizeof(new_value));
|
||||||
|
|
||||||
|
SmallString old_disasm, new_disasm;
|
||||||
|
CPU::DisassembleInstruction(&old_disasm, address, existing_value);
|
||||||
|
CPU::DisassembleInstruction(&new_disasm, address, new_value);
|
||||||
|
Log_DevPrintf("BIOS-Patch 0x%08X (+0x%X): 0x%08X %s -> %08X %s", address, offset, existing_value,
|
||||||
|
old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PatchBIOSEnableTTY(Image& image, const Hash& hash)
|
||||||
|
{
|
||||||
|
if (hash != SCPH_1000_HASH && hash != SCPH_1001_HASH && hash != SCPH_1002_HASH && hash != SCPH_5500_HASH &&
|
||||||
|
hash != SCPH_5501_HASH && hash != SCPH_5502_HASH)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Incompatible version for TTY patch: %s", hash.ToString().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_InfoPrintf("Patching BIOS to enable TTY/printf");
|
||||||
|
PatchBIOS(image, 0x1FC06F0C, 0x24010001);
|
||||||
|
PatchBIOS(image, 0x1FC06F14, 0xAF81A9C0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PatchBIOSFastBoot(Image& image, const Hash& hash)
|
||||||
|
{
|
||||||
|
if (hash != SCPH_1000_HASH && hash != SCPH_1001_HASH && hash != SCPH_1002_HASH && hash != SCPH_5500_HASH &&
|
||||||
|
hash != SCPH_5501_HASH && hash != SCPH_5502_HASH)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Incompatible version for fast-boot patch: %s", hash.ToString().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the shell entry point with a return back to the bootstrap.
|
||||||
|
Log_InfoPrintf("Patching BIOS to skip intro");
|
||||||
|
PatchBIOS(image, 0x1FC18000, 0x03E00008);
|
||||||
|
PatchBIOS(image, 0x1FC18004, 0x00000000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PatchBIOSForEXE(Image& image, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp)
|
||||||
|
{
|
||||||
|
// pc has to be done first because we can't load it in the delay slot
|
||||||
|
PatchBIOS(image, 0xBFC06FF0, UINT32_C(0x3C080000) | r_pc >> 16); // lui $t0, (r_pc >> 16)
|
||||||
|
PatchBIOS(image, 0xBFC06FF4, UINT32_C(0x35080000) | (r_pc & UINT32_C(0xFFFF))); // ori $t0, $t0, (r_pc & 0xFFFF)
|
||||||
|
PatchBIOS(image, 0xBFC06FF8, UINT32_C(0x3C1C0000) | r_gp >> 16); // lui $gp, (r_gp >> 16)
|
||||||
|
PatchBIOS(image, 0xBFC06FFC, UINT32_C(0x379C0000) | (r_gp & UINT32_C(0xFFFF))); // ori $gp, $gp, (r_gp & 0xFFFF)
|
||||||
|
PatchBIOS(image, 0xBFC07000, UINT32_C(0x3C1D0000) | r_sp >> 16); // lui $sp, (r_sp >> 16)
|
||||||
|
PatchBIOS(image, 0xBFC07004, UINT32_C(0x37BD0000) | (r_sp & UINT32_C(0xFFFF))); // ori $sp, $sp, (r_sp & 0xFFFF)
|
||||||
|
PatchBIOS(image, 0xBFC07008, UINT32_C(0x3C1E0000) | r_fp >> 16); // lui $fp, (r_fp >> 16)
|
||||||
|
PatchBIOS(image, 0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
|
||||||
|
PatchBIOS(image, 0xBFC07010, UINT32_C(0x37DE0000) | (r_fp & UINT32_C(0xFFFF))); // ori $fp, $fp, (r_fp & 0xFFFF)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace BIOS
|
37
src/core/bios.h
Normal file
37
src/core/bios.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace BIOS {
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
BIOS_BASE = 0x1FC00000,
|
||||||
|
BIOS_SIZE = 0x80000
|
||||||
|
};
|
||||||
|
|
||||||
|
using Image = std::vector<u8>;
|
||||||
|
|
||||||
|
struct Hash
|
||||||
|
{
|
||||||
|
u8 bytes[16];
|
||||||
|
|
||||||
|
ALWAYS_INLINE bool operator==(const Hash& bh) const { return (std::memcmp(bytes, bh.bytes, sizeof(bytes)) == 0); }
|
||||||
|
ALWAYS_INLINE bool operator!=(const Hash& bh) const { return (std::memcmp(bytes, bh.bytes, sizeof(bytes)) != 0); }
|
||||||
|
|
||||||
|
std::string ToString() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
Hash GetHash(const Image& image);
|
||||||
|
std::optional<Image> LoadImageFromFile(std::string_view filename);
|
||||||
|
std::optional<Hash> GetHashForFile(std::string_view filename);
|
||||||
|
|
||||||
|
bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash);
|
||||||
|
|
||||||
|
void PatchBIOS(Image& image, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
||||||
|
|
||||||
|
bool PatchBIOSEnableTTY(Image& image, const Hash& hash);
|
||||||
|
bool PatchBIOSFastBoot(Image& image, const Hash& hash);
|
||||||
|
bool PatchBIOSForEXE(Image& image, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp);
|
||||||
|
} // namespace BIOS
|
|
@ -166,65 +166,20 @@ TickCount Bus::WriteWords(PhysicalMemoryAddress address, const u32* words, u32 w
|
||||||
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
|
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::PatchBIOS(u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/)
|
|
||||||
{
|
|
||||||
const u32 phys_address = address & UINT32_C(0x1FFFFFFF);
|
|
||||||
const u32 offset = phys_address - BIOS_BASE;
|
|
||||||
Assert(phys_address >= BIOS_BASE && offset < BIOS_SIZE);
|
|
||||||
|
|
||||||
u32 existing_value;
|
|
||||||
std::memcpy(&existing_value, &m_bios[offset], sizeof(existing_value));
|
|
||||||
u32 new_value = (existing_value & ~mask) | value;
|
|
||||||
std::memcpy(&m_bios[offset], &new_value, sizeof(new_value));
|
|
||||||
|
|
||||||
SmallString old_disasm, new_disasm;
|
|
||||||
CPU::DisassembleInstruction(&old_disasm, address, existing_value);
|
|
||||||
CPU::DisassembleInstruction(&new_disasm, address, new_value);
|
|
||||||
Log_InfoPrintf("BIOS-Patch 0x%08X (+0x%X): 0x%08X %s -> %08X %s", address, offset, existing_value,
|
|
||||||
old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bus::GetBIOSHash(u8 hash[16])
|
|
||||||
{
|
|
||||||
MD5Digest digest;
|
|
||||||
digest.Update(m_bios.data(), static_cast<u32>(m_bios.size()));
|
|
||||||
digest.Final(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bus::SetExpansionROM(std::vector<u8> data)
|
void Bus::SetExpansionROM(std::vector<u8> data)
|
||||||
{
|
{
|
||||||
m_exp1_rom = std::move(data);
|
m_exp1_rom = std::move(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bus::LoadBIOS(const char* filename)
|
void Bus::SetBIOS(const std::vector<u8>& image)
|
||||||
{
|
{
|
||||||
std::FILE* fp = std::fopen(filename, "rb");
|
if (image.size() != static_cast<u32>(BIOS_SIZE))
|
||||||
if (!fp)
|
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to open BIOS image '%s'", filename);
|
Panic("Incorrect BIOS image size");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fseek(fp, 0, SEEK_END);
|
std::copy(image.cbegin(), image.cend(), m_bios.begin());
|
||||||
const u32 size = static_cast<u32>(std::ftell(fp));
|
|
||||||
std::fseek(fp, 0, SEEK_SET);
|
|
||||||
|
|
||||||
if (size != m_bios.size())
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("BIOS image mismatch, expecting %u bytes, got %u bytes", static_cast<u32>(m_bios.size()), size);
|
|
||||||
std::fclose(fp);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::fread(m_bios.data(), 1, m_bios.size(), fp) != m_bios.size())
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to read BIOS image");
|
|
||||||
std::fclose(fp);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::fclose(fp);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<TickCount, TickCount, TickCount> Bus::CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay)
|
std::tuple<TickCount, TickCount, TickCount> Bus::CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay)
|
||||||
|
|
|
@ -32,10 +32,6 @@ public:
|
||||||
void Reset();
|
void Reset();
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
bool LoadBIOS(const char* filename);
|
|
||||||
void PatchBIOS(u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
|
||||||
void GetBIOSHash(u8 hash[16]);
|
|
||||||
|
|
||||||
bool ReadByte(PhysicalMemoryAddress address, u8* value);
|
bool ReadByte(PhysicalMemoryAddress address, u8* value);
|
||||||
bool ReadHalfWord(PhysicalMemoryAddress address, u16* value);
|
bool ReadHalfWord(PhysicalMemoryAddress address, u16* value);
|
||||||
bool ReadWord(PhysicalMemoryAddress address, u32* value);
|
bool ReadWord(PhysicalMemoryAddress address, u32* value);
|
||||||
|
@ -51,6 +47,7 @@ public:
|
||||||
TickCount WriteWords(PhysicalMemoryAddress address, const u32* words, u32 word_count);
|
TickCount WriteWords(PhysicalMemoryAddress address, const u32* words, u32 word_count);
|
||||||
|
|
||||||
void SetExpansionROM(std::vector<u8> data);
|
void SetExpansionROM(std::vector<u8> data);
|
||||||
|
void SetBIOS(const std::vector<u8>& image);
|
||||||
|
|
||||||
// changing interfaces
|
// changing interfaces
|
||||||
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
||||||
|
|
|
@ -147,20 +147,12 @@ bool CDROM::DoState(StateWrapper& sw)
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDROM::InsertMedia(const char* filename)
|
void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
||||||
{
|
{
|
||||||
auto media = CDImage::Open(filename);
|
|
||||||
if (!media)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to open media at '%s'", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasMedia())
|
if (HasMedia())
|
||||||
RemoveMedia();
|
RemoveMedia();
|
||||||
|
|
||||||
m_media = std::move(media);
|
m_media = std::move(media);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::RemoveMedia()
|
void CDROM::RemoveMedia()
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
bool HasMedia() const { return static_cast<bool>(m_media); }
|
bool HasMedia() const { return static_cast<bool>(m_media); }
|
||||||
bool InsertMedia(const char* filename);
|
void InsertMedia(std::unique_ptr<CDImage> media);
|
||||||
void RemoveMedia();
|
void RemoveMedia();
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
</ProjectConfiguration>
|
</ProjectConfiguration>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="bios.cpp" />
|
||||||
<ClCompile Include="bus.cpp" />
|
<ClCompile Include="bus.cpp" />
|
||||||
<ClCompile Include="cdrom.cpp" />
|
<ClCompile Include="cdrom.cpp" />
|
||||||
<ClCompile Include="cpu_core.cpp" />
|
<ClCompile Include="cpu_core.cpp" />
|
||||||
|
@ -61,6 +62,7 @@
|
||||||
<ClCompile Include="timers.cpp" />
|
<ClCompile Include="timers.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="bios.h" />
|
||||||
<ClInclude Include="bus.h" />
|
<ClInclude Include="bus.h" />
|
||||||
<ClInclude Include="cdrom.h" />
|
<ClInclude Include="cdrom.h" />
|
||||||
<ClInclude Include="cpu_core.h" />
|
<ClInclude Include="cpu_core.h" />
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<ClCompile Include="gpu_sw.cpp" />
|
<ClCompile Include="gpu_sw.cpp" />
|
||||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||||
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||||
|
<ClCompile Include="bios.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||||
<ClInclude Include="gpu_hw_d3d11.h" />
|
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||||
<ClInclude Include="host_display.h" />
|
<ClInclude Include="host_display.h" />
|
||||||
|
<ClInclude Include="bios.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="cpu_core.inl" />
|
<None Include="cpu_core.inl" />
|
||||||
|
|
|
@ -21,6 +21,15 @@ class Timers;
|
||||||
class GPU
|
class GPU
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class State : u8
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
WaitingForParameters,
|
||||||
|
ExecutingCommand,
|
||||||
|
ReadingVRAM,
|
||||||
|
WritingVRAM
|
||||||
|
};
|
||||||
|
|
||||||
enum class DMADirection : u32
|
enum class DMADirection : u32
|
||||||
{
|
{
|
||||||
Off = 0,
|
Off = 0,
|
||||||
|
@ -72,15 +81,6 @@ public:
|
||||||
Disabled = 4 // Not a register value
|
Disabled = 4 // Not a register value
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class State : u8
|
|
||||||
{
|
|
||||||
Idle,
|
|
||||||
WaitingForParameters,
|
|
||||||
ExecutingCommand,
|
|
||||||
ReadingVRAM,
|
|
||||||
WritingVRAM
|
|
||||||
};
|
|
||||||
|
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
VRAM_WIDTH = 1024,
|
VRAM_WIDTH = 1024,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#include "host_interface.h"
|
#include "host_interface.h"
|
||||||
#include "YBaseLib/ByteStream.h"
|
#include "YBaseLib/ByteStream.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "bios.h"
|
||||||
#include "common/audio_stream.h"
|
#include "common/audio_stream.h"
|
||||||
#include "host_display.h"
|
#include "host_display.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include <filesystem>
|
||||||
Log_SetChannel(HostInterface);
|
Log_SetChannel(HostInterface);
|
||||||
|
|
||||||
HostInterface::HostInterface()
|
HostInterface::HostInterface()
|
||||||
|
@ -13,67 +15,113 @@ HostInterface::HostInterface()
|
||||||
|
|
||||||
HostInterface::~HostInterface() = default;
|
HostInterface::~HostInterface() = default;
|
||||||
|
|
||||||
bool HostInterface::InitializeSystem(const char* filename, const char* exp1_filename)
|
bool HostInterface::CreateSystem()
|
||||||
{
|
{
|
||||||
m_system = std::make_unique<System>(this);
|
m_system = System::Create(this);
|
||||||
if (!m_system->Initialize())
|
|
||||||
{
|
|
||||||
m_system.reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_system->Reset();
|
// Pull in any invalid settings which have been reset.
|
||||||
|
|
||||||
if (filename)
|
|
||||||
{
|
|
||||||
const StaticString filename_str(filename);
|
|
||||||
if (filename_str.EndsWith(".psexe", false) || filename_str.EndsWith(".exe", false))
|
|
||||||
{
|
|
||||||
Log_InfoPrintf("Sideloading EXE file '%s'", filename);
|
|
||||||
if (!m_system->LoadEXE(filename))
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to load EXE file '%s'", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log_InfoPrintf("Inserting CDROM from image file '%s'", filename);
|
|
||||||
if (!m_system->InsertMedia(filename))
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to insert media '%s'", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exp1_filename)
|
|
||||||
m_system->SetExpansionROM(exp1_filename);
|
|
||||||
|
|
||||||
// Resume execution.
|
|
||||||
m_settings = m_system->GetSettings();
|
m_settings = m_system->GetSettings();
|
||||||
|
m_paused = true;
|
||||||
|
UpdateAudioVisualSync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostInterface::ShutdownSystem()
|
bool HostInterface::BootSystem(const char* filename, const char* state_filename)
|
||||||
|
{
|
||||||
|
if (!m_system->Boot(filename))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_paused = m_settings.start_paused;
|
||||||
|
ConnectControllers();
|
||||||
|
UpdateAudioVisualSync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostInterface::DestroySystem()
|
||||||
{
|
{
|
||||||
m_system.reset();
|
m_system.reset();
|
||||||
m_paused = false;
|
m_paused = false;
|
||||||
UpdateAudioVisualSync();
|
UpdateAudioVisualSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HostInterface::ReportError(const char* message)
|
||||||
|
{
|
||||||
|
Log_ErrorPrint(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostInterface::ReportMessage(const char* message)
|
||||||
|
{
|
||||||
|
Log_InfoPrintf(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
|
||||||
|
{
|
||||||
|
// Try the other default filenames in the directory of the configured BIOS.
|
||||||
|
#define TRY_FILENAME(filename) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
std::string try_filename = filename; \
|
||||||
|
std::optional<BIOS::Image> found_image = BIOS::LoadImageFromFile(try_filename); \
|
||||||
|
BIOS::Hash found_hash = BIOS::GetHash(*found_image); \
|
||||||
|
Log_DevPrintf("Hash for BIOS '%s': %s", try_filename.c_str(), found_hash.ToString().c_str()); \
|
||||||
|
if (BIOS::IsValidHashForRegion(region, found_hash)) \
|
||||||
|
{ \
|
||||||
|
Log_InfoPrintf("Using BIOS from '%s'", try_filename.c_str()); \
|
||||||
|
return found_image; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define RELATIVE_PATH(filename) std::filesystem::path(m_settings.bios_path).replace_filename(filename).string()
|
||||||
|
|
||||||
|
// Try the configured image.
|
||||||
|
TRY_FILENAME(m_settings.bios_path);
|
||||||
|
|
||||||
|
// Try searching in the same folder for other region's images.
|
||||||
|
switch (region)
|
||||||
|
{
|
||||||
|
case ConsoleRegion::NTSC_J:
|
||||||
|
TRY_FILENAME(RELATIVE_PATH("scph1000.bin"));
|
||||||
|
TRY_FILENAME(RELATIVE_PATH("scph5500.bin"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleRegion::NTSC_U:
|
||||||
|
TRY_FILENAME(RELATIVE_PATH("scph1001.bin"));
|
||||||
|
TRY_FILENAME(RELATIVE_PATH("scph5501.bin"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleRegion::PAL:
|
||||||
|
TRY_FILENAME(RELATIVE_PATH("scph1002.bin"));
|
||||||
|
TRY_FILENAME(RELATIVE_PATH("scph5502.bin"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef RELATIVE_PATH
|
||||||
|
#undef TRY_FILENAME
|
||||||
|
|
||||||
|
// Fall back to the default image.
|
||||||
|
Log_WarningPrintf("No suitable BIOS image for region %s could be located, using configured image '%s'. This may "
|
||||||
|
"result in instability.",
|
||||||
|
Settings::GetConsoleRegionName(region), m_settings.bios_path.c_str());
|
||||||
|
return BIOS::LoadImageFromFile(m_settings.bios_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostInterface::ConnectControllers() {}
|
||||||
|
|
||||||
bool HostInterface::LoadState(const char* filename)
|
bool HostInterface::LoadState(const char* filename)
|
||||||
{
|
{
|
||||||
ByteStream* stream;
|
ByteStream* stream;
|
||||||
if (!ByteStream_OpenFileStream(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, &stream))
|
if (!ByteStream_OpenFileStream(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, &stream))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ReportMessage(SmallString::FromFormat("Loading state from %s...", filename));
|
AddOSDMessage(SmallString::FromFormat("Loading state from %s...", filename));
|
||||||
|
|
||||||
const bool result = m_system->LoadState(stream);
|
const bool result = m_system->LoadState(stream);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
ReportMessage(SmallString::FromFormat("Loading state from %s failed. Resetting.", filename));
|
ReportError(SmallString::FromFormat("Loading state from %s failed. Resetting.", filename));
|
||||||
m_system->Reset();
|
m_system->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,12 +143,12 @@ bool HostInterface::SaveState(const char* filename)
|
||||||
const bool result = m_system->SaveState(stream);
|
const bool result = m_system->SaveState(stream);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
ReportMessage(SmallString::FromFormat("Saving state to %s failed.", filename));
|
ReportError(SmallString::FromFormat("Saving state to %s failed.", filename));
|
||||||
stream->Discard();
|
stream->Discard();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReportMessage(SmallString::FromFormat("State saved to %s.", filename));
|
AddOSDMessage(SmallString::FromFormat("State saved to %s.", filename));
|
||||||
stream->Commit();
|
stream->Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +160,7 @@ void HostInterface::UpdateAudioVisualSync()
|
||||||
{
|
{
|
||||||
const bool speed_limiter_enabled = m_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled;
|
const bool speed_limiter_enabled = m_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled;
|
||||||
const bool audio_sync_enabled = speed_limiter_enabled;
|
const bool audio_sync_enabled = speed_limiter_enabled;
|
||||||
const bool vsync_enabled = !m_system || (speed_limiter_enabled && m_settings.gpu_vsync);
|
const bool vsync_enabled = !m_system || m_paused || (speed_limiter_enabled && m_settings.gpu_vsync);
|
||||||
Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "",
|
Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "",
|
||||||
(speed_limiter_enabled && vsync_enabled) ? " and video" : "");
|
(speed_limiter_enabled && vsync_enabled) ? " and video" : "");
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class AudioStream;
|
class AudioStream;
|
||||||
class HostDisplay;
|
class HostDisplay;
|
||||||
|
@ -23,20 +25,26 @@ public:
|
||||||
/// Returns a settings object which can be modified.
|
/// Returns a settings object which can be modified.
|
||||||
Settings& GetSettings() { return m_settings; }
|
Settings& GetSettings() { return m_settings; }
|
||||||
|
|
||||||
|
bool CreateSystem();
|
||||||
|
bool BootSystem(const char* filename, const char* state_filename);
|
||||||
|
void DestroySystem();
|
||||||
|
|
||||||
bool InitializeSystem(const char* filename, const char* exp1_filename);
|
virtual void ReportError(const char* message);
|
||||||
void ShutdownSystem();
|
virtual void ReportMessage(const char* message);
|
||||||
|
|
||||||
|
|
||||||
virtual void ReportMessage(const char* message) = 0;
|
|
||||||
|
|
||||||
// Adds OSD messages, duration is in seconds.
|
// Adds OSD messages, duration is in seconds.
|
||||||
virtual void AddOSDMessage(const char* message, float duration = 2.0f) = 0;
|
virtual void AddOSDMessage(const char* message, float duration = 2.0f) = 0;
|
||||||
|
|
||||||
|
/// Loads the BIOS image for the specified region.
|
||||||
|
virtual std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
|
||||||
|
|
||||||
bool LoadState(const char* filename);
|
bool LoadState(const char* filename);
|
||||||
bool SaveState(const char* filename);
|
bool SaveState(const char* filename);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// Connects controllers. TODO: Clean this up later...
|
||||||
|
virtual void ConnectControllers();
|
||||||
|
|
||||||
void UpdateAudioVisualSync();
|
void UpdateAudioVisualSync();
|
||||||
|
|
||||||
std::unique_ptr<HostDisplay> m_display;
|
std::unique_ptr<HostDisplay> m_display;
|
||||||
|
|
|
@ -91,11 +91,39 @@ bool Settings::Save(const char* filename) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::array<const char*, 4> s_console_region_names = {{"Auto", "NTSC-J", "NTSC-U", "PAL"}};
|
||||||
|
static std::array<const char*, 4> s_console_region_display_names = {
|
||||||
|
{"Auto-Detect", "NTSC-J (Japan)", "NTSC-U (US)", "PAL (Europe, Australia)"}};
|
||||||
|
|
||||||
|
std::optional<ConsoleRegion> Settings::ParseConsoleRegionName(const char* str)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
for (const char* name : s_console_region_names)
|
||||||
|
{
|
||||||
|
if (strcasecmp(name, str) == 0)
|
||||||
|
return static_cast<ConsoleRegion>(index);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetConsoleRegionName(ConsoleRegion region)
|
||||||
|
{
|
||||||
|
return s_console_region_names[static_cast<int>(region)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetConsoleRegionDisplayName(ConsoleRegion region)
|
||||||
|
{
|
||||||
|
return s_console_region_display_names[static_cast<int>(region)];
|
||||||
|
}
|
||||||
|
|
||||||
static std::array<const char*, 3> s_gpu_renderer_names = {{"D3D11", "OpenGL", "Software"}};
|
static std::array<const char*, 3> s_gpu_renderer_names = {{"D3D11", "OpenGL", "Software"}};
|
||||||
static std::array<const char*, 3> s_gpu_renderer_display_names = {
|
static std::array<const char*, 3> s_gpu_renderer_display_names = {
|
||||||
{"Hardware (D3D11)", "Hardware (OpenGL)", "Software"}};
|
{"Hardware (D3D11)", "Hardware (OpenGL)", "Software"}};
|
||||||
|
|
||||||
std::optional<Settings::GPURenderer> Settings::ParseRendererName(const char* str)
|
std::optional<GPURenderer> Settings::ParseRendererName(const char* str)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const char* name : s_gpu_renderer_names)
|
for (const char* name : s_gpu_renderer_names)
|
||||||
|
|
|
@ -4,16 +4,10 @@
|
||||||
|
|
||||||
struct Settings
|
struct Settings
|
||||||
{
|
{
|
||||||
enum class GPURenderer
|
|
||||||
{
|
|
||||||
HardwareD3D11,
|
|
||||||
HardwareOpenGL,
|
|
||||||
Software,
|
|
||||||
Count
|
|
||||||
};
|
|
||||||
|
|
||||||
Settings();
|
Settings();
|
||||||
|
|
||||||
|
ConsoleRegion region = ConsoleRegion::NTSC_U;
|
||||||
|
|
||||||
bool start_paused = false;
|
bool start_paused = false;
|
||||||
bool speed_limiter_enabled = true;
|
bool speed_limiter_enabled = true;
|
||||||
|
|
||||||
|
@ -52,6 +46,10 @@ struct Settings
|
||||||
void Load(const char* filename);
|
void Load(const char* filename);
|
||||||
bool Save(const char* filename) const;
|
bool Save(const char* filename) const;
|
||||||
|
|
||||||
|
static std::optional<ConsoleRegion> ParseConsoleRegionName(const char* str);
|
||||||
|
static const char* GetConsoleRegionName(ConsoleRegion region);
|
||||||
|
static const char* GetConsoleRegionDisplayName(ConsoleRegion region);
|
||||||
|
|
||||||
static std::optional<GPURenderer> ParseRendererName(const char* str);
|
static std::optional<GPURenderer> ParseRendererName(const char* str);
|
||||||
static const char* GetRendererName(GPURenderer renderer);
|
static const char* GetRendererName(GPURenderer renderer);
|
||||||
static const char* GetRendererDisplayName(GPURenderer renderer);
|
static const char* GetRendererDisplayName(GPURenderer renderer);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "YBaseLib/AutoReleasePtr.h"
|
#include "YBaseLib/AutoReleasePtr.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "bios.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "cdrom.h"
|
#include "cdrom.h"
|
||||||
#include "common/state_wrapper.h"
|
#include "common/state_wrapper.h"
|
||||||
|
@ -19,15 +20,6 @@
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
Log_SetChannel(System);
|
Log_SetChannel(System);
|
||||||
|
|
||||||
namespace BIOSHashes {
|
|
||||||
static constexpr char SCPH_1000[] = "239665b1a3dade1b5a52c06338011044";
|
|
||||||
static constexpr char SCPH_1001[] = "924e392ed05558ffdb115408c263dccf";
|
|
||||||
static constexpr char SCPH_1002[] = "54847e693405ffeb0359c6287434cbef";
|
|
||||||
static constexpr char SCPH_5500[] = "8dd7d5296a650fac7319bce665a6a53c";
|
|
||||||
static constexpr char SCPH_5501[] = "490f666e1afb15b7362b406ed1cea246";
|
|
||||||
static constexpr char SCPH_5502[] = "32736f17079d0b2b7024407c39bd3050";
|
|
||||||
} // namespace BIOSHashes
|
|
||||||
|
|
||||||
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||||
{
|
{
|
||||||
m_cpu = std::make_unique<CPU::Core>();
|
m_cpu = std::make_unique<CPU::Core>();
|
||||||
|
@ -39,10 +31,32 @@ System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||||
m_timers = std::make_unique<Timers>();
|
m_timers = std::make_unique<Timers>();
|
||||||
m_spu = std::make_unique<SPU>();
|
m_spu = std::make_unique<SPU>();
|
||||||
m_mdec = std::make_unique<MDEC>();
|
m_mdec = std::make_unique<MDEC>();
|
||||||
|
m_region = host_interface->GetSettings().region;
|
||||||
}
|
}
|
||||||
|
|
||||||
System::~System() = default;
|
System::~System() = default;
|
||||||
|
|
||||||
|
std::optional<ConsoleRegion> System::GetRegionForCDImage(const CDImage* image)
|
||||||
|
{
|
||||||
|
// TODO: Implement me.
|
||||||
|
return ConsoleRegion::NTSC_U;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool System::IsPSExe(const char* filename)
|
||||||
|
{
|
||||||
|
const StaticString filename_str(filename);
|
||||||
|
return filename_str.EndsWith(".psexe", false) || filename_str.EndsWith(".exe", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<System> System::Create(HostInterface* host_interface)
|
||||||
|
{
|
||||||
|
std::unique_ptr<System> system(new System(host_interface));
|
||||||
|
if (!system->CreateGPU())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
|
||||||
bool System::RecreateGPU()
|
bool System::RecreateGPU()
|
||||||
{
|
{
|
||||||
// save current state
|
// save current state
|
||||||
|
@ -70,7 +84,83 @@ bool System::RecreateGPU()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::Initialize()
|
bool System::Boot(const char* filename)
|
||||||
|
{
|
||||||
|
// Load CD image up and detect region.
|
||||||
|
std::unique_ptr<CDImage> media;
|
||||||
|
bool exe_boot = false;
|
||||||
|
if (filename)
|
||||||
|
{
|
||||||
|
exe_boot = IsPSExe(filename);
|
||||||
|
if (exe_boot)
|
||||||
|
{
|
||||||
|
if (m_region == ConsoleRegion::Auto)
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("Defaulting to NTSC-U region for executable.");
|
||||||
|
m_region = ConsoleRegion::NTSC_U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("Loading CD image '%s'...", filename);
|
||||||
|
media = CDImage::Open(filename);
|
||||||
|
if (!media)
|
||||||
|
{
|
||||||
|
m_host_interface->ReportError(SmallString::FromFormat("Failed to load CD image '%s'", filename));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_region == ConsoleRegion::Auto)
|
||||||
|
{
|
||||||
|
std::optional<ConsoleRegion> detected_region = GetRegionForCDImage(media.get());
|
||||||
|
m_region = detected_region.value_or(ConsoleRegion::NTSC_U);
|
||||||
|
if (detected_region)
|
||||||
|
Log_InfoPrintf("Auto-detected %s region for '%s'", Settings::GetConsoleRegionName(m_region));
|
||||||
|
else
|
||||||
|
Log_WarningPrintf("Could not determine region for CD. Defaulting to NTSC-U.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load BIOS image.
|
||||||
|
std::optional<BIOS::Image> bios_image = m_host_interface->GetBIOSImage(m_region);
|
||||||
|
if (!bios_image)
|
||||||
|
{
|
||||||
|
m_host_interface->ReportError(
|
||||||
|
TinyString::FromFormat("Failed to load %s BIOS", Settings::GetConsoleRegionName(m_region)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component setup.
|
||||||
|
InitializeComponents();
|
||||||
|
UpdateMemoryCards();
|
||||||
|
|
||||||
|
// Enable tty by patching bios.
|
||||||
|
const BIOS::Hash bios_hash = BIOS::GetHash(*bios_image);
|
||||||
|
if (GetSettings().bios_patch_tty_enable)
|
||||||
|
BIOS::PatchBIOSEnableTTY(*bios_image, bios_hash);
|
||||||
|
|
||||||
|
// Load EXE late after BIOS.
|
||||||
|
if (exe_boot && !LoadEXE(filename, *bios_image))
|
||||||
|
{
|
||||||
|
m_host_interface->ReportError(SmallString::FromFormat("Failed to load EXE file '%s'", filename));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert CD, and apply fastboot patch if enabled.
|
||||||
|
m_cdrom->InsertMedia(std::move(media));
|
||||||
|
if (m_cdrom->HasMedia() && GetSettings().bios_patch_fast_boot)
|
||||||
|
BIOS::PatchBIOSFastBoot(*bios_image, bios_hash);
|
||||||
|
|
||||||
|
// Load the patched BIOS up.
|
||||||
|
m_bus->SetBIOS(*bios_image);
|
||||||
|
|
||||||
|
// Good to go.
|
||||||
|
Reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::InitializeComponents()
|
||||||
{
|
{
|
||||||
m_cpu->Initialize(m_bus.get());
|
m_cpu->Initialize(m_bus.get());
|
||||||
m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_pad.get(),
|
m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_pad.get(),
|
||||||
|
@ -86,32 +176,23 @@ bool System::Initialize()
|
||||||
m_timers->Initialize(this, m_interrupt_controller.get());
|
m_timers->Initialize(this, m_interrupt_controller.get());
|
||||||
m_spu->Initialize(this, m_dma.get(), m_interrupt_controller.get());
|
m_spu->Initialize(this, m_dma.get(), m_interrupt_controller.get());
|
||||||
m_mdec->Initialize(this, m_dma.get());
|
m_mdec->Initialize(this, m_dma.get());
|
||||||
|
|
||||||
if (!CreateGPU())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!LoadBIOS())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
UpdateMemoryCards();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::CreateGPU()
|
bool System::CreateGPU()
|
||||||
{
|
{
|
||||||
switch (m_host_interface->GetSettings().gpu_renderer)
|
switch (m_host_interface->GetSettings().gpu_renderer)
|
||||||
{
|
{
|
||||||
case Settings::GPURenderer::HardwareOpenGL:
|
case GPURenderer::HardwareOpenGL:
|
||||||
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
case Settings::GPURenderer::HardwareD3D11:
|
case GPURenderer::HardwareD3D11:
|
||||||
m_gpu = GPU::CreateHardwareD3D11Renderer();
|
m_gpu = GPU::CreateHardwareD3D11Renderer();
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
case Settings::GPURenderer::Software:
|
case GPURenderer::Software:
|
||||||
default:
|
default:
|
||||||
m_gpu = GPU::CreateSoftwareRenderer();
|
m_gpu = GPU::CreateSoftwareRenderer();
|
||||||
break;
|
break;
|
||||||
|
@ -122,7 +203,7 @@ bool System::CreateGPU()
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to initialize GPU, falling back to software");
|
Log_ErrorPrintf("Failed to initialize GPU, falling back to software");
|
||||||
m_gpu.reset();
|
m_gpu.reset();
|
||||||
m_host_interface->GetSettings().gpu_renderer = Settings::GPURenderer::Software;
|
m_host_interface->GetSettings().gpu_renderer = GPURenderer::Software;
|
||||||
m_gpu = GPU::CreateSoftwareRenderer();
|
m_gpu = GPU::CreateSoftwareRenderer();
|
||||||
if (!m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(),
|
if (!m_gpu->Initialize(m_host_interface->GetDisplay(), this, m_dma.get(), m_interrupt_controller.get(),
|
||||||
m_timers.get()))
|
m_timers.get()))
|
||||||
|
@ -136,49 +217,6 @@ bool System::CreateGPU()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::LoadBIOS()
|
|
||||||
{
|
|
||||||
if (!m_bus->LoadBIOS(GetSettings().bios_path.c_str()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// apply patches
|
|
||||||
u8 bios_hash[16];
|
|
||||||
m_bus->GetBIOSHash(bios_hash);
|
|
||||||
SmallString bios_hash_string;
|
|
||||||
bios_hash_string.Format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", bios_hash[0],
|
|
||||||
bios_hash[1], bios_hash[2], bios_hash[3], bios_hash[4], bios_hash[5], bios_hash[6],
|
|
||||||
bios_hash[7], bios_hash[8], bios_hash[9], bios_hash[10], bios_hash[11], bios_hash[12],
|
|
||||||
bios_hash[13], bios_hash[14], bios_hash[15]);
|
|
||||||
Log_InfoPrintf("BIOS hash: %s", bios_hash_string.GetCharArray());
|
|
||||||
|
|
||||||
if (bios_hash_string == BIOSHashes::SCPH_1000 || bios_hash_string == BIOSHashes::SCPH_1001 ||
|
|
||||||
bios_hash_string == BIOSHashes::SCPH_1002 || bios_hash_string == BIOSHashes::SCPH_5500 ||
|
|
||||||
bios_hash_string == BIOSHashes::SCPH_5501 || bios_hash_string == BIOSHashes::SCPH_5502)
|
|
||||||
{
|
|
||||||
if (GetSettings().bios_patch_tty_enable)
|
|
||||||
{
|
|
||||||
Log_InfoPrintf("Patching BIOS to enable TTY/printf");
|
|
||||||
m_bus->PatchBIOS(0x1FC06F0C, 0x24010001);
|
|
||||||
m_bus->PatchBIOS(0x1FC06F14, 0xAF81A9C0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetSettings().bios_patch_fast_boot)
|
|
||||||
{
|
|
||||||
Log_InfoPrintf("Patching BIOS for fast boot");
|
|
||||||
|
|
||||||
// Replace the shell entry point with a return back to the bootstrap.
|
|
||||||
m_bus->PatchBIOS(0x1FC18000, 0x03E00008);
|
|
||||||
m_bus->PatchBIOS(0x1FC18004, 0x00000000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log_WarningPrintf("Unknown BIOS version, not applying patches");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool System::DoState(StateWrapper& sw)
|
bool System::DoState(StateWrapper& sw)
|
||||||
{
|
{
|
||||||
if (!sw.DoMarker("System"))
|
if (!sw.DoMarker("System"))
|
||||||
|
@ -260,7 +298,7 @@ void System::RunFrame()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::LoadEXE(const char* filename)
|
bool System::LoadEXE(const char* filename, std::vector<u8>& bios_image)
|
||||||
{
|
{
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct EXEHeader
|
struct EXEHeader
|
||||||
|
@ -326,25 +364,11 @@ bool System::LoadEXE(const char* filename)
|
||||||
std::fclose(fp);
|
std::fclose(fp);
|
||||||
|
|
||||||
// patch the BIOS to jump to the executable directly
|
// patch the BIOS to jump to the executable directly
|
||||||
{
|
|
||||||
const u32 r_pc = header.load_address;
|
const u32 r_pc = header.load_address;
|
||||||
const u32 r_gp = header.initial_gp;
|
const u32 r_gp = header.initial_gp;
|
||||||
const u32 r_sp = header.initial_sp_base;
|
const u32 r_sp = header.initial_sp_base;
|
||||||
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
|
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
|
||||||
|
return BIOS::PatchBIOSForEXE(bios_image, r_pc, r_gp, r_sp, r_fp);
|
||||||
// pc has to be done first because we can't load it in the delay slot
|
|
||||||
m_bus->PatchBIOS(0xBFC06FF0, UINT32_C(0x3C080000) | r_pc >> 16); // lui $t0, (r_pc >> 16)
|
|
||||||
m_bus->PatchBIOS(0xBFC06FF4, UINT32_C(0x35080000) | (r_pc & UINT32_C(0xFFFF))); // ori $t0, $t0, (r_pc & 0xFFFF)
|
|
||||||
m_bus->PatchBIOS(0xBFC06FF8, UINT32_C(0x3C1C0000) | r_gp >> 16); // lui $gp, (r_gp >> 16)
|
|
||||||
m_bus->PatchBIOS(0xBFC06FFC, UINT32_C(0x379C0000) | (r_gp & UINT32_C(0xFFFF))); // ori $gp, $gp, (r_gp & 0xFFFF)
|
|
||||||
m_bus->PatchBIOS(0xBFC07000, UINT32_C(0x3C1D0000) | r_sp >> 16); // lui $sp, (r_sp >> 16)
|
|
||||||
m_bus->PatchBIOS(0xBFC07004, UINT32_C(0x37BD0000) | (r_sp & UINT32_C(0xFFFF))); // ori $sp, $sp, (r_sp & 0xFFFF)
|
|
||||||
m_bus->PatchBIOS(0xBFC07008, UINT32_C(0x3C1E0000) | r_fp >> 16); // lui $fp, (r_fp >> 16)
|
|
||||||
m_bus->PatchBIOS(0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
|
|
||||||
m_bus->PatchBIOS(0xBFC07010, UINT32_C(0x37DE0000) | (r_fp & UINT32_C(0xFFFF))); // ori $fp, $fp, (r_fp & 0xFFFF)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::SetExpansionROM(const char* filename)
|
bool System::SetExpansionROM(const char* filename)
|
||||||
|
@ -438,7 +462,12 @@ bool System::HasMedia() const
|
||||||
|
|
||||||
bool System::InsertMedia(const char* path)
|
bool System::InsertMedia(const char* path)
|
||||||
{
|
{
|
||||||
return m_cdrom->InsertMedia(path);
|
std::unique_ptr<CDImage> image = CDImage::Open(path);
|
||||||
|
if (!image)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_cdrom->InsertMedia(std::move(image));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::RemoveMedia()
|
void System::RemoveMedia()
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "host_interface.h"
|
#include "host_interface.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
class ByteStream;
|
class ByteStream;
|
||||||
|
class CDImage;
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
|
@ -24,9 +26,17 @@ class MDEC;
|
||||||
class System
|
class System
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
System(HostInterface* host_interface);
|
|
||||||
~System();
|
~System();
|
||||||
|
|
||||||
|
/// Detects region for the specified image file.
|
||||||
|
static std::optional<ConsoleRegion> GetRegionForCDImage(const CDImage* image);
|
||||||
|
|
||||||
|
/// Returns true if the filename is a PlayStation executable we can inject.
|
||||||
|
static bool IsPSExe(const char* filename);
|
||||||
|
|
||||||
|
/// Creates a new System.
|
||||||
|
static std::unique_ptr<System> Create(HostInterface* host_interface);
|
||||||
|
|
||||||
// Accessing components.
|
// Accessing components.
|
||||||
HostInterface* GetHostInterface() const { return m_host_interface; }
|
HostInterface* GetHostInterface() const { return m_host_interface; }
|
||||||
CPU::Core* GetCPU() const { return m_cpu.get(); }
|
CPU::Core* GetCPU() const { return m_cpu.get(); }
|
||||||
|
@ -48,7 +58,7 @@ public:
|
||||||
|
|
||||||
const Settings& GetSettings() { return m_host_interface->GetSettings(); }
|
const Settings& GetSettings() { return m_host_interface->GetSettings(); }
|
||||||
|
|
||||||
bool Initialize();
|
bool Boot(const char* filename);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
bool LoadState(ByteStream* state);
|
bool LoadState(ByteStream* state);
|
||||||
|
@ -59,7 +69,7 @@ public:
|
||||||
|
|
||||||
void RunFrame();
|
void RunFrame();
|
||||||
|
|
||||||
bool LoadEXE(const char* filename);
|
bool LoadEXE(const char* filename, std::vector<u8>& bios_image);
|
||||||
bool SetExpansionROM(const char* filename);
|
bool SetExpansionROM(const char* filename);
|
||||||
|
|
||||||
void SetDowncount(TickCount downcount);
|
void SetDowncount(TickCount downcount);
|
||||||
|
@ -76,9 +86,12 @@ public:
|
||||||
void RemoveMedia();
|
void RemoveMedia();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
System(HostInterface* host_interface);
|
||||||
|
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
bool CreateGPU();
|
bool CreateGPU();
|
||||||
bool LoadBIOS();
|
|
||||||
|
void InitializeComponents();
|
||||||
|
|
||||||
HostInterface* m_host_interface;
|
HostInterface* m_host_interface;
|
||||||
std::unique_ptr<CPU::Core> m_cpu;
|
std::unique_ptr<CPU::Core> m_cpu;
|
||||||
|
@ -91,6 +104,7 @@ private:
|
||||||
std::unique_ptr<Timers> m_timers;
|
std::unique_ptr<Timers> m_timers;
|
||||||
std::unique_ptr<SPU> m_spu;
|
std::unique_ptr<SPU> m_spu;
|
||||||
std::unique_ptr<MDEC> m_mdec;
|
std::unique_ptr<MDEC> m_mdec;
|
||||||
|
ConsoleRegion m_region = ConsoleRegion::NTSC_U;
|
||||||
u32 m_frame_number = 1;
|
u32 m_frame_number = 1;
|
||||||
u32 m_internal_frame_number = 1;
|
u32 m_internal_frame_number = 1;
|
||||||
u32 m_global_tick_counter = 0;
|
u32 m_global_tick_counter = 0;
|
||||||
|
|
|
@ -22,3 +22,19 @@ using TickCount = s32;
|
||||||
static constexpr TickCount MASTER_CLOCK = 44100 * 0x300; // 33868800Hz or 33.8688MHz, also used as CPU clock
|
static constexpr TickCount MASTER_CLOCK = 44100 * 0x300; // 33868800Hz or 33.8688MHz, also used as CPU clock
|
||||||
static constexpr TickCount MAX_SLICE_SIZE = MASTER_CLOCK / 10;
|
static constexpr TickCount MAX_SLICE_SIZE = MASTER_CLOCK / 10;
|
||||||
|
|
||||||
|
enum class ConsoleRegion
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
NTSC_J,
|
||||||
|
NTSC_U,
|
||||||
|
PAL,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GPURenderer : u8
|
||||||
|
{
|
||||||
|
HardwareD3D11,
|
||||||
|
HardwareOpenGL,
|
||||||
|
Software,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
|
@ -133,21 +133,7 @@ void SDLHostInterface::SaveSettings()
|
||||||
m_settings.Save(m_settings_filename.c_str());
|
m_settings.Save(m_settings_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SDLHostInterface::InitializeSystem(const char* filename, const char* exp1_filename)
|
void SDLHostInterface::ConnectControllers()
|
||||||
{
|
|
||||||
if (!HostInterface::InitializeSystem(filename, exp1_filename))
|
|
||||||
{
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "System initialization failed.", m_window);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectDevices();
|
|
||||||
UpdateAudioVisualSync();
|
|
||||||
m_paused = m_system->GetSettings().start_paused;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDLHostInterface::ConnectDevices()
|
|
||||||
{
|
{
|
||||||
m_controller = DigitalController::Create();
|
m_controller = DigitalController::Create();
|
||||||
m_system->SetController(0, m_controller);
|
m_system->SetController(0, m_controller);
|
||||||
|
@ -216,7 +202,7 @@ std::unique_ptr<SDLHostInterface> SDLHostInterface::Create(const char* filename
|
||||||
const bool boot = (filename != nullptr || exp1_filename != nullptr || save_state_filename != nullptr);
|
const bool boot = (filename != nullptr || exp1_filename != nullptr || save_state_filename != nullptr);
|
||||||
if (boot)
|
if (boot)
|
||||||
{
|
{
|
||||||
if (!intf->InitializeSystem(filename, exp1_filename))
|
if (!intf->CreateSystem() || !intf->BootSystem(filename, exp1_filename))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (save_state_filename)
|
if (save_state_filename)
|
||||||
|
@ -235,9 +221,14 @@ TinyString SDLHostInterface::GetSaveStateFilename(u32 index)
|
||||||
return TinyString::FromFormat("savestate_%u.bin", index);
|
return TinyString::FromFormat("savestate_%u.bin", index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDLHostInterface::ReportError(const char* message)
|
||||||
|
{
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "DuckStation Error", message, m_window);
|
||||||
|
}
|
||||||
|
|
||||||
void SDLHostInterface::ReportMessage(const char* message)
|
void SDLHostInterface::ReportMessage(const char* message)
|
||||||
{
|
{
|
||||||
AddOSDMessage(message, 3.0f);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "DuckStation Information", message, m_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 SDLButtonToHostButton(u32 button)
|
static inline u32 SDLButtonToHostButton(u32 button)
|
||||||
|
@ -736,13 +727,13 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Renderer"))
|
if (ImGui::BeginMenu("Renderer"))
|
||||||
{
|
{
|
||||||
const Settings::GPURenderer current = m_settings.gpu_renderer;
|
const GPURenderer current = m_settings.gpu_renderer;
|
||||||
for (u32 i = 0; i < static_cast<u32>(Settings::GPURenderer::Count); i++)
|
for (u32 i = 0; i < static_cast<u32>(GPURenderer::Count); i++)
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem(Settings::GetRendererDisplayName(static_cast<Settings::GPURenderer>(i)), nullptr,
|
if (ImGui::MenuItem(Settings::GetRendererDisplayName(static_cast<GPURenderer>(i)), nullptr,
|
||||||
i == static_cast<u32>(current)))
|
i == static_cast<u32>(current)))
|
||||||
{
|
{
|
||||||
m_settings.gpu_renderer = static_cast<Settings::GPURenderer>(i);
|
m_settings.gpu_renderer = static_cast<GPURenderer>(i);
|
||||||
settings_changed = true;
|
settings_changed = true;
|
||||||
if (m_system)
|
if (m_system)
|
||||||
SwitchGPURenderer();
|
SwitchGPURenderer();
|
||||||
|
@ -998,12 +989,12 @@ void SDLHostInterface::DrawSettingsWindow()
|
||||||
if (ImGui::Combo(
|
if (ImGui::Combo(
|
||||||
"##gpu_renderer", &gpu_renderer,
|
"##gpu_renderer", &gpu_renderer,
|
||||||
[](void*, int index, const char** out_text) {
|
[](void*, int index, const char** out_text) {
|
||||||
*out_text = Settings::GetRendererDisplayName(static_cast<Settings::GPURenderer>(index));
|
*out_text = Settings::GetRendererDisplayName(static_cast<GPURenderer>(index));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
nullptr, static_cast<int>(Settings::GPURenderer::Count)))
|
nullptr, static_cast<int>(GPURenderer::Count)))
|
||||||
{
|
{
|
||||||
m_settings.gpu_renderer = static_cast<Settings::GPURenderer>(gpu_renderer);
|
m_settings.gpu_renderer = static_cast<GPURenderer>(gpu_renderer);
|
||||||
SwitchGPURenderer();
|
SwitchGPURenderer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1213,21 +1204,16 @@ void SDLHostInterface::DoReset()
|
||||||
void SDLHostInterface::DoPowerOff()
|
void SDLHostInterface::DoPowerOff()
|
||||||
{
|
{
|
||||||
Assert(m_system);
|
Assert(m_system);
|
||||||
ShutdownSystem();
|
DestroySystem();
|
||||||
AddOSDMessage("System powered off.");
|
AddOSDMessage("System powered off.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLHostInterface::DoResume()
|
void SDLHostInterface::DoResume()
|
||||||
{
|
{
|
||||||
Assert(!m_system);
|
Assert(!m_system);
|
||||||
if (!InitializeSystem())
|
if (!CreateSystem() || !BootSystem(nullptr, RESUME_SAVESTATE_FILENAME))
|
||||||
return;
|
|
||||||
|
|
||||||
if (!LoadState(RESUME_SAVESTATE_FILENAME))
|
|
||||||
{
|
{
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Load state failed",
|
DestroySystem();
|
||||||
"Failed to load the resume save state. Stopping emulation.", m_window);
|
|
||||||
ShutdownSystem();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1244,8 +1230,11 @@ void SDLHostInterface::DoStartDisc()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AddOSDMessage(SmallString::FromFormat("Starting disc from '%s'...", path));
|
AddOSDMessage(SmallString::FromFormat("Starting disc from '%s'...", path));
|
||||||
if (!InitializeSystem(path, nullptr))
|
if (!CreateSystem() || !BootSystem(path, nullptr))
|
||||||
|
{
|
||||||
|
DestroySystem();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ResetPerformanceCounters();
|
ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
|
@ -1256,8 +1245,11 @@ void SDLHostInterface::DoStartBIOS()
|
||||||
Assert(!m_system);
|
Assert(!m_system);
|
||||||
|
|
||||||
AddOSDMessage("Starting BIOS...");
|
AddOSDMessage("Starting BIOS...");
|
||||||
if (!InitializeSystem(nullptr, nullptr))
|
if (!CreateSystem() || !BootSystem(nullptr, nullptr))
|
||||||
|
{
|
||||||
|
DestroySystem();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ResetPerformanceCounters();
|
ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
|
@ -1282,10 +1274,19 @@ void SDLHostInterface::DoChangeDisc()
|
||||||
|
|
||||||
void SDLHostInterface::DoLoadState(u32 index)
|
void SDLHostInterface::DoLoadState(u32 index)
|
||||||
{
|
{
|
||||||
if (!HasSystem() && !InitializeSystem(nullptr, nullptr))
|
if (HasSystem())
|
||||||
return;
|
{
|
||||||
|
|
||||||
LoadState(GetSaveStateFilename(index));
|
LoadState(GetSaveStateFilename(index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!CreateSystem() || !BootSystem(nullptr, GetSaveStateFilename(index)))
|
||||||
|
{
|
||||||
|
DestroySystem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ResetPerformanceCounters();
|
ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
@ -1321,16 +1322,15 @@ void SDLHostInterface::DoToggleSoftwareRendering()
|
||||||
if (!m_system)
|
if (!m_system)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_settings.gpu_renderer != Settings::GPURenderer::Software)
|
if (m_settings.gpu_renderer != GPURenderer::Software)
|
||||||
{
|
{
|
||||||
m_settings.gpu_renderer = Settings::GPURenderer::Software;
|
m_settings.gpu_renderer = GPURenderer::Software;
|
||||||
AddOSDMessage("Switched to software GPU renderer.");
|
AddOSDMessage("Switched to software GPU renderer.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_settings.gpu_renderer = m_display->GetRenderAPI() == HostDisplay::RenderAPI::D3D11 ?
|
m_settings.gpu_renderer = m_display->GetRenderAPI() == HostDisplay::RenderAPI::D3D11 ? GPURenderer::HardwareD3D11 :
|
||||||
Settings::GPURenderer::HardwareD3D11 :
|
GPURenderer::HardwareOpenGL;
|
||||||
Settings::GPURenderer::HardwareOpenGL;
|
|
||||||
AddOSDMessage("Switched to hardware GPU renderer.");
|
AddOSDMessage("Switched to hardware GPU renderer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1431,6 +1431,6 @@ void SDLHostInterface::Run()
|
||||||
"Saving state failed, you will not be able to resume this session.", m_window);
|
"Saving state failed, you will not be able to resume this session.", m_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShutdownSystem();
|
DestroySystem();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ class DigitalController;
|
||||||
class MemoryCard;
|
class MemoryCard;
|
||||||
class AudioStream;
|
class AudioStream;
|
||||||
|
|
||||||
class SDLHostInterface : public HostInterface
|
class SDLHostInterface final : public HostInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SDLHostInterface();
|
SDLHostInterface();
|
||||||
|
@ -28,6 +28,7 @@ public:
|
||||||
|
|
||||||
static TinyString GetSaveStateFilename(u32 index);
|
static TinyString GetSaveStateFilename(u32 index);
|
||||||
|
|
||||||
|
void ReportError(const char* message) override;
|
||||||
void ReportMessage(const char* message) override;
|
void ReportMessage(const char* message) override;
|
||||||
|
|
||||||
// Adds OSD messages, duration is in seconds.
|
// Adds OSD messages, duration is in seconds.
|
||||||
|
@ -35,6 +36,9 @@ public:
|
||||||
|
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ConnectControllers() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr u32 NUM_QUICK_SAVE_STATES = 10;
|
static constexpr u32 NUM_QUICK_SAVE_STATES = 10;
|
||||||
static constexpr char RESUME_SAVESTATE_FILENAME[] = "savestate_resume.bin";
|
static constexpr char RESUME_SAVESTATE_FILENAME[] = "savestate_resume.bin";
|
||||||
|
@ -49,7 +53,7 @@ private:
|
||||||
bool HasSystem() const { return static_cast<bool>(m_system); }
|
bool HasSystem() const { return static_cast<bool>(m_system); }
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
bool UseOpenGLRenderer() const { return m_settings.gpu_renderer == Settings::GPURenderer::HardwareOpenGL; }
|
bool UseOpenGLRenderer() const { return m_settings.gpu_renderer == GPURenderer::HardwareOpenGL; }
|
||||||
#else
|
#else
|
||||||
bool UseOpenGLRenderer() const { return true; }
|
bool UseOpenGLRenderer() const { return true; }
|
||||||
#endif
|
#endif
|
||||||
|
@ -64,8 +68,6 @@ private:
|
||||||
|
|
||||||
void SaveSettings();
|
void SaveSettings();
|
||||||
|
|
||||||
bool InitializeSystem(const char* filename = nullptr, const char* exp1_filename = nullptr);
|
|
||||||
void ConnectDevices();
|
|
||||||
void ResetPerformanceCounters();
|
void ResetPerformanceCounters();
|
||||||
void SwitchGPURenderer();
|
void SwitchGPURenderer();
|
||||||
void UpdateFullscreen();
|
void UpdateFullscreen();
|
||||||
|
|
Loading…
Reference in a new issue