This commit is contained in:
Nayla Hanegan 2024-11-04 10:59:00 -05:00
commit 76c08d3a22
119 changed files with 47784 additions and 40258 deletions

View file

@ -672,7 +672,7 @@ dolphin_make_imported_target_if_missing(LibLZMA::LibLZMA LIBLZMA)
dolphin_find_optional_system_library_pkgconfig(ZSTD libzstd>=1.4.0 zstd::zstd Externals/zstd) dolphin_find_optional_system_library_pkgconfig(ZSTD libzstd>=1.4.0 zstd::zstd Externals/zstd)
dolphin_find_optional_system_library_pkgconfig(ZLIB zlib-ng ZLIB::ZLIB Externals/zlib-ng) add_subdirectory(Externals/zlib-ng)
dolphin_find_optional_system_library_pkgconfig(MINIZIP dolphin_find_optional_system_library_pkgconfig(MINIZIP
"minizip>=4.0.4" minizip::minizip Externals/minizip-ng "minizip>=4.0.4" minizip::minizip Externals/minizip-ng

View file

@ -1,4 +1,4 @@
# GEDE01, GEDP01 - Eternal Darkness # GEDE01, GEDP01, GEDJ01, GEDW01 - Eternal Darkness
[Core] [Core]
# Values set here will override the main Dolphin settings. # Values set here will override the main Dolphin settings.

View file

@ -0,0 +1,8 @@
# GEDE01 - Eternal Darkness
[OnFrame]
$Fix startup hang
0x801EF444:dword:0x480371ED
[OnFrame_Enabled]
$Fix startup hang

View file

@ -0,0 +1,8 @@
# GEDJ01 - Eternal Darkness
[OnFrame]
$Fix startup hang
0x801E4588:dword:0x48036E71
[OnFrame_Enabled]
$Fix startup hang

View file

@ -0,0 +1,8 @@
# GEDP01 - Eternal Darkness
[OnFrame]
$Fix startup hang
0x801E3BC4:dword:0x48036F15
[OnFrame_Enabled]
$Fix startup hang

View file

@ -0,0 +1,8 @@
# GEDW01 - Eternal Darkness
[OnFrame]
$Fix startup hang
0x801BE42C:dword:0x48036E65
[OnFrame_Enabled]
$Fix startup hang

View file

@ -9,8 +9,5 @@
[ActionReplay] [ActionReplay]
# Add action replay cheats here. # Add action replay cheats here.
[Video_Settings]
[Video_Hacks] [Video_Hacks]
EFBAccessEnable = False
ImmediateXFBEnable = False ImmediateXFBEnable = False

View file

@ -1,14 +0,0 @@
# GXBE69, GXBP69 - SSX3
[Core]
# Values set here will override the main Dolphin settings.
[OnFrame]
# Add memory patches to be applied every frame here.
[ActionReplay]
# Add action replay cheats here.
[Video_Hacks]
EFBAccessEnable = False

View file

@ -16,6 +16,3 @@ $Disable blur
[Video_Settings] [Video_Settings]
SafeTextureCacheColorSamples = 0 SafeTextureCacheColorSamples = 0
[Video_Hacks]
EFBAccessEnable = False

View file

@ -1,14 +0,0 @@
# SNCE8P, SNCJ8P, SNCP8P - SONIC COLOURS
[Core]
# Values set here will override the main Dolphin settings.
[OnFrame]
# Add memory patches to be applied every frame here.
[ActionReplay]
# Add action replay cheats here.
[Video_Hacks]
EFBAccessEnable = False

22
Flatpak/SDL2/SDL2.json Normal file
View file

@ -0,0 +1,22 @@
{
"name": "SDL2",
"buildsystem": "autotools",
"config-opts": ["--disable-static"],
"sources": [
{
"type": "dir",
"path": "../../Externals/SDL/SDL"
}
],
"cleanup": [ "/bin/sdl2-config",
"/include",
"/lib/libSDL2.la",
"/lib/libSDL2main.a",
"/lib/libSDL2main.la",
"/lib/libSDL2_test.a",
"/lib/libSDL2_test.la",
"/lib/cmake",
"/share/aclocal",
"/lib/pkgconfig"]
}

8
Flatpak/fill_release_node.sh Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
DATE=$(git log -1 --pretty=%cd --date=iso8601 --date=format:'%Y-%m-%d')
sed -i -e "s/@DATE_PLACEHOLDER/${DATE}/" org.DolphinEmu.dolphin-emu.metainfo.xml
VERSION=$(git describe --tags | sed -E 's/^([0-9]+-[0-9]+).*/\1/')
sed -i -e "s/@VERSION_PLACEHOLDER/${VERSION}/" org.DolphinEmu.dolphin-emu.metainfo.xml

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2016 Jeremy Newton -->
<component type="desktop-application">
<id>org.DolphinEmu.dolphin-emu</id>
<name>Dolphin Emulator</name>
<developer id="org.dolphin-emu">
<name>Dolphin Emulator Project</name>
</developer>
<summary>GameCube / Wii</summary>
<metadata_license>CC-BY-SA-3.0</metadata_license>
<project_license>GPL-2.0+</project_license>
<content_rating type="oars-1.0"/>
<!-- Descriptions taken from Dolphin Homepage -->
<description><p>Dolphin is an emulator for two recent Nintendo video game consoles: the GameCube and the Wii. It allows PC gamers to enjoy games for these two consoles in full HD (1080p) with several enhancements: compatibility with all PC controllers, turbo speed, networked multiplayer, and even more!</p></description>
<screenshots>
<screenshot type="default">
<caption>Dolphin's main window</caption>
<image type="source">http://dolphin-emu.org/m/user/flatpak/screenshot_1.png</image>
</screenshot>
<screenshot>
<caption>In-game</caption>
<image type="source">http://dolphin-emu.org/m/user/flatpak/screenshot_2.png</image>
</screenshot>
<screenshot>
<caption>Graphics configuration</caption>
<image type="source">http://dolphin-emu.org/m/user/flatpak/screenshot_3.png</image>
</screenshot>
<screenshot>
<caption>Controller configuration</caption>
<image type="source">http://dolphin-emu.org/m/user/flatpak/screenshot_4.png</image>
</screenshot>
</screenshots>
<launchable type="desktop-id">dolphin-emu.desktop</launchable>
<provides>
<binary>dolphin-emu</binary>
<id>dolphin-emu.desktop</id>
</provides>
<releases>
<release version="@VERSION_PLACEHOLDER" date="@DATE_PLACEHOLDER"/>
</releases>
<url type="homepage">https://dolphin-emu.org</url>
<url type="bugtracker">https://bugs.dolphin-emu.org/projects/emulator/issues</url>
<url type="faq">https://dolphin-emu.org/docs/faq/</url>
<url type="help">https://dolphin-emu.org/docs/guides/</url>
<url type="translate">https://www.transifex.com/projects/p/dolphin-emu</url>
<url type="contact">https://dolphin-emu.org/docs/faq/#ive-got-idea-make-dolphin-better-how-should-i-tell</url>
<url type="vcs-browser">https://github.com/dolphin-emu/dolphin</url>
<url type="contribute">https://github.com/dolphin-emu/dolphin/blob/master/Contributing.md</url>
</component>

View file

@ -0,0 +1,83 @@
app-id: org.DolphinEmu.dolphin-emu
runtime: org.kde.Platform
runtime-version: '6.7'
sdk: org.kde.Sdk
command: dolphin-emu-wrapper
rename-desktop-file: dolphin-emu.desktop
rename-icon: dolphin-emu
finish-args:
- --device=all
- --socket=pulseaudio
# dolphin doesn't work on wayland (only the ui does), if a user were to set
# this env variable globally to wayland then games wouldn't work.
# we overwrite the setting and force xcb to prevent this from happening.
- --env=QT_QPA_PLATFORM=xcb
- --socket=x11
- --share=network
- --share=ipc
# required for the emulated bluetooth adapter feature to work.
- --allow=bluetooth
- --filesystem=xdg-run/app/com.discordapp.Discord:create
- --talk-name=org.freedesktop.ScreenSaver
# required for Gamescope on Steam Deck
- --filesystem=xdg-run/gamescope-0:ro
modules:
# enables motion controls on non-wii controllers (switch, ps4, etc)
# requires a udev rule enabling Motion Sensors access
- name: libevdev
buildsystem: meson
config-opts:
- -Dtests=disabled
- -Ddocumentation=disabled
sources:
- type: archive
url: https://www.freedesktop.org/software/libevdev/libevdev-1.13.3.tar.xz
sha256: abf1aace86208eebdd5d3550ffded4c8d73bb405b796d51c389c9d0604cbcfbf
x-checker-data:
type: anitya
project-id: 20540
stable-only: true
url-template: https://www.freedesktop.org/software/libevdev/libevdev-$version.tar.xz
# needed for screensaver inhibition
- name: xdg-screensaver-shim
buildsystem: meson
sources:
- type: archive
url: https://github.com/Unrud/xdg-screensaver-shim/archive/0.0.2.tar.gz
sha256: 0ed2a69fe6ee6cbffd2fe16f85116db737f17fb1e79bfb812d893cf15c728399
# build the vendored SDL2 from Externals until the runtime gets 2.30.6
- SDL2/SDL2.json
- name: dolphin-emu
buildsystem: cmake-ninja
config-opts:
- -DCMAKE_BUILD_TYPE=Release
- -DENABLE_ALSA=OFF
- -DENABLE_SDL=ON
- -DENABLE_EVDEV=ON
- -DDISTRIBUTOR=dolphin-emu.org
cleanup:
- /share/man
post-install:
- install -D -t ${FLATPAK_DEST}/bin/ dolphin-emu-wrapper
- "${FLATPAK_BUILDER_BUILDDIR}/Flatpak/fill_release_node.sh"
- install -Dm644 -t ${FLATPAK_DEST}/share/metainfo/ org.DolphinEmu.dolphin-emu.metainfo.xml
- desktop-file-edit --set-key=Exec --set-value='/app/bin/dolphin-emu-wrapper'
/app/share/applications/dolphin-emu.desktop
sources:
- type: dir
path: ..
- type: file
path: org.DolphinEmu.dolphin-emu.metainfo.xml.in
dest-filename: org.DolphinEmu.dolphin-emu.metainfo.xml
- type: script
commands:
- |
for i in {0..9}; do
test -S $XDG_RUNTIME_DIR/discord-ipc-$i ||
ln -sf {app/com.discordapp.Discord,$XDG_RUNTIME_DIR}/discord-ipc-$i;
done
dolphin-emu "$@"
dest-filename: dolphin-emu-wrapper

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -302,7 +302,7 @@ public final class NativeLibrary
public static native int DefaultCPUCore(); public static native int DefaultCPUCore();
public static native String GetDefaultGraphicsBackendName(); public static native String GetDefaultGraphicsBackendConfigName();
public static native int GetMaxLogLevel(); public static native int GetMaxLogLevel();

View file

@ -789,7 +789,7 @@ enum class BooleanSetting(
Settings.FILE_GFX, Settings.FILE_GFX,
Settings.SECTION_GFX_HACKS, Settings.SECTION_GFX_HACKS,
"EFBAccessEnable", "EFBAccessEnable",
true false
), ),
GFX_HACK_EFB_DEFER_INVALIDATION( GFX_HACK_EFB_DEFER_INVALIDATION(
Settings.FILE_GFX, Settings.FILE_GFX,

View file

@ -45,7 +45,7 @@ enum class StringSetting(
Settings.FILE_DOLPHIN, Settings.FILE_DOLPHIN,
Settings.SECTION_INI_CORE, Settings.SECTION_INI_CORE,
"GFXBackend", "GFXBackend",
NativeLibrary.GetDefaultGraphicsBackendName() NativeLibrary.GetDefaultGraphicsBackendConfigName()
), ),
MAIN_DUMP_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "DumpPath", ""), MAIN_DUMP_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "DumpPath", ""),
MAIN_LOAD_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "LoadPath", ""), MAIN_LOAD_PATH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_GENERAL, "LoadPath", ""),

View file

@ -146,7 +146,7 @@ void Host_UpdateDisasmDialog()
{ {
} }
void Host_JitCacheCleared() void Host_JitCacheInvalidation()
{ {
} }
@ -408,9 +408,10 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_DefaultCPUCo
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_GetDefaultGraphicsBackendName(JNIEnv* env, jclass) Java_org_dolphinemu_dolphinemu_NativeLibrary_GetDefaultGraphicsBackendConfigName(JNIEnv* env,
jclass)
{ {
return ToJString(env, VideoBackendBase::GetDefaultBackendName()); return ToJString(env, VideoBackendBase::GetDefaultBackendConfigName());
} }
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetMaxLogLevel(JNIEnv*, jclass) JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetMaxLogLevel(JNIEnv*, jclass)

View file

@ -47,7 +47,7 @@ private:
std::array<uint32_t, 36 + sizeof...(ExtraMatches)> _conns; std::array<uint32_t, 36 + sizeof...(ExtraMatches)> _conns;
std::optional<V> _val; std::optional<V> _val;
TrieEntry() { std::fill(_conns.begin(), _conns.end(), INVALID_CONN); } TrieEntry() { _conns.fill(INVALID_CONN); }
}; };
constexpr size_t IndexOf(char c) const constexpr size_t IndexOf(char c) const

View file

@ -17,72 +17,72 @@
namespace Common namespace Common
{ {
SettingsHandler::SettingsHandler() : m_buffer{}, m_position{0}, m_key{INITIAL_SEED}, decoded{""} namespace
{
// Key used to encrypt/decrypt setting.txt contents
constexpr u32 INITIAL_SEED = 0x73B5DBFA;
} // namespace
SettingsWriter::SettingsWriter() : m_buffer{}, m_position{0}, m_key{INITIAL_SEED}
{ {
} }
SettingsHandler::SettingsHandler(const Buffer& buffer) : SettingsHandler() const SettingsBuffer& SettingsWriter::GetBytes() const
{
m_buffer = buffer;
Decrypt();
}
const SettingsHandler::Buffer& SettingsHandler::GetBytes() const
{ {
return m_buffer; return m_buffer;
} }
std::string SettingsHandler::GetValue(std::string_view key) const std::string SettingsReader::GetValue(std::string_view key) const
{ {
constexpr char delim[] = "\n"; constexpr char delim[] = "\n";
std::string toFind = std::string(delim).append(key).append("="); std::string toFind = std::string(delim).append(key).append("=");
size_t found = decoded.find(toFind); size_t found = m_decoded.find(toFind);
if (found != std::string_view::npos) if (found != std::string_view::npos)
{ {
size_t delimFound = decoded.find(delim, found + toFind.length()); size_t delimFound = m_decoded.find(delim, found + toFind.length());
if (delimFound == std::string_view::npos) if (delimFound == std::string_view::npos)
delimFound = decoded.length() - 1; delimFound = m_decoded.length() - 1;
return decoded.substr(found + toFind.length(), delimFound - (found + toFind.length())); return m_decoded.substr(found + toFind.length(), delimFound - (found + toFind.length()));
} }
else else
{ {
toFind = std::string(key).append("="); toFind = std::string(key).append("=");
found = decoded.find(toFind); found = m_decoded.find(toFind);
if (found == 0) if (found == 0)
{ {
size_t delimFound = decoded.find(delim, found + toFind.length()); size_t delimFound = m_decoded.find(delim, found + toFind.length());
if (delimFound == std::string_view::npos) if (delimFound == std::string_view::npos)
delimFound = decoded.length() - 1; delimFound = m_decoded.length() - 1;
return decoded.substr(found + toFind.length(), delimFound - (found + toFind.length())); return m_decoded.substr(found + toFind.length(), delimFound - (found + toFind.length()));
} }
} }
return ""; return "";
} }
void SettingsHandler::Decrypt() SettingsReader::SettingsReader(const SettingsBuffer& buffer) : m_decoded{""}
{ {
while (m_position < m_buffer.size()) u32 key = INITIAL_SEED;
for (u32 position = 0; position < buffer.size(); ++position)
{ {
decoded.push_back((u8)(m_buffer[m_position] ^ m_key)); m_decoded.push_back((u8)(buffer[position] ^ key));
m_position++; key = (key >> 31) | (key << 1);
m_key = (m_key >> 31) | (m_key << 1);
} }
// The decoded data normally uses CRLF line endings, but occasionally // The decoded data normally uses CRLF line endings, but occasionally
// (see the comment in WriteLine), lines can be separated by CRLFLF. // (see the comment in WriteLine), lines can be separated by CRLFLF.
// To handle this, we remove every CR and treat LF as the line ending. // To handle this, we remove every CR and treat LF as the line ending.
// (We ignore empty lines.) // (We ignore empty lines.)
std::erase(decoded, '\x0d'); std::erase(m_decoded, '\x0d');
} }
void SettingsHandler::AddSetting(std::string_view key, std::string_view value) void SettingsWriter::AddSetting(std::string_view key, std::string_view value)
{ {
WriteLine(fmt::format("{}={}\r\n", key, value)); WriteLine(fmt::format("{}={}\r\n", key, value));
} }
void SettingsHandler::WriteLine(std::string_view str) void SettingsWriter::WriteLine(std::string_view str)
{ {
const u32 old_position = m_position; const u32 old_position = m_position;
const u32 old_key = m_key; const u32 old_key = m_key;
@ -106,7 +106,7 @@ void SettingsHandler::WriteLine(std::string_view str)
} }
} }
void SettingsHandler::WriteByte(u8 b) void SettingsWriter::WriteByte(u8 b)
{ {
if (m_position >= m_buffer.size()) if (m_position >= m_buffer.size())
return; return;
@ -116,7 +116,7 @@ void SettingsHandler::WriteByte(u8 b)
m_key = (m_key >> 31) | (m_key << 1); m_key = (m_key >> 31) | (m_key << 1);
} }
std::string SettingsHandler::GenerateSerialNumber() std::string SettingsWriter::GenerateSerialNumber()
{ {
const std::time_t t = std::time(nullptr); const std::time_t t = std::time(nullptr);

View file

@ -13,34 +13,35 @@
namespace Common namespace Common
{ {
class SettingsHandler using SettingsBuffer = std::array<u8, 0x100>;
class SettingsWriter
{ {
public: public:
enum SettingsWriter();
{
SETTINGS_SIZE = 0x100,
// Key used to encrypt/decrypt setting.txt contents
INITIAL_SEED = 0x73B5DBFA
};
using Buffer = std::array<u8, SETTINGS_SIZE>;
SettingsHandler();
explicit SettingsHandler(const Buffer& buffer);
void AddSetting(std::string_view key, std::string_view value); void AddSetting(std::string_view key, std::string_view value);
const Buffer& GetBytes() const; const SettingsBuffer& GetBytes() const;
std::string GetValue(std::string_view key) const;
static std::string GenerateSerialNumber(); static std::string GenerateSerialNumber();
private: private:
void Decrypt();
void WriteLine(std::string_view str); void WriteLine(std::string_view str);
void WriteByte(u8 b); void WriteByte(u8 b);
std::array<u8, SETTINGS_SIZE> m_buffer; SettingsBuffer m_buffer;
u32 m_position, m_key; u32 m_position, m_key;
std::string decoded; };
class SettingsReader
{
public:
explicit SettingsReader(const SettingsBuffer& buffer);
std::string GetValue(std::string_view key) const;
private:
std::string m_decoded;
}; };
} // namespace Common } // namespace Common

View file

@ -24,6 +24,8 @@
#include "Common/Version.h" #include "Common/Version.h"
#include "Common/WorkQueueThread.h" #include "Common/WorkQueueThread.h"
#include "Core/Config/AchievementSettings.h" #include "Core/Config/AchievementSettings.h"
#include "Core/Config/FreeLookSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/HW/VideoInterface.h" #include "Core/HW/VideoInterface.h"
@ -62,7 +64,7 @@ void AchievementManager::Init()
[](const char* message, const rc_client_t* client) { [](const char* message, const rc_client_t* client) {
INFO_LOG_FMT(ACHIEVEMENTS, "{}", message); INFO_LOG_FMT(ACHIEVEMENTS, "{}", message);
}); });
rc_client_set_hardcore_enabled(m_client, Config::Get(Config::RA_HARDCORE_ENABLED)); SetHardcoreMode();
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); }); m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
m_image_queue.Reset("AchievementManagerImageQueue", m_image_queue.Reset("AchievementManagerImageQueue",
[](const std::function<void()>& func) { func(); }); [](const std::function<void()>& func) { func(); });
@ -361,6 +363,13 @@ std::recursive_mutex& AchievementManager::GetLock()
void AchievementManager::SetHardcoreMode() void AchievementManager::SetHardcoreMode()
{ {
rc_client_set_hardcore_enabled(m_client, Config::Get(Config::RA_HARDCORE_ENABLED)); rc_client_set_hardcore_enabled(m_client, Config::Get(Config::RA_HARDCORE_ENABLED));
if (Config::Get(Config::RA_HARDCORE_ENABLED))
{
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false);
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, false);
}
} }
bool AchievementManager::IsHardcoreModeActive() const bool AchievementManager::IsHardcoreModeActive() const

View file

@ -371,12 +371,12 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
const auto fs = system.GetIOS()->GetFS(); const auto fs = system.GetIOS()->GetFS();
{ {
Common::SettingsHandler::Buffer data; Common::SettingsBuffer data;
const auto file = fs->OpenFile(IOS::SYSMENU_UID, IOS::SYSMENU_GID, settings_file_path, const auto file = fs->OpenFile(IOS::SYSMENU_UID, IOS::SYSMENU_GID, settings_file_path,
IOS::HLE::FS::Mode::Read); IOS::HLE::FS::Mode::Read);
if (file && file->Read(data.data(), data.size())) if (file && file->Read(data.data(), data.size()))
{ {
Common::SettingsHandler settings_reader(data); const Common::SettingsReader settings_reader(data);
serno = settings_reader.GetValue("SERNO"); serno = settings_reader.GetValue("SERNO");
model = settings_reader.GetValue("MODEL"); model = settings_reader.GetValue("MODEL");
@ -413,7 +413,7 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
if (Core::WantsDeterminism()) if (Core::WantsDeterminism())
serno = "123456789"; serno = "123456789";
else else
serno = Common::SettingsHandler::GenerateSerialNumber(); serno = Common::SettingsWriter::GenerateSerialNumber();
INFO_LOG_FMT(BOOT, "No previous serial number found, generated one instead: {}", serno); INFO_LOG_FMT(BOOT, "No previous serial number found, generated one instead: {}", serno);
} }
else else
@ -421,20 +421,21 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
INFO_LOG_FMT(BOOT, "Using serial number: {}", serno); INFO_LOG_FMT(BOOT, "Using serial number: {}", serno);
} }
Common::SettingsHandler gen; Common::SettingsWriter settings_writer;
gen.AddSetting("AREA", region_setting.area); settings_writer.AddSetting("AREA", region_setting.area);
gen.AddSetting("MODEL", model); settings_writer.AddSetting("MODEL", model);
gen.AddSetting("DVD", "0"); settings_writer.AddSetting("DVD", "0");
gen.AddSetting("MPCH", "0x7FFE"); settings_writer.AddSetting("MPCH", "0x7FFE");
gen.AddSetting("CODE", region_setting.code); settings_writer.AddSetting("CODE", region_setting.code);
gen.AddSetting("SERNO", serno); settings_writer.AddSetting("SERNO", serno);
gen.AddSetting("VIDEO", region_setting.video); settings_writer.AddSetting("VIDEO", region_setting.video);
gen.AddSetting("GAME", region_setting.game); settings_writer.AddSetting("GAME", region_setting.game);
constexpr IOS::HLE::FS::Mode rw_mode = IOS::HLE::FS::Mode::ReadWrite; constexpr IOS::HLE::FS::Mode rw_mode = IOS::HLE::FS::Mode::ReadWrite;
const auto settings_file = fs->CreateAndOpenFile(IOS::SYSMENU_UID, IOS::SYSMENU_GID, const auto settings_file = fs->CreateAndOpenFile(IOS::SYSMENU_UID, IOS::SYSMENU_GID,
settings_file_path, {rw_mode, rw_mode, rw_mode}); settings_file_path, {rw_mode, rw_mode, rw_mode});
if (!settings_file || !settings_file->Write(gen.GetBytes().data(), gen.GetBytes().size())) if (!settings_file ||
!settings_file->Write(settings_writer.GetBytes().data(), settings_writer.GetBytes().size()))
{ {
PanicAlertFmtT("SetupWiiMemory: Can't create setting.txt file"); PanicAlertFmtT("SetupWiiMemory: Can't create setting.txt file");
return false; return false;
@ -443,7 +444,7 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
auto& memory = system.GetMemory(); auto& memory = system.GetMemory();
// Write the 256 byte setting.txt to memory. // Write the 256 byte setting.txt to memory.
memory.CopyToEmu(0x3800, gen.GetBytes().data(), gen.GetBytes().size()); memory.CopyToEmu(0x3800, settings_writer.GetBytes().data(), settings_writer.GetBytes().size());
INFO_LOG_FMT(BOOT, "Setup Wii Memory..."); INFO_LOG_FMT(BOOT, "Setup Wii Memory...");

View file

@ -178,7 +178,7 @@ const Info<int> GFX_STEREO_DEPTH_PERCENTAGE{{System::GFX, "Stereoscopy", "Stereo
// Graphics.Hacks // Graphics.Hacks
const Info<bool> GFX_HACK_EFB_ACCESS_ENABLE{{System::GFX, "Hacks", "EFBAccessEnable"}, true}; const Info<bool> GFX_HACK_EFB_ACCESS_ENABLE{{System::GFX, "Hacks", "EFBAccessEnable"}, false};
const Info<bool> GFX_HACK_EFB_DEFER_INVALIDATION{ const Info<bool> GFX_HACK_EFB_DEFER_INVALIDATION{
{System::GFX, "Hacks", "EFBAccessDeferInvalidation"}, false}; {System::GFX, "Hacks", "EFBAccessDeferInvalidation"}, false};
const Info<int> GFX_HACK_EFB_ACCESS_TILE_SIZE{{System::GFX, "Hacks", "EFBAccessTileSize"}, 64}; const Info<int> GFX_HACK_EFB_ACCESS_TILE_SIZE{{System::GFX, "Hacks", "EFBAccessTileSize"}, 64};

View file

@ -212,7 +212,7 @@ const Info<bool> MAIN_RAM_OVERRIDE_ENABLE{{System::Main, "Core", "RAMOverrideEna
const Info<u32> MAIN_MEM1_SIZE{{System::Main, "Core", "MEM1Size"}, Memory::MEM1_SIZE_RETAIL}; const Info<u32> MAIN_MEM1_SIZE{{System::Main, "Core", "MEM1Size"}, Memory::MEM1_SIZE_RETAIL};
const Info<u32> MAIN_MEM2_SIZE{{System::Main, "Core", "MEM2Size"}, Memory::MEM2_SIZE_RETAIL}; const Info<u32> MAIN_MEM2_SIZE{{System::Main, "Core", "MEM2Size"}, Memory::MEM2_SIZE_RETAIL};
const Info<std::string> MAIN_GFX_BACKEND{{System::Main, "Core", "GFXBackend"}, const Info<std::string> MAIN_GFX_BACKEND{{System::Main, "Core", "GFXBackend"},
VideoBackendBase::GetDefaultBackendName()}; VideoBackendBase::GetDefaultBackendConfigName()};
const Info<HSP::HSPDeviceType> MAIN_HSP_DEVICE{{System::Main, "Core", "HSPDevice"}, const Info<HSP::HSPDeviceType> MAIN_HSP_DEVICE{{System::Main, "Core", "HSPDevice"},
HSP::HSPDeviceType::None}; HSP::HSPDeviceType::None};
const Info<u32> MAIN_ARAM_EXPANSION_SIZE{{System::Main, "Core", "ARAMExpansionSize"}, 0x400000}; const Info<u32> MAIN_ARAM_EXPANSION_SIZE{{System::Main, "Core", "ARAMExpansionSize"}, 0x400000};

View file

@ -6,19 +6,14 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include "Common/Config/Config.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/UISettings.h"
#include "Core/Config/WiimoteSettings.h" #include "Core/Config/WiimoteSettings.h"
namespace ConfigLoaders namespace ConfigLoaders
{ {
bool IsSettingSaveable(const Config::Location& config_location) bool IsSettingSaveable(const Config::Location& config_location)
{ {
static constexpr std::array<Config::System, 3> systems_not_saveable = { static constexpr std::array systems_not_saveable = {Config::System::GCPad, Config::System::WiiPad,
Config::System::GCPad, Config::System::WiiPad, Config::System::GCKeyboard}; Config::System::GCKeyboard};
if (std::find(begin(systems_not_saveable), end(systems_not_saveable), config_location.system) == if (std::find(begin(systems_not_saveable), end(systems_not_saveable), config_location.system) ==
end(systems_not_saveable)) end(systems_not_saveable))

View file

@ -217,7 +217,7 @@ void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard& guard)
} }
CBoot::LoadMapFromFilename(guard, ppc_symbol_db); CBoot::LoadMapFromFilename(guard, ppc_symbol_db);
HLE::Reload(system); HLE::Reload(system);
PatchEngine::Reload(); PatchEngine::Reload(system);
HiresTexture::Update(); HiresTexture::Update();
WC24PatchEngine::Reload(); WC24PatchEngine::Reload();
} }

View file

@ -17,6 +17,7 @@
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/Host.h"
#include "Core/PowerPC/MMU.h" #include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
#include "ConfigManager.h" #include "ConfigManager.h"
@ -272,6 +273,7 @@ static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
{ {
ppc_state.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j); ppc_state.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j);
} }
Host_JitCacheInvalidation();
return Installation::Installed; return Installation::Installed;
} }

View file

@ -60,7 +60,7 @@ void Host_PPCSymbolsChanged();
void Host_RefreshDSPDebuggerWindow(); void Host_RefreshDSPDebuggerWindow();
void Host_RequestRenderWindowSize(int width, int height); void Host_RequestRenderWindowSize(int width, int height);
void Host_UpdateDisasmDialog(); void Host_UpdateDisasmDialog();
void Host_JitCacheCleared(); void Host_JitCacheInvalidation();
void Host_JitProfileDataWiped(); void Host_JitProfileDataWiped();
void Host_UpdateMainFrame(); void Host_UpdateMainFrame();
void Host_UpdateTitle(const std::string& title); void Host_UpdateTitle(const std::string& title);

View file

@ -133,13 +133,13 @@ IPCReply GetRealProductCode(Core::System& system, const IOCtlVRequest& request)
if (!file) if (!file)
return IPCReply(IPC_ENOENT); return IPCReply(IPC_ENOENT);
Common::SettingsHandler::Buffer data; Common::SettingsBuffer data;
if (!file.ReadBytes(data.data(), data.size())) if (!file.ReadBytes(data.data(), data.size()))
return IPCReply(IPC_ENOENT); return IPCReply(IPC_ENOENT);
Common::SettingsHandler gen(data); const Common::SettingsReader settings_reader(data);
const std::string code = gen.GetValue("CODE"); const std::string code = settings_reader.GetValue("CODE");
const size_t length = std::min<size_t>(request.io_vectors[0].size, code.length()); const size_t length = std::min<size_t>(request.io_vectors[0].size, code.length());
if (length == 0) if (length == 0)

View file

@ -10,6 +10,7 @@
#include <map> #include <map>
#include <optional> #include <optional>
#include <string> #include <string>
#include <string_view>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -575,7 +576,7 @@ SharedContentMap::GetFilenameFromSHA1(const std::array<u8, 20>& sha1) const
if (it == m_entries.end()) if (it == m_entries.end())
return {}; return {};
const std::string id_string(it->id.begin(), it->id.end()); const std::string_view id_string(reinterpret_cast<const char*>(it->id.data()), it->id.size());
return fmt::format("/shared1/{}.app", id_string); return fmt::format("/shared1/{}.app", id_string);
} }
@ -591,20 +592,22 @@ std::vector<std::array<u8, 20>> SharedContentMap::GetHashes() const
std::string SharedContentMap::AddSharedContent(const std::array<u8, 20>& sha1) std::string SharedContentMap::AddSharedContent(const std::array<u8, 20>& sha1)
{ {
auto filename = GetFilenameFromSHA1(sha1); if (auto filename = GetFilenameFromSHA1(sha1))
if (filename) return *std::move(filename);
return *filename;
const std::string id = fmt::format("{:08x}", m_last_id); Entry& entry = m_entries.emplace_back();
Entry entry; static_assert(sizeof(m_last_id) == 4,
std::copy(id.cbegin(), id.cend(), entry.id.begin()); "'m_last_id' must be represented by 8 characters when formatted in hexadecimal.");
static_assert(std::tuple_size_v<decltype(entry.id)> == sizeof(m_last_id) * 2,
"'entry.id' must be a std::array capable of storing every nibble of 'm_last_id'.");
fmt::format_to(entry.id.data(), "{:08x}", m_last_id);
entry.sha1 = sha1; entry.sha1 = sha1;
m_entries.push_back(entry);
WriteEntries(); WriteEntries();
filename = fmt::format("/shared1/{}.app", id);
m_last_id++; m_last_id++;
return *filename;
const std::string_view id_string(reinterpret_cast<const char*>(entry.id.data()), entry.id.size());
return fmt::format("/shared1/{}.app", id_string);
} }
bool SharedContentMap::DeleteSharedContent(const std::array<u8, 20>& sha1) bool SharedContentMap::DeleteSharedContent(const std::array<u8, 20>& sha1)

View file

@ -923,7 +923,7 @@ IPCReply NetKDRequestDevice::HandleRequestRegisterUserId(const IOS::HLE::IOCtlRe
return IPCReply{IPC_SUCCESS}; return IPCReply{IPC_SUCCESS};
} }
Common::SettingsHandler::Buffer data; Common::SettingsBuffer data;
if (!file->Read(data.data(), data.size())) if (!file->Read(data.data(), data.size()))
{ {
WriteReturnValue(memory, NWC24::WC24_ERR_FILE_READ, request.buffer_out); WriteReturnValue(memory, NWC24::WC24_ERR_FILE_READ, request.buffer_out);
@ -931,8 +931,8 @@ IPCReply NetKDRequestDevice::HandleRequestRegisterUserId(const IOS::HLE::IOCtlRe
return IPCReply{IPC_SUCCESS}; return IPCReply{IPC_SUCCESS};
} }
const Common::SettingsHandler gen{data}; const Common::SettingsReader settings_reader{data};
const std::string serno = gen.GetValue("SERNO"); const std::string serno = settings_reader.GetValue("SERNO");
const std::string form_data = const std::string form_data =
fmt::format("mlid=w{}&hdid={}&rgncd={}", m_config.Id(), m_ios.GetIOSC().GetDeviceId(), serno); fmt::format("mlid=w{}&hdid={}&rgncd={}", m_config.Id(), m_ios.GetIOSC().GetDeviceId(), serno);
const Common::HttpRequest::Response response = m_http.Post(m_config.GetAccountURL(), form_data); const Common::HttpRequest::Response response = m_http.Post(m_config.GetAccountURL(), form_data);
@ -1076,12 +1076,12 @@ std::optional<IPCReply> NetKDRequestDevice::IOCtl(const IOCtlRequest& request)
const auto fs = m_ios.GetFS(); const auto fs = m_ios.GetFS();
if (const auto file = fs->OpenFile(PID_KD, PID_KD, settings_file_path, FS::Mode::Read)) if (const auto file = fs->OpenFile(PID_KD, PID_KD, settings_file_path, FS::Mode::Read))
{ {
Common::SettingsHandler::Buffer data; Common::SettingsBuffer data;
if (file->Read(data.data(), data.size())) if (file->Read(data.data(), data.size()))
{ {
const Common::SettingsHandler gen{data}; const Common::SettingsReader settings_reader{data};
area = gen.GetValue("AREA"); area = settings_reader.GetValue("AREA");
model = gen.GetValue("MODEL"); model = settings_reader.GetValue("MODEL");
} }
} }

View file

@ -296,6 +296,13 @@ void RemoveMemoryPatch(std::size_t index)
std::erase(s_on_frame_memory, index); std::erase(s_on_frame_memory, index);
} }
static void ApplyStartupPatches(Core::System& system)
{
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard(system);
ApplyPatches(guard, s_on_frame);
}
bool ApplyFramePatches(Core::System& system) bool ApplyFramePatches(Core::System& system)
{ {
const auto& ppc_state = system.GetPPCState(); const auto& ppc_state = system.GetPPCState();
@ -335,10 +342,11 @@ void Shutdown()
Gecko::Shutdown(); Gecko::Shutdown();
} }
void Reload() void Reload(Core::System& system)
{ {
Shutdown(); Shutdown();
LoadPatches(); LoadPatches();
ApplyStartupPatches(system);
} }
} // namespace PatchEngine } // namespace PatchEngine

View file

@ -61,7 +61,7 @@ void RemoveMemoryPatch(std::size_t index);
bool ApplyFramePatches(Core::System& system); bool ApplyFramePatches(Core::System& system);
void Shutdown(); void Shutdown();
void Reload(); void Reload(Core::System& system);
inline int GetPatchTypeCharLength(PatchType type) inline int GetPatchTypeCharLength(PatchType type)
{ {

View file

@ -442,7 +442,7 @@ void CachedInterpreter::ClearCache()
ClearCodeSpace(); ClearCodeSpace();
ResetFreeMemoryRanges(); ResetFreeMemoryRanges();
RefreshConfig(); RefreshConfig();
Host_JitCacheCleared(); Host_JitCacheInvalidation();
} }
void CachedInterpreter::LogGeneratedCode() const void CachedInterpreter::LogGeneratedCode() const

View file

@ -313,7 +313,7 @@ void Jit64::ClearCache()
RefreshConfig(); RefreshConfig();
asm_routines.Regenerate(); asm_routines.Regenerate();
ResetFreeMemoryRanges(); ResetFreeMemoryRanges();
Host_JitCacheCleared(); Host_JitCacheInvalidation();
} }
void Jit64::FreeRanges() void Jit64::FreeRanges()

View file

@ -197,7 +197,7 @@ void JitArm64::GenerateAsmAndResetFreeMemoryRanges()
ResetFreeMemoryRanges(routines_near_end - routines_near_start, ResetFreeMemoryRanges(routines_near_end - routines_near_start,
routines_far_end - routines_far_start); routines_far_end - routines_far_start);
Host_JitCacheCleared(); Host_JitCacheInvalidation();
} }
void JitArm64::FreeRanges() void JitArm64::FreeRanges()
@ -262,12 +262,11 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
if (js.op->canEndBlock) if (js.op->canEndBlock)
{ {
// also flush the program counter // also flush the program counter
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
MOVI2R(WA, js.compilerPC); MOVI2R(WA, js.compilerPC);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(pc)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(pc));
ADD(WA, WA, 4); ADD(WA, WA, 4);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(npc)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(npc));
gpr.Unlock(WA);
} }
Interpreter::Instruction instr = Interpreter::GetInterpreterOp(inst); Interpreter::Instruction instr = Interpreter::GetInterpreterOp(inst);
@ -283,24 +282,23 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
{ {
if (js.isLastInstruction) if (js.isLastInstruction)
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(npc)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(npc));
WriteExceptionExit(WA); WriteExceptionExit(WA);
gpr.Unlock(WA);
} }
else else
{ {
// only exit if ppcstate.npc was changed // only exit if ppcstate.npc was changed
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(npc)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(npc));
ARM64Reg WB = gpr.GetReg(); {
MOVI2R(WB, js.compilerPC + 4); auto WB = gpr.GetScopedReg();
CMP(WB, WA); MOVI2R(WB, js.compilerPC + 4);
gpr.Unlock(WB); CMP(WB, WA);
}
FixupBranch c = B(CC_EQ); FixupBranch c = B(CC_EQ);
WriteExceptionExit(WA); WriteExceptionExit(WA);
SetJumpTarget(c); SetJumpTarget(c);
gpr.Unlock(WA);
} }
} }
else if (ShouldHandleFPExceptionForInstruction(js.op)) else if (ShouldHandleFPExceptionForInstruction(js.op))
@ -399,11 +397,12 @@ void JitArm64::IntializeSpeculativeConstants()
SwitchToNearCode(); SwitchToNearCode();
} }
ARM64Reg tmp = gpr.GetReg(); {
ARM64Reg value = gpr.R(i); auto tmp = gpr.GetScopedReg();
MOVI2R(tmp, compile_time_value); ARM64Reg value = gpr.R(i);
CMP(value, tmp); MOVI2R(tmp, compile_time_value);
gpr.Unlock(tmp); CMP(value, tmp);
}
FixupBranch no_fail = B(CCFlags::CC_EQ); FixupBranch no_fail = B(CCFlags::CC_EQ);
B(fail); B(fail);
@ -442,16 +441,15 @@ void JitArm64::MSRUpdated(u32 msr)
} }
else else
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
MOVI2R(WA, feature_flags); MOVI2R(WA, feature_flags);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(feature_flags)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(feature_flags));
gpr.Unlock(WA);
} }
} }
void JitArm64::MSRUpdated(ARM64Reg msr) void JitArm64::MSRUpdated(ARM64Reg msr)
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
// Update mem_ptr // Update mem_ptr
@ -472,8 +470,6 @@ void JitArm64::MSRUpdated(ARM64Reg msr)
if (other_feature_flags != 0) if (other_feature_flags != 0)
ORR(WA, WA, LogicalImm(other_feature_flags, GPRSize::B32)); ORR(WA, WA, LogicalImm(other_feature_flags, GPRSize::B32));
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(feature_flags)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(feature_flags));
gpr.Unlock(WA);
} }
void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return, void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return,
@ -671,35 +667,37 @@ void JitArm64::FakeLKExit(u32 exit_address_after_return, ARM64Reg exit_address_a
// function has been called! // function has been called!
gpr.Lock(ARM64Reg::W30); gpr.Lock(ARM64Reg::W30);
} }
// Push {ARM_PC (64-bit); PPC_PC (32-bit); feature_flags (32-bit)} on the stack
ARM64Reg after_reg = ARM64Reg::INVALID_REG; const u8* host_address_after_return;
ARM64Reg reg_to_push;
const u64 feature_flags = m_ppc_state.feature_flags;
if (exit_address_after_return_reg == ARM64Reg::INVALID_REG)
{ {
after_reg = gpr.GetReg(); // Push {ARM_PC (64-bit); PPC_PC (32-bit); feature_flags (32-bit)} on the stack
reg_to_push = EncodeRegTo64(after_reg); Arm64RegCache::ScopedARM64Reg after_reg;
MOVI2R(reg_to_push, feature_flags << 32 | exit_address_after_return); ARM64Reg reg_to_push;
const u64 feature_flags = m_ppc_state.feature_flags;
if (exit_address_after_return_reg == ARM64Reg::INVALID_REG)
{
after_reg = gpr.GetScopedReg();
reg_to_push = EncodeRegTo64(after_reg);
MOVI2R(reg_to_push, feature_flags << 32 | exit_address_after_return);
}
else if (feature_flags == 0)
{
reg_to_push = EncodeRegTo64(exit_address_after_return_reg);
}
else
{
after_reg = gpr.GetScopedReg();
reg_to_push = EncodeRegTo64(after_reg);
ORRI2R(reg_to_push, EncodeRegTo64(exit_address_after_return_reg), feature_flags << 32,
reg_to_push);
}
auto code_reg = gpr.GetScopedReg();
constexpr s32 adr_offset = sizeof(u32) * 3;
host_address_after_return = GetCodePtr() + adr_offset;
ADR(EncodeRegTo64(code_reg), adr_offset);
STP(IndexType::Pre, EncodeRegTo64(code_reg), reg_to_push, ARM64Reg::SP, -16);
} }
else if (feature_flags == 0)
{
reg_to_push = EncodeRegTo64(exit_address_after_return_reg);
}
else
{
after_reg = gpr.GetReg();
reg_to_push = EncodeRegTo64(after_reg);
ORRI2R(reg_to_push, EncodeRegTo64(exit_address_after_return_reg), feature_flags << 32,
reg_to_push);
}
ARM64Reg code_reg = gpr.GetReg();
constexpr s32 adr_offset = sizeof(u32) * 3;
const u8* host_address_after_return = GetCodePtr() + adr_offset;
ADR(EncodeRegTo64(code_reg), adr_offset);
STP(IndexType::Pre, EncodeRegTo64(code_reg), reg_to_push, ARM64Reg::SP, -16);
gpr.Unlock(code_reg);
if (after_reg != ARM64Reg::INVALID_REG)
gpr.Unlock(after_reg);
FixupBranch skip_exit = BL(); FixupBranch skip_exit = BL();
DEBUG_ASSERT(GetCodePtr() == host_address_after_return || HasWriteFailed()); DEBUG_ASSERT(GetCodePtr() == host_address_after_return || HasWriteFailed());
@ -832,10 +830,9 @@ void JitArm64::WriteExceptionExit(ARM64Reg dest, bool only_external, bool always
void JitArm64::WriteConditionalExceptionExit(int exception, u64 increment_sp_on_exit) void JitArm64::WriteConditionalExceptionExit(int exception, u64 increment_sp_on_exit)
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
WriteConditionalExceptionExit(exception, WA, Arm64Gen::ARM64Reg::INVALID_REG, WriteConditionalExceptionExit(exception, WA, Arm64Gen::ARM64Reg::INVALID_REG,
increment_sp_on_exit); increment_sp_on_exit);
gpr.Unlock(WA);
} }
void JitArm64::WriteConditionalExceptionExit(int exception, ARM64Reg temp_gpr, ARM64Reg temp_fpr, void JitArm64::WriteConditionalExceptionExit(int exception, ARM64Reg temp_gpr, ARM64Reg temp_fpr,
@ -1227,7 +1224,7 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
// asynchronous. // asynchronous.
if (jo.optimizeGatherPipe && gatherPipeIntCheck) if (jo.optimizeGatherPipe && gatherPipeIntCheck)
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
@ -1253,8 +1250,6 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
SwitchToNearCode(); SwitchToNearCode();
SetJumpTarget(no_ext_exception); SetJumpTarget(no_ext_exception);
SetJumpTarget(exit); SetJumpTarget(exit);
gpr.Unlock(WA);
} }
} }
@ -1268,12 +1263,11 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
// The only thing that currently sets op.skip is the BLR following optimization. // The only thing that currently sets op.skip is the BLR following optimization.
// If any non-branch instruction starts setting that too, this will need to be changed. // If any non-branch instruction starts setting that too, this will need to be changed.
ASSERT(op.inst.hex == 0x4e800020); ASSERT(op.inst.hex == 0x4e800020);
const ARM64Reg bw_reg_a = gpr.GetReg(), bw_reg_b = gpr.GetReg(); const auto bw_reg_a = gpr.GetScopedReg(), bw_reg_b = gpr.GetScopedReg();
const BitSet32 gpr_caller_save = const BitSet32 gpr_caller_save =
gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(bw_reg_a), DecodeReg(bw_reg_b)}; gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(bw_reg_a), DecodeReg(bw_reg_b)};
WriteBranchWatch<true>(op.address, op.branchTo, op.inst, bw_reg_a, bw_reg_b, WriteBranchWatch<true>(op.address, op.branchTo, op.inst, bw_reg_a, bw_reg_b,
gpr_caller_save, fpr.GetCallerSavedUsed()); gpr_caller_save, fpr.GetCallerSavedUsed());
gpr.Unlock(bw_reg_a, bw_reg_b);
} }
} }
else else
@ -1311,23 +1305,24 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
if ((opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound) if ((opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound)
{ {
FixupBranch b1;
// This instruction uses FPU - needs to add FP exception bailout // This instruction uses FPU - needs to add FP exception bailout
ARM64Reg WA = gpr.GetReg(); {
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(msr)); auto WA = gpr.GetScopedReg();
FixupBranch b1 = TBNZ(WA, 13); // Test FP enabled bit LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(msr));
b1 = TBNZ(WA, 13); // Test FP enabled bit
FixupBranch far_addr = B(); FixupBranch far_addr = B();
SwitchToFarCode(); SwitchToFarCode();
SetJumpTarget(far_addr); SetJumpTarget(far_addr);
gpr.Flush(FlushMode::MaintainState, WA); gpr.Flush(FlushMode::MaintainState, WA);
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG); fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
ORR(WA, WA, LogicalImm(EXCEPTION_FPU_UNAVAILABLE, GPRSize::B32)); ORR(WA, WA, LogicalImm(EXCEPTION_FPU_UNAVAILABLE, GPRSize::B32));
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
}
gpr.Unlock(WA);
WriteExceptionExit(js.compilerPC, false, true); WriteExceptionExit(js.compilerPC, false, true);

View file

@ -24,13 +24,12 @@ void JitArm64::sc(UGeckoInstruction inst)
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
ARM64Reg WA = gpr.GetReg(); {
auto WA = gpr.GetScopedReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
ORR(WA, WA, LogicalImm(EXCEPTION_SYSCALL, GPRSize::B32)); ORR(WA, WA, LogicalImm(EXCEPTION_SYSCALL, GPRSize::B32));
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
}
gpr.Unlock(WA);
WriteExceptionExit(js.compilerPC + 4, false, true); WriteExceptionExit(js.compilerPC + 4, false, true);
} }
@ -51,28 +50,28 @@ void JitArm64::rfi(UGeckoInstruction inst)
// R1 = MSR contents // R1 = MSR contents
// R2 = Mask // R2 = Mask
// R3 = Mask // R3 = Mask
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WB = gpr.GetReg(); {
ARM64Reg WC = gpr.GetReg(); auto WB = gpr.GetScopedReg();
auto WC = gpr.GetScopedReg();
LDR(IndexType::Unsigned, WC, PPC_REG, PPCSTATE_OFF(msr)); LDR(IndexType::Unsigned, WC, PPC_REG, PPCSTATE_OFF(msr));
ANDI2R(WC, WC, (~mask) & clearMSR13, WA); // rD = Masked MSR ANDI2R(WC, WC, (~mask) & clearMSR13, WA); // rD = Masked MSR
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_SRR1)); // rB contains SRR1 here LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_SRR1)); // rB contains SRR1 here
ANDI2R(WA, WA, mask & clearMSR13, WB); // rB contains masked SRR1 here ANDI2R(WA, WA, mask & clearMSR13, WB); // rB contains masked SRR1 here
ORR(WA, WA, WC); // rB = Masked MSR OR masked SRR1 ORR(WA, WA, WC); // rB = Masked MSR OR masked SRR1
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(msr)); // STR rB in to rA STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(msr)); // STR rB in to rA
gpr.Unlock(WB, WC); }
MSRUpdated(WA); MSRUpdated(WA);
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_SRR0)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_SRR0));
WriteExceptionExit(WA); WriteExceptionExit(WA);
gpr.Unlock(WA);
} }
template <bool condition> template <bool condition>
@ -144,10 +143,10 @@ void JitArm64::bx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITBranchOff); JITDISABLE(bJITBranchOff);
ARM64Reg WA = ARM64Reg::INVALID_REG; Arm64GPRCache::ScopedARM64Reg WA = ARM64Reg::INVALID_REG;
if (inst.LK) if (inst.LK)
{ {
WA = gpr.GetReg(); WA = gpr.GetScopedReg();
MOVI2R(WA, js.compilerPC + 4); MOVI2R(WA, js.compilerPC + 4);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
} }
@ -156,13 +155,12 @@ void JitArm64::bx(UGeckoInstruction inst)
{ {
if (IsDebuggingEnabled()) if (IsDebuggingEnabled())
{ {
const ARM64Reg WB = gpr.GetReg(), WC = gpr.GetReg(); const auto WB = gpr.GetScopedReg(), WC = gpr.GetScopedReg();
BitSet32 gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WB), DecodeReg(WC)}; BitSet32 gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WB), DecodeReg(WC)};
if (WA != ARM64Reg::INVALID_REG && js.op->skipLRStack) if (WA != ARM64Reg::INVALID_REG && js.op->skipLRStack)
gpr_caller_save[DecodeReg(WA)] = false; gpr_caller_save[DecodeReg(WA)] = false;
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WB, WC, gpr_caller_save, WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WB, WC, gpr_caller_save,
fpr.GetCallerSavedUsed()); fpr.GetCallerSavedUsed());
gpr.Unlock(WB, WC);
} }
if (inst.LK && !js.op->skipLRStack) if (inst.LK && !js.op->skipLRStack)
{ {
@ -172,9 +170,6 @@ void JitArm64::bx(UGeckoInstruction inst)
FakeLKExit(js.compilerPC + 4, WA); FakeLKExit(js.compilerPC + 4, WA);
} }
if (WA != ARM64Reg::INVALID_REG)
gpr.Unlock(WA);
return; return;
} }
@ -184,13 +179,12 @@ void JitArm64::bx(UGeckoInstruction inst)
if (js.op->branchIsIdleLoop) if (js.op->branchIsIdleLoop)
{ {
if (WA == ARM64Reg::INVALID_REG) if (WA == ARM64Reg::INVALID_REG)
WA = gpr.GetReg(); WA = gpr.GetScopedReg();
if (IsDebuggingEnabled()) if (IsDebuggingEnabled())
{ {
const ARM64Reg WB = gpr.GetReg(); const auto WB = gpr.GetScopedReg();
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WA, WB, {}, {}); WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WA, WB, {}, {});
gpr.Unlock(WB);
} }
// make idle loops go faster // make idle loops go faster
@ -198,7 +192,7 @@ void JitArm64::bx(UGeckoInstruction inst)
MOVP2R(XA, &CoreTiming::GlobalIdle); MOVP2R(XA, &CoreTiming::GlobalIdle);
BLR(XA); BLR(XA);
gpr.Unlock(WA); WA.Unlock();
WriteExceptionExit(js.op->branchTo); WriteExceptionExit(js.op->branchTo);
return; return;
@ -206,16 +200,12 @@ void JitArm64::bx(UGeckoInstruction inst)
if (IsDebuggingEnabled()) if (IsDebuggingEnabled())
{ {
const ARM64Reg WB = gpr.GetReg(), WC = gpr.GetReg(); const auto WB = gpr.GetScopedReg(), WC = gpr.GetScopedReg();
const BitSet32 gpr_caller_save = const BitSet32 gpr_caller_save =
WA != ARM64Reg::INVALID_REG ? BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS : BitSet32{}; WA != ARM64Reg::INVALID_REG ? BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS : BitSet32{};
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WB, WC, gpr_caller_save, {}); WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WB, WC, gpr_caller_save, {});
gpr.Unlock(WB, WC);
} }
WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4, WA); WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4, WA);
if (WA != ARM64Reg::INVALID_REG)
gpr.Unlock(WA);
} }
void JitArm64::bcx(UGeckoInstruction inst) void JitArm64::bcx(UGeckoInstruction inst)
@ -223,77 +213,79 @@ void JitArm64::bcx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITBranchOff); JITDISABLE(bJITBranchOff);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WB = inst.LK || IsDebuggingEnabled() ? gpr.GetReg() : WA; auto WB = inst.LK || IsDebuggingEnabled() ? gpr.GetScopedReg() :
ARM64Reg WC = IsDebuggingEnabled() && inst.LK && !js.op->branchIsIdleLoop ? gpr.GetReg() : Arm64GPRCache::ScopedARM64Reg(WA.GetReg());
ARM64Reg::INVALID_REG;
FixupBranch pCTRDontBranch;
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{ {
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR)); auto WC = IsDebuggingEnabled() && inst.LK && !js.op->branchIsIdleLoop ?
SUBS(WA, WA, 1); gpr.GetScopedReg() :
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR)); Arm64GPRCache::ScopedARM64Reg(ARM64Reg::INVALID_REG);
if (inst.BO & BO_BRANCH_IF_CTR_0) FixupBranch pCTRDontBranch;
pCTRDontBranch = B(CC_NEQ); if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
SUBS(WA, WA, 1);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
if (inst.BO & BO_BRANCH_IF_CTR_0)
pCTRDontBranch = B(CC_NEQ);
else
pCTRDontBranch = B(CC_EQ);
}
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{
pConditionDontBranch =
JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), !(inst.BO_2 & BO_BRANCH_IF_TRUE));
}
if (inst.LK)
{
MOVI2R(WA, js.compilerPC + 4);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
}
gpr.Flush(FlushMode::MaintainState, WB);
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);
if (IsDebuggingEnabled())
{
ARM64Reg bw_reg_a, bw_reg_b;
// WC is only allocated when WA is needed for WriteExit and cannot be clobbered.
if (WC == ARM64Reg::INVALID_REG)
bw_reg_a = WA, bw_reg_b = WB;
else
bw_reg_a = WB, bw_reg_b = WC;
const BitSet32 gpr_caller_save =
gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(bw_reg_a), DecodeReg(bw_reg_b)};
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, bw_reg_a, bw_reg_b,
gpr_caller_save, fpr.GetCallerSavedUsed());
}
if (js.op->branchIsIdleLoop)
{
// make idle loops go faster
ARM64Reg XA = EncodeRegTo64(WA);
MOVP2R(XA, &CoreTiming::GlobalIdle);
BLR(XA);
WriteExceptionExit(js.op->branchTo);
}
else else
pCTRDontBranch = B(CC_EQ); {
WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4, WA);
}
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget(pConditionDontBranch);
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget(pCTRDontBranch);
} }
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{
pConditionDontBranch =
JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), !(inst.BO_2 & BO_BRANCH_IF_TRUE));
}
if (inst.LK)
{
MOVI2R(WA, js.compilerPC + 4);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
}
gpr.Flush(FlushMode::MaintainState, WB);
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);
if (IsDebuggingEnabled())
{
ARM64Reg bw_reg_a, bw_reg_b;
// WC is only allocated when WA is needed for WriteExit and cannot be clobbered.
if (WC == ARM64Reg::INVALID_REG)
bw_reg_a = WA, bw_reg_b = WB;
else
bw_reg_a = WB, bw_reg_b = WC;
const BitSet32 gpr_caller_save =
gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(bw_reg_a), DecodeReg(bw_reg_b)};
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, bw_reg_a, bw_reg_b,
gpr_caller_save, fpr.GetCallerSavedUsed());
}
if (js.op->branchIsIdleLoop)
{
// make idle loops go faster
ARM64Reg XA = EncodeRegTo64(WA);
MOVP2R(XA, &CoreTiming::GlobalIdle);
BLR(XA);
WriteExceptionExit(js.op->branchTo);
}
else
{
WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4, WA);
}
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget(pConditionDontBranch);
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget(pCTRDontBranch);
if (WC != ARM64Reg::INVALID_REG)
gpr.Unlock(WC);
if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE)) if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE))
{ {
gpr.Flush(FlushMode::All, WA); gpr.Flush(FlushMode::All, WA);
@ -311,10 +303,6 @@ void JitArm64::bcx(UGeckoInstruction inst)
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, gpr_caller_save, WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, gpr_caller_save,
fpr.GetCallerSavedUsed()); fpr.GetCallerSavedUsed());
} }
gpr.Unlock(WA);
if (WB != WA)
gpr.Unlock(WB);
} }
void JitArm64::bcctrx(UGeckoInstruction inst) void JitArm64::bcctrx(UGeckoInstruction inst)
@ -337,34 +325,29 @@ void JitArm64::bcctrx(UGeckoInstruction inst)
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
ARM64Reg WB = ARM64Reg::INVALID_REG; Arm64GPRCache::ScopedARM64Reg WB = ARM64Reg::INVALID_REG;
if (inst.LK_3) if (inst.LK_3)
{ {
WB = gpr.GetReg(); WB = gpr.GetScopedReg();
MOVI2R(WB, js.compilerPC + 4); MOVI2R(WB, js.compilerPC + 4);
STR(IndexType::Unsigned, WB, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR)); STR(IndexType::Unsigned, WB, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
} }
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
AND(WA, WA, LogicalImm(~0x3, GPRSize::B32)); AND(WA, WA, LogicalImm(~0x3, GPRSize::B32));
if (IsDebuggingEnabled()) if (IsDebuggingEnabled())
{ {
const ARM64Reg WC = gpr.GetReg(), WD = gpr.GetReg(); const auto WC = gpr.GetScopedReg(), WD = gpr.GetScopedReg();
BitSet32 gpr_caller_save = BitSet32{DecodeReg(WA)}; BitSet32 gpr_caller_save = BitSet32{DecodeReg(WA)};
if (WB != ARM64Reg::INVALID_REG) if (WB != ARM64Reg::INVALID_REG)
gpr_caller_save[DecodeReg(WB)] = true; gpr_caller_save[DecodeReg(WB)] = true;
gpr_caller_save &= CALLER_SAVED_GPRS; gpr_caller_save &= CALLER_SAVED_GPRS;
WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, WC, WD, gpr_caller_save, {}); WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, WC, WD, gpr_caller_save, {});
gpr.Unlock(WC, WD);
} }
WriteExit(WA, inst.LK_3, js.compilerPC + 4, WB); WriteExit(WA, inst.LK_3, js.compilerPC + 4, WB);
if (WB != ARM64Reg::INVALID_REG)
gpr.Unlock(WB);
gpr.Unlock(WA);
} }
void JitArm64::bclrx(UGeckoInstruction inst) void JitArm64::bclrx(UGeckoInstruction inst)
@ -375,85 +358,92 @@ void JitArm64::bclrx(UGeckoInstruction inst)
bool conditional = bool conditional =
(inst.BO & BO_DONT_DECREMENT_FLAG) == 0 || (inst.BO & BO_DONT_CHECK_CONDITION) == 0; (inst.BO & BO_DONT_DECREMENT_FLAG) == 0 || (inst.BO & BO_DONT_CHECK_CONDITION) == 0;
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WB = Arm64GPRCache::ScopedARM64Reg WB;
conditional || inst.LK || IsDebuggingEnabled() ? gpr.GetReg() : ARM64Reg::INVALID_REG; if (conditional || inst.LK || IsDebuggingEnabled())
ARM64Reg WC = IsDebuggingEnabled() ? gpr.GetReg() : ARM64Reg::INVALID_REG;
FixupBranch pCTRDontBranch;
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{ {
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR)); WB = gpr.GetScopedReg();
SUBS(WA, WA, 1);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
if (inst.BO & BO_BRANCH_IF_CTR_0)
pCTRDontBranch = B(CC_NEQ);
else
pCTRDontBranch = B(CC_EQ);
} }
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{ {
pConditionDontBranch = Arm64GPRCache::ScopedARM64Reg WC;
JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), !(inst.BO_2 & BO_BRANCH_IF_TRUE)); if (IsDebuggingEnabled())
}
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
AND(WA, WA, LogicalImm(~0x3, GPRSize::B32));
if (inst.LK)
{
MOVI2R(WB, js.compilerPC + 4);
STR(IndexType::Unsigned, WB, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
}
gpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, WB);
fpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, ARM64Reg::INVALID_REG);
if (IsDebuggingEnabled())
{
BitSet32 gpr_caller_save;
BitSet32 fpr_caller_save;
if (conditional)
{ {
gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WB), DecodeReg(WC)}; WC = gpr.GetScopedReg();
if (js.op->branchIsIdleLoop) }
gpr_caller_save[DecodeReg(WA)] = false;
fpr_caller_save = fpr.GetCallerSavedUsed(); FixupBranch pCTRDontBranch;
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
SUBS(WA, WA, 1);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
if (inst.BO & BO_BRANCH_IF_CTR_0)
pCTRDontBranch = B(CC_NEQ);
else
pCTRDontBranch = B(CC_EQ);
}
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{
pConditionDontBranch =
JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), !(inst.BO_2 & BO_BRANCH_IF_TRUE));
}
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
AND(WA, WA, LogicalImm(~0x3, GPRSize::B32));
if (inst.LK)
{
MOVI2R(WB, js.compilerPC + 4);
STR(IndexType::Unsigned, WB, PPC_REG, PPCSTATE_OFF_SPR(SPR_LR));
}
gpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, WB);
fpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, ARM64Reg::INVALID_REG);
if (IsDebuggingEnabled())
{
BitSet32 gpr_caller_save;
BitSet32 fpr_caller_save;
if (conditional)
{
gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WB), DecodeReg(WC)};
if (js.op->branchIsIdleLoop)
gpr_caller_save[DecodeReg(WA)] = false;
fpr_caller_save = fpr.GetCallerSavedUsed();
}
else
{
gpr_caller_save =
js.op->branchIsIdleLoop ? BitSet32{} : BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS;
fpr_caller_save = {};
}
WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, WB, WC, gpr_caller_save,
fpr_caller_save);
}
if (js.op->branchIsIdleLoop)
{
// make idle loops go faster
ARM64Reg XA = EncodeRegTo64(WA);
MOVP2R(XA, &CoreTiming::GlobalIdle);
BLR(XA);
WriteExceptionExit(js.op->branchTo);
} }
else else
{ {
gpr_caller_save = WriteBLRExit(WA);
js.op->branchIsIdleLoop ? BitSet32{} : BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS;
fpr_caller_save = {};
} }
WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, WB, WC, gpr_caller_save,
fpr_caller_save); if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget(pConditionDontBranch);
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget(pCTRDontBranch);
} }
if (js.op->branchIsIdleLoop)
{
// make idle loops go faster
ARM64Reg XA = EncodeRegTo64(WA);
MOVP2R(XA, &CoreTiming::GlobalIdle);
BLR(XA);
WriteExceptionExit(js.op->branchTo);
}
else
{
WriteBLRExit(WA);
}
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget(pConditionDontBranch);
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget(pCTRDontBranch);
if (WC != ARM64Reg::INVALID_REG)
gpr.Unlock(WC);
if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE)) if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE))
{ {
@ -472,8 +462,4 @@ void JitArm64::bclrx(UGeckoInstruction inst)
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, gpr_caller_save, WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, gpr_caller_save,
fpr.GetCallerSavedUsed()); fpr.GetCallerSavedUsed());
} }
gpr.Unlock(WA);
if (WB != ARM64Reg::INVALID_REG)
gpr.Unlock(WB);
} }

View file

@ -102,154 +102,151 @@ void JitArm64::fp_arith(UGeckoInstruction inst)
const ARM64Reg VC = use_c ? reg_encoder(fpr.R(c, type)) : ARM64Reg::INVALID_REG; const ARM64Reg VC = use_c ? reg_encoder(fpr.R(c, type)) : ARM64Reg::INVALID_REG;
const ARM64Reg VD = reg_encoder(fpr.RW(d, type_out)); const ARM64Reg VD = reg_encoder(fpr.RW(d, type_out));
ARM64Reg V0Q = ARM64Reg::INVALID_REG;
ARM64Reg V1Q = ARM64Reg::INVALID_REG;
ARM64Reg rounded_c_reg = VC;
if (round_c)
{ {
ASSERT_MSG(DYNA_REC, !inputs_are_singles, "Tried to apply 25-bit precision to single"); Arm64FPRCache::ScopedARM64Reg V0Q = ARM64Reg::INVALID_REG;
Arm64FPRCache::ScopedARM64Reg V1Q = ARM64Reg::INVALID_REG;
V0Q = fpr.GetReg(); ARM64Reg rounded_c_reg = VC;
rounded_c_reg = reg_encoder(V0Q); if (round_c)
Force25BitPrecision(rounded_c_reg, VC);
}
ARM64Reg inaccurate_fma_reg = VD;
if (fma && inaccurate_fma && VD == VB)
{
if (V0Q == ARM64Reg::INVALID_REG)
V0Q = fpr.GetReg();
inaccurate_fma_reg = reg_encoder(V0Q);
}
ARM64Reg result_reg = VD;
const bool preserve_d =
m_accurate_nans && (VD == VA || (use_b && VD == VB) || (use_c && VD == VC));
if (preserve_d)
{
V1Q = fpr.GetReg();
result_reg = reg_encoder(V1Q);
}
switch (op5)
{
case 18:
m_float_emit.FDIV(result_reg, VA, VB);
break;
case 20:
m_float_emit.FSUB(result_reg, VA, VB);
break;
case 21:
m_float_emit.FADD(result_reg, VA, VB);
break;
case 25:
m_float_emit.FMUL(result_reg, VA, rounded_c_reg);
break;
// While it may seem like PowerPC's nmadd/nmsub map to AArch64's nmadd/msub [sic],
// the subtly different definitions affect how signed zeroes are handled.
// Also, PowerPC's nmadd/nmsub perform rounding before the final negation.
// So, we negate using a separate FNEG instruction instead of using AArch64's nmadd/msub.
case 28: // fmsub: "D = A*C - B" vs "Vd = (-Va) + Vn*Vm"
case 30: // fnmsub: "D = -(A*C - B)" vs "Vd = -((-Va) + Vn*Vm)"
if (inaccurate_fma)
{ {
m_float_emit.FMUL(inaccurate_fma_reg, VA, rounded_c_reg); ASSERT_MSG(DYNA_REC, !inputs_are_singles, "Tried to apply 25-bit precision to single");
m_float_emit.FSUB(result_reg, inaccurate_fma_reg, VB);
}
else
{
m_float_emit.FNMSUB(result_reg, VA, rounded_c_reg, VB);
}
break;
case 29: // fmadd: "D = A*C + B" vs "Vd = Va + Vn*Vm"
case 31: // fnmadd: "D = -(A*C + B)" vs "Vd = -(Va + Vn*Vm)"
if (inaccurate_fma)
{
m_float_emit.FMUL(inaccurate_fma_reg, VA, rounded_c_reg);
m_float_emit.FADD(result_reg, inaccurate_fma_reg, VB);
}
else
{
m_float_emit.FMADD(result_reg, VA, rounded_c_reg, VB);
}
break;
default:
ASSERT_MSG(DYNA_REC, 0, "fp_arith");
break;
}
Common::SmallVector<FixupBranch, 4> nan_fixups; V0Q = fpr.GetScopedReg();
if (m_accurate_nans) rounded_c_reg = reg_encoder(V0Q);
{ Force25BitPrecision(rounded_c_reg, VC);
// Check if we need to handle NaNs
m_float_emit.FCMP(result_reg);
FixupBranch no_nan = B(CCFlags::CC_VC);
FixupBranch nan = B();
SetJumpTarget(no_nan);
SwitchToFarCode();
SetJumpTarget(nan);
Common::SmallVector<ARM64Reg, 3> inputs;
inputs.push_back(VA);
if (use_b && VA != VB)
inputs.push_back(VB);
if (use_c && VA != VC && (!use_b || VB != VC))
inputs.push_back(VC);
// If any inputs are NaNs, pick the first NaN of them and set its quiet bit.
// However, we can skip checking the last input, because if exactly one input is NaN, AArch64
// arithmetic instructions automatically pick that NaN and make it quiet, just like we want.
for (size_t i = 0; i < inputs.size() - 1; ++i)
{
const ARM64Reg input = inputs[i];
m_float_emit.FCMP(input);
FixupBranch skip = B(CCFlags::CC_VC);
// Make the NaN quiet
m_float_emit.FADD(VD, input, input);
nan_fixups.push_back(B());
SetJumpTarget(skip);
} }
std::optional<FixupBranch> nan_early_fixup; ARM64Reg inaccurate_fma_reg = VD;
if (fma && inaccurate_fma && VD == VB)
{
if (V0Q == ARM64Reg::INVALID_REG)
V0Q = fpr.GetScopedReg();
inaccurate_fma_reg = reg_encoder(V0Q);
}
ARM64Reg result_reg = VD;
const bool preserve_d =
m_accurate_nans && (VD == VA || (use_b && VD == VB) || (use_c && VD == VC));
if (preserve_d)
{
V1Q = fpr.GetScopedReg();
result_reg = reg_encoder(V1Q);
}
switch (op5)
{
case 18:
m_float_emit.FDIV(result_reg, VA, VB);
break;
case 20:
m_float_emit.FSUB(result_reg, VA, VB);
break;
case 21:
m_float_emit.FADD(result_reg, VA, VB);
break;
case 25:
m_float_emit.FMUL(result_reg, VA, rounded_c_reg);
break;
// While it may seem like PowerPC's nmadd/nmsub map to AArch64's nmadd/msub [sic],
// the subtly different definitions affect how signed zeroes are handled.
// Also, PowerPC's nmadd/nmsub perform rounding before the final negation.
// So, we negate using a separate FNEG instruction instead of using AArch64's nmadd/msub.
case 28: // fmsub: "D = A*C - B" vs "Vd = (-Va) + Vn*Vm"
case 30: // fnmsub: "D = -(A*C - B)" vs "Vd = -((-Va) + Vn*Vm)"
if (inaccurate_fma)
{
m_float_emit.FMUL(inaccurate_fma_reg, VA, rounded_c_reg);
m_float_emit.FSUB(result_reg, inaccurate_fma_reg, VB);
}
else
{
m_float_emit.FNMSUB(result_reg, VA, rounded_c_reg, VB);
}
break;
case 29: // fmadd: "D = A*C + B" vs "Vd = Va + Vn*Vm"
case 31: // fnmadd: "D = -(A*C + B)" vs "Vd = -(Va + Vn*Vm)"
if (inaccurate_fma)
{
m_float_emit.FMUL(inaccurate_fma_reg, VA, rounded_c_reg);
m_float_emit.FADD(result_reg, inaccurate_fma_reg, VB);
}
else
{
m_float_emit.FMADD(result_reg, VA, rounded_c_reg, VB);
}
break;
default:
ASSERT_MSG(DYNA_REC, 0, "fp_arith");
break;
}
Common::SmallVector<FixupBranch, 4> nan_fixups;
if (m_accurate_nans)
{
// Check if we need to handle NaNs
m_float_emit.FCMP(result_reg);
FixupBranch no_nan = B(CCFlags::CC_VC);
FixupBranch nan = B();
SetJumpTarget(no_nan);
SwitchToFarCode();
SetJumpTarget(nan);
Common::SmallVector<ARM64Reg, 3> inputs;
inputs.push_back(VA);
if (use_b && VA != VB)
inputs.push_back(VB);
if (use_c && VA != VC && (!use_b || VB != VC))
inputs.push_back(VC);
// If any inputs are NaNs, pick the first NaN of them and set its quiet bit.
// However, we can skip checking the last input, because if exactly one input is NaN, AArch64
// arithmetic instructions automatically pick that NaN and make it quiet, just like we want.
for (size_t i = 0; i < inputs.size() - 1; ++i)
{
const ARM64Reg input = inputs[i];
m_float_emit.FCMP(input);
FixupBranch skip = B(CCFlags::CC_VC);
// Make the NaN quiet
m_float_emit.FADD(VD, input, input);
nan_fixups.push_back(B());
SetJumpTarget(skip);
}
std::optional<FixupBranch> nan_early_fixup;
if (negate_result)
{
// If we have a NaN, we must not execute FNEG.
if (result_reg != VD)
m_float_emit.MOV(EncodeRegToDouble(VD), EncodeRegToDouble(result_reg));
nan_fixups.push_back(B());
}
else
{
nan_early_fixup = B();
}
SwitchToNearCode();
if (nan_early_fixup)
SetJumpTarget(*nan_early_fixup);
}
// PowerPC's nmadd/nmsub perform rounding before the final negation, which is not the case
// for any of AArch64's FMA instructions, so we negate using a separate instruction.
if (negate_result) if (negate_result)
{ m_float_emit.FNEG(VD, result_reg);
// If we have a NaN, we must not execute FNEG. else if (result_reg != VD)
if (result_reg != VD) m_float_emit.MOV(EncodeRegToDouble(VD), EncodeRegToDouble(result_reg));
m_float_emit.MOV(EncodeRegToDouble(VD), EncodeRegToDouble(result_reg));
nan_fixups.push_back(B());
}
else
{
nan_early_fixup = B();
}
SwitchToNearCode(); for (FixupBranch fixup : nan_fixups)
SetJumpTarget(fixup);
if (nan_early_fixup)
SetJumpTarget(*nan_early_fixup);
} }
// PowerPC's nmadd/nmsub perform rounding before the final negation, which is not the case
// for any of AArch64's FMA instructions, so we negate using a separate instruction.
if (negate_result)
m_float_emit.FNEG(VD, result_reg);
else if (result_reg != VD)
m_float_emit.MOV(EncodeRegToDouble(VD), EncodeRegToDouble(result_reg));
for (FixupBranch fixup : nan_fixups)
SetJumpTarget(fixup);
if (V0Q != ARM64Reg::INVALID_REG)
fpr.Unlock(V0Q);
if (V1Q != ARM64Reg::INVALID_REG)
fpr.Unlock(V1Q);
if (output_is_single) if (output_is_single)
{ {
ASSERT_MSG(DYNA_REC, inputs_are_singles == inputs_are_singles_func(), ASSERT_MSG(DYNA_REC, inputs_are_singles == inputs_are_singles_func(),
@ -449,43 +446,40 @@ void JitArm64::FloatCompare(UGeckoInstruction inst, bool upper)
gpr.BindCRToRegister(crf, false); gpr.BindCRToRegister(crf, false);
const ARM64Reg XA = gpr.CR(crf); const ARM64Reg XA = gpr.CR(crf);
ARM64Reg fpscr_reg = ARM64Reg::INVALID_REG; Arm64GPRCache::ScopedARM64Reg fpscr_reg = ARM64Reg::INVALID_REG;
if (fprf) if (fprf)
{ {
fpscr_reg = gpr.GetReg(); fpscr_reg = gpr.GetScopedReg();
LDR(IndexType::Unsigned, fpscr_reg, PPC_REG, PPCSTATE_OFF(fpscr)); LDR(IndexType::Unsigned, fpscr_reg, PPC_REG, PPCSTATE_OFF(fpscr));
AND(fpscr_reg, fpscr_reg, LogicalImm(~FPCC_MASK, GPRSize::B32)); AND(fpscr_reg, fpscr_reg, LogicalImm(~FPCC_MASK, GPRSize::B32));
} }
ARM64Reg V0Q = ARM64Reg::INVALID_REG;
ARM64Reg V1Q = ARM64Reg::INVALID_REG;
if (upper_a)
{ {
V0Q = fpr.GetReg(); Arm64FPRCache::ScopedARM64Reg V0Q;
m_float_emit.DUP(singles ? 32 : 64, paired_reg_encoder(V0Q), paired_reg_encoder(VA), 1); Arm64FPRCache::ScopedARM64Reg V1Q;
VA = reg_encoder(V0Q); if (upper_a)
}
if (upper_b)
{
if (a == b)
{ {
VB = VA; V0Q = fpr.GetScopedReg();
m_float_emit.DUP(singles ? 32 : 64, paired_reg_encoder(V0Q), paired_reg_encoder(VA), 1);
VA = reg_encoder(V0Q);
} }
else if (upper_b)
{ {
V1Q = fpr.GetReg(); if (a == b)
m_float_emit.DUP(singles ? 32 : 64, paired_reg_encoder(V1Q), paired_reg_encoder(VB), 1); {
VB = reg_encoder(V1Q); VB = VA;
}
else
{
V1Q = fpr.GetScopedReg();
m_float_emit.DUP(singles ? 32 : 64, paired_reg_encoder(V1Q), paired_reg_encoder(VB), 1);
VB = reg_encoder(V1Q);
}
} }
m_float_emit.FCMP(VA, VB);
} }
m_float_emit.FCMP(VA, VB);
if (V0Q != ARM64Reg::INVALID_REG)
fpr.Unlock(V0Q);
if (V1Q != ARM64Reg::INVALID_REG)
fpr.Unlock(V1Q);
FixupBranch pNaN, pLesser, pGreater; FixupBranch pNaN, pLesser, pGreater;
FixupBranch continue1, continue2, continue3; FixupBranch continue1, continue2, continue3;
@ -538,7 +532,6 @@ void JitArm64::FloatCompare(UGeckoInstruction inst, bool upper)
if (fprf) if (fprf)
{ {
STR(IndexType::Unsigned, fpscr_reg, PPC_REG, PPCSTATE_OFF(fpscr)); STR(IndexType::Unsigned, fpscr_reg, PPC_REG, PPCSTATE_OFF(fpscr));
gpr.Unlock(fpscr_reg);
} }
} }
@ -572,7 +565,7 @@ void JitArm64::fctiwx(UGeckoInstruction inst)
if (single) if (single)
{ {
const ARM64Reg V0 = fpr.GetReg(); const auto V0 = fpr.GetScopedReg();
if (is_fctiwzx) if (is_fctiwzx)
{ {
@ -589,12 +582,10 @@ void JitArm64::fctiwx(UGeckoInstruction inst)
m_float_emit.BIC(16, EncodeRegToDouble(V0), 0x7); m_float_emit.BIC(16, EncodeRegToDouble(V0), 0x7);
m_float_emit.ORR(EncodeRegToDouble(VD), EncodeRegToDouble(VD), EncodeRegToDouble(V0)); m_float_emit.ORR(EncodeRegToDouble(VD), EncodeRegToDouble(VD), EncodeRegToDouble(V0));
fpr.Unlock(V0);
} }
else else
{ {
const ARM64Reg WA = gpr.GetReg(); const auto WA = gpr.GetScopedReg();
if (is_fctiwzx) if (is_fctiwzx)
{ {
@ -608,8 +599,6 @@ void JitArm64::fctiwx(UGeckoInstruction inst)
ORR(EncodeRegTo64(WA), EncodeRegTo64(WA), LogicalImm(0xFFF8'0000'0000'0000ULL, GPRSize::B64)); ORR(EncodeRegTo64(WA), EncodeRegTo64(WA), LogicalImm(0xFFF8'0000'0000'0000ULL, GPRSize::B64));
m_float_emit.FMOV(EncodeRegToDouble(VD), EncodeRegTo64(WA)); m_float_emit.FMOV(EncodeRegToDouble(VD), EncodeRegTo64(WA));
gpr.Unlock(WA);
} }
ASSERT_MSG(DYNA_REC, b == d || single == fpr.IsSingle(b, true), ASSERT_MSG(DYNA_REC, b == d || single == fpr.IsSingle(b, true),

View file

@ -86,10 +86,9 @@ void JitArm64::LoadCarry()
{ {
case CarryFlag::InPPCState: case CarryFlag::InPPCState:
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
CMP(WA, 1); CMP(WA, 1);
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
@ -119,18 +118,16 @@ void JitArm64::FlushCarry()
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
CSET(WA, CC_CS); CSET(WA, CC_CS);
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::ConstantTrue: case CarryFlag::ConstantTrue:
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
MOVI2R(WA, 1); MOVI2R(WA, 1);
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::ConstantFalse: case CarryFlag::ConstantFalse:
@ -155,9 +152,10 @@ void JitArm64::reg_imm(u32 d, u32 a, u32 value, u32 (*do_op)(u32, u32),
else else
{ {
gpr.BindToRegister(d, d == a); gpr.BindToRegister(d, d == a);
ARM64Reg WA = gpr.GetReg(); {
(this->*op)(gpr.R(d), gpr.R(a), value, WA); auto WA = gpr.GetScopedReg();
gpr.Unlock(WA); (this->*op)(gpr.R(d), gpr.R(a), value, WA);
}
if (Rc) if (Rc)
ComputeRC0(gpr.R(d)); ComputeRC0(gpr.R(d));
@ -245,9 +243,8 @@ void JitArm64::addix(UGeckoInstruction inst)
{ {
gpr.BindToRegister(d, d == a); gpr.BindToRegister(d, d == a);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ADDI2R(gpr.R(d), gpr.R(a), imm, WA); ADDI2R(gpr.R(d), gpr.R(a), imm, WA);
gpr.Unlock(WA);
} }
} }
else else
@ -544,9 +541,10 @@ void JitArm64::addx(UGeckoInstruction inst)
int imm_value = gpr.GetImm(imm_reg); int imm_value = gpr.GetImm(imm_reg);
gpr.BindToRegister(d, d == in_reg); gpr.BindToRegister(d, d == in_reg);
ARM64Reg WA = gpr.GetReg(); {
ADDI2R(gpr.R(d), gpr.R(in_reg), imm_value, WA); auto WA = gpr.GetScopedReg();
gpr.Unlock(WA); ADDI2R(gpr.R(d), gpr.R(in_reg), imm_value, WA);
}
if (inst.Rc) if (inst.Rc)
ComputeRC0(gpr.R(d)); ComputeRC0(gpr.R(d));
} }
@ -722,9 +720,8 @@ void JitArm64::cmpi(UGeckoInstruction inst)
if (B != 0) if (B != 0)
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
SUBI2R(CR, CR, B, EncodeRegTo64(WA)); SUBI2R(CR, CR, B, EncodeRegTo64(WA));
gpr.Unlock(WA);
} }
} }
@ -796,10 +793,9 @@ void JitArm64::rlwinmx_internal(UGeckoInstruction inst, u32 sh)
} }
else else
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
MOVI2R(WA, mask); MOVI2R(WA, mask);
AND(gpr.R(a), WA, gpr.R(s), ArithOption(gpr.R(s), ShiftType::ROR, 32 - sh)); AND(gpr.R(a), WA, gpr.R(s), ArithOption(gpr.R(s), ShiftType::ROR, 32 - sh));
gpr.Unlock(WA);
} }
if (inst.Rc) if (inst.Rc)
@ -829,11 +825,12 @@ void JitArm64::rlwnmx(UGeckoInstruction inst)
const u32 mask = MakeRotationMask(inst.MB, inst.ME); const u32 mask = MakeRotationMask(inst.MB, inst.ME);
gpr.BindToRegister(a, a == s || a == b); gpr.BindToRegister(a, a == s || a == b);
ARM64Reg WA = gpr.GetReg(); {
NEG(WA, gpr.R(b)); auto WA = gpr.GetScopedReg();
RORV(gpr.R(a), gpr.R(s), WA); NEG(WA, gpr.R(b));
ANDI2R(gpr.R(a), gpr.R(a), mask, WA); RORV(gpr.R(a), gpr.R(s), WA);
gpr.Unlock(WA); ANDI2R(gpr.R(a), gpr.R(a), mask, WA);
}
if (inst.Rc) if (inst.Rc)
ComputeRC0(gpr.R(a)); ComputeRC0(gpr.R(a));
@ -878,8 +875,8 @@ void JitArm64::srawix(UGeckoInstruction inst)
if (js.op->wantsCA) if (js.op->wantsCA)
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg dest = inplace_carry ? WA : ARM64Reg::WSP; ARM64Reg dest = inplace_carry ? ARM64Reg(WA) : ARM64Reg::WSP;
if (a != s) if (a != s)
{ {
ASR(RA, RS, amount); ASR(RA, RS, amount);
@ -901,7 +898,6 @@ void JitArm64::srawix(UGeckoInstruction inst)
CSINC(WA, ARM64Reg::WSP, ARM64Reg::WSP, CC_EQ); CSINC(WA, ARM64Reg::WSP, ARM64Reg::WSP, CC_EQ);
ComputeCarry(WA); ComputeCarry(WA);
} }
gpr.Unlock(WA);
} }
else else
{ {
@ -936,9 +932,10 @@ void JitArm64::addic(UGeckoInstruction inst)
else else
{ {
gpr.BindToRegister(d, d == a); gpr.BindToRegister(d, d == a);
ARM64Reg WA = gpr.GetReg(); {
CARRY_IF_NEEDED(ADDI2R, ADDSI2R, gpr.R(d), gpr.R(a), simm, WA); auto WA = gpr.GetScopedReg();
gpr.Unlock(WA); CARRY_IF_NEEDED(ADDI2R, ADDSI2R, gpr.R(d), gpr.R(a), simm, WA);
}
ComputeCarry(); ComputeCarry();
if (rc) if (rc)
@ -1037,12 +1034,10 @@ void JitArm64::mulli(UGeckoInstruction inst)
gpr.BindToRegister(d, allocate_reg); gpr.BindToRegister(d, allocate_reg);
// Reuse d to hold the immediate if possible, allocate a register otherwise. // Reuse d to hold the immediate if possible, allocate a register otherwise.
ARM64Reg WA = allocate_reg ? gpr.GetReg() : gpr.R(d); auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(d));
MOVI2R(WA, (u32)(s32)inst.SIMM_16); MOVI2R(WA, (u32)(s32)inst.SIMM_16);
MUL(gpr.R(d), gpr.R(a), WA); MUL(gpr.R(d), gpr.R(a), WA);
if (allocate_reg)
gpr.Unlock(WA);
} }
} }
@ -1137,16 +1132,16 @@ void JitArm64::addzex(UGeckoInstruction inst)
{ {
case CarryFlag::InPPCState: case CarryFlag::InPPCState:
{ {
gpr.BindToRegister(d, d == a); const bool allocate_reg = d == a;
ARM64Reg WA = d == a ? gpr.GetReg() : gpr.R(d); gpr.BindToRegister(d, allocate_reg);
{
auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(d));
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), WA);
}
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), WA);
ComputeCarry(); ComputeCarry();
if (d == a)
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
@ -1229,18 +1224,16 @@ void JitArm64::subfex(UGeckoInstruction inst)
{ {
case CarryFlag::InPPCState: case CarryFlag::InPPCState:
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(gpr.R(d), WA, ~i + j, gpr.R(d)); ADDI2R(gpr.R(d), WA, ~i + j, gpr.R(d));
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
MOVI2R(WA, ~i + j); MOVI2R(WA, ~i + j);
ADC(gpr.R(d), WA, ARM64Reg::WZR); ADC(gpr.R(d), WA, ARM64Reg::WZR);
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::ConstantTrue: case CarryFlag::ConstantTrue:
@ -1274,23 +1267,30 @@ void JitArm64::subfex(UGeckoInstruction inst)
else else
{ {
gpr.BindToRegister(d, d == a || d == b); gpr.BindToRegister(d, d == a || d == b);
ARM64Reg RB = mex ? gpr.GetReg() : gpr.R(b); {
if (mex) Arm64GPRCache::ScopedARM64Reg RB;
MOVI2R(RB, -1); if (mex)
{
RB = gpr.GetScopedReg();
MOVI2R(RB, -1);
}
else
{
RB = gpr.R(b);
}
if (js.carryFlag == CarryFlag::ConstantTrue) if (js.carryFlag == CarryFlag::ConstantTrue)
{ {
CARRY_IF_NEEDED(SUB, SUBS, gpr.R(d), RB, gpr.R(a)); CARRY_IF_NEEDED(SUB, SUBS, gpr.R(d), RB, gpr.R(a));
} }
else else
{ {
LoadCarry(); LoadCarry();
CARRY_IF_NEEDED(SBC, SBCS, gpr.R(d), RB, gpr.R(a)); CARRY_IF_NEEDED(SBC, SBCS, gpr.R(d), RB, gpr.R(a));
}
} }
ComputeCarry(); ComputeCarry();
if (mex)
gpr.Unlock(RB);
} }
if (inst.Rc) if (inst.Rc)
@ -1343,12 +1343,13 @@ void JitArm64::subfzex(UGeckoInstruction inst)
{ {
case CarryFlag::InPPCState: case CarryFlag::InPPCState:
{ {
ARM64Reg WA = gpr.GetReg(); {
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); auto WA = gpr.GetScopedReg();
MVN(gpr.R(d), gpr.R(a)); LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(d), WA); MVN(gpr.R(d), gpr.R(a));
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(d), WA);
}
ComputeCarry(); ComputeCarry();
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
@ -1394,21 +1395,20 @@ void JitArm64::subfic(UGeckoInstruction inst)
{ {
const bool will_read = d == a; const bool will_read = d == a;
const bool is_zero = imm == 0; const bool is_zero = imm == 0;
const bool allocate_reg = will_read && !is_zero;
gpr.BindToRegister(d, will_read); gpr.BindToRegister(d, will_read);
// d = imm - a // d = imm - a
ARM64Reg RD = gpr.R(d); ARM64Reg RD = gpr.R(d);
ARM64Reg WA = ARM64Reg::WZR;
if (!is_zero)
{ {
WA = will_read ? gpr.GetReg() : RD; Arm64GPRCache::ScopedARM64Reg WA(ARM64Reg::WZR);
MOVI2R(WA, imm); if (!is_zero)
} {
CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a)); WA = will_read ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
MOVI2R(WA, imm);
}
if (allocate_reg) CARRY_IF_NEEDED(SUB, SUBS, RD, WA, gpr.R(a));
gpr.Unlock(WA); }
ComputeCarry(); ComputeCarry();
} }
@ -1433,10 +1433,9 @@ void JitArm64::addex(UGeckoInstruction inst)
{ {
case CarryFlag::InPPCState: case CarryFlag::InPPCState:
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(gpr.R(d), WA, i + j, gpr.R(d)); ADDI2R(gpr.R(d), WA, i + j, gpr.R(d));
gpr.Unlock(WA);
break; break;
} }
case CarryFlag::InHostCarry: case CarryFlag::InHostCarry:
@ -1477,23 +1476,30 @@ void JitArm64::addex(UGeckoInstruction inst)
else else
{ {
gpr.BindToRegister(d, d == a || d == b); gpr.BindToRegister(d, d == a || d == b);
ARM64Reg RB = mex ? gpr.GetReg() : gpr.R(b); {
if (mex) Arm64GPRCache::ScopedARM64Reg RB;
MOVI2R(RB, -1); if (mex)
{
RB = gpr.GetScopedReg();
MOVI2R(RB, -1);
}
else
{
RB = gpr.R(b);
}
if (js.carryFlag == CarryFlag::ConstantFalse) if (js.carryFlag == CarryFlag::ConstantFalse)
{ {
CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), RB); CARRY_IF_NEEDED(ADD, ADDS, gpr.R(d), gpr.R(a), RB);
} }
else else
{ {
LoadCarry(); LoadCarry();
CARRY_IF_NEEDED(ADC, ADCS, gpr.R(d), gpr.R(a), RB); CARRY_IF_NEEDED(ADC, ADCS, gpr.R(d), gpr.R(a), RB);
}
} }
ComputeCarry(); ComputeCarry();
if (mex)
gpr.Unlock(RB);
} }
if (inst.Rc) if (inst.Rc)
@ -1575,7 +1581,7 @@ void JitArm64::divwux(UGeckoInstruction inst)
{ {
UnsignedMagic m = UnsignedDivisionConstants(divisor); UnsignedMagic m = UnsignedDivisionConstants(divisor);
ARM64Reg WI = allocate_reg ? gpr.GetReg() : RD; auto WI = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
ARM64Reg XD = EncodeRegTo64(RD); ARM64Reg XD = EncodeRegTo64(RD);
MOVI2R(WI, m.multiplier); MOVI2R(WI, m.multiplier);
@ -1590,9 +1596,6 @@ void JitArm64::divwux(UGeckoInstruction inst)
} }
LSR(XD, XD, 32 + m.shift); LSR(XD, XD, 32 + m.shift);
if (allocate_reg)
gpr.Unlock(WI);
} }
if (inst.Rc) if (inst.Rc)
@ -1719,7 +1722,7 @@ void JitArm64::divwx(UGeckoInstruction inst)
ARM64Reg RA = gpr.R(a); ARM64Reg RA = gpr.R(a);
ARM64Reg RD = gpr.R(d); ARM64Reg RD = gpr.R(d);
ARM64Reg WA = allocate_reg ? gpr.GetReg() : RD; auto WA = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
TST(RA, RA); TST(RA, RA);
ADDI2R(WA, RA, abs_val - 1, WA); ADDI2R(WA, RA, abs_val - 1, WA);
@ -1729,9 +1732,6 @@ void JitArm64::divwx(UGeckoInstruction inst)
NEG(RD, WA, ArithOption(WA, ShiftType::ASR, MathUtil::IntLog2(abs_val))); NEG(RD, WA, ArithOption(WA, ShiftType::ASR, MathUtil::IntLog2(abs_val)));
else else
ASR(RD, WA, MathUtil::IntLog2(abs_val)); ASR(RD, WA, MathUtil::IntLog2(abs_val));
if (allocate_reg)
gpr.Unlock(WA);
} }
else else
{ {
@ -1739,8 +1739,8 @@ void JitArm64::divwx(UGeckoInstruction inst)
SignedMagic m = SignedDivisionConstants(divisor); SignedMagic m = SignedDivisionConstants(divisor);
ARM64Reg RD = gpr.R(d); ARM64Reg RD = gpr.R(d);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WB = allocate_reg ? gpr.GetReg() : RD; auto WB = allocate_reg ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RD);
ARM64Reg XD = EncodeRegTo64(RD); ARM64Reg XD = EncodeRegTo64(RD);
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
@ -1776,10 +1776,6 @@ void JitArm64::divwx(UGeckoInstruction inst)
ASR(XD, XD, 32 + m.shift); ASR(XD, XD, 32 + m.shift);
ADD(RD, WA, RD); ADD(RD, WA, RD);
} }
gpr.Unlock(WA);
if (allocate_reg)
gpr.Unlock(WB);
} }
if (inst.Rc) if (inst.Rc)
@ -1982,8 +1978,7 @@ void JitArm64::srawx(UGeckoInstruction inst)
else else
{ {
gpr.BindToRegister(a, a == s); gpr.BindToRegister(a, a == s);
auto WA = gpr.GetScopedReg();
ARM64Reg WA = gpr.GetReg();
if (a != s) if (a != s)
{ {
@ -2009,8 +2004,6 @@ void JitArm64::srawx(UGeckoInstruction inst)
CSET(WA, CC_NEQ); CSET(WA, CC_NEQ);
ComputeCarry(WA); ComputeCarry(WA);
gpr.Unlock(WA);
} }
} }
else else
@ -2018,8 +2011,8 @@ void JitArm64::srawx(UGeckoInstruction inst)
const bool will_read = a == b || a == s; const bool will_read = a == b || a == s;
gpr.BindToRegister(a, will_read); gpr.BindToRegister(a, will_read);
const bool allocate_reg = will_read || js.op->wantsCA; auto WA =
ARM64Reg WA = allocate_reg ? gpr.GetReg() : gpr.R(a); will_read || js.op->wantsCA ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(gpr.R(a));
LSL(EncodeRegTo64(WA), EncodeRegTo64(gpr.R(s)), 32); LSL(EncodeRegTo64(WA), EncodeRegTo64(gpr.R(s)), 32);
ASRV(EncodeRegTo64(WA), EncodeRegTo64(WA), EncodeRegTo64(gpr.R(b))); ASRV(EncodeRegTo64(WA), EncodeRegTo64(WA), EncodeRegTo64(gpr.R(b)));
@ -2031,9 +2024,6 @@ void JitArm64::srawx(UGeckoInstruction inst)
CSET(WA, CC_NEQ); CSET(WA, CC_NEQ);
ComputeCarry(WA); ComputeCarry(WA);
} }
if (allocate_reg)
gpr.Unlock(WA);
} }
if (inst.Rc) if (inst.Rc)
@ -2088,10 +2078,9 @@ void JitArm64::rlwimix(UGeckoInstruction inst)
// No rotation // No rotation
// No mask inversion // No mask inversion
gpr.BindToRegister(a, true); gpr.BindToRegister(a, true);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
UBFX(WA, gpr.R(s), lsb, width); UBFX(WA, gpr.R(s), lsb, width);
BFI(gpr.R(a), WA, lsb, width); BFI(gpr.R(a), WA, lsb, width);
gpr.Unlock(WA);
} }
else if (inst.SH && inst.MB <= inst.ME) else if (inst.SH && inst.MB <= inst.ME)
{ {
@ -2103,28 +2092,22 @@ void JitArm64::rlwimix(UGeckoInstruction inst)
} }
else else
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ROR(WA, gpr.R(s), (rot_dist + lsb) % 32); ROR(WA, gpr.R(s), (rot_dist + lsb) % 32);
BFI(gpr.R(a), WA, lsb, width); BFI(gpr.R(a), WA, lsb, width);
gpr.Unlock(WA);
} }
} }
else else
{ {
gpr.BindToRegister(a, true); gpr.BindToRegister(a, true);
const bool allocate_reg = a == s;
ARM64Reg RA = gpr.R(a); ARM64Reg RA = gpr.R(a);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WB = allocate_reg ? gpr.GetReg() : RA; auto WB = a == s ? gpr.GetScopedReg() : Arm64GPRCache::ScopedARM64Reg(RA);
MOVI2R(WA, mask); MOVI2R(WA, mask);
BIC(WB, RA, WA); BIC(WB, RA, WA);
AND(WA, WA, gpr.R(s), ArithOption(gpr.R(s), ShiftType::ROR, rot_dist)); AND(WA, WA, gpr.R(s), ArithOption(gpr.R(s), ShiftType::ROR, rot_dist));
ORR(RA, WB, WA); ORR(RA, WB, WA);
gpr.Unlock(WA);
if (allocate_reg)
gpr.Unlock(WB);
} }
if (inst.Rc) if (inst.Rc)

View file

@ -538,9 +538,12 @@ void JitArm64::lmw(UGeckoInstruction inst)
else else
ADDI2R(addr_reg, gpr.R(a), offset, addr_reg); ADDI2R(addr_reg, gpr.R(a), offset, addr_reg);
ARM64Reg addr_base_reg = a_is_addr_base_reg ? ARM64Reg::INVALID_REG : gpr.GetReg(); Arm64RegCache::ScopedARM64Reg addr_base_reg;
if (!a_is_addr_base_reg) if (!a_is_addr_base_reg)
{
addr_base_reg = gpr.GetScopedReg();
MOV(addr_base_reg, addr_reg); MOV(addr_base_reg, addr_reg);
}
BitSet32 gprs_to_discard{}; BitSet32 gprs_to_discard{};
if (!jo.memcheck) if (!jo.memcheck)
@ -628,8 +631,6 @@ void JitArm64::lmw(UGeckoInstruction inst)
gpr.Unlock(ARM64Reg::W1, ARM64Reg::W30); gpr.Unlock(ARM64Reg::W1, ARM64Reg::W30);
if (jo.memcheck || !jo.fastmem) if (jo.memcheck || !jo.fastmem)
gpr.Unlock(ARM64Reg::W0); gpr.Unlock(ARM64Reg::W0);
if (!a_is_addr_base_reg)
gpr.Unlock(addr_base_reg);
} }
void JitArm64::stmw(UGeckoInstruction inst) void JitArm64::stmw(UGeckoInstruction inst)
@ -655,9 +656,12 @@ void JitArm64::stmw(UGeckoInstruction inst)
else else
ADDI2R(addr_reg, gpr.R(a), offset, addr_reg); ADDI2R(addr_reg, gpr.R(a), offset, addr_reg);
ARM64Reg addr_base_reg = a_is_addr_base_reg ? ARM64Reg::INVALID_REG : gpr.GetReg(); Arm64GPRCache::ScopedARM64Reg addr_base_reg;
if (!a_is_addr_base_reg) if (!a_is_addr_base_reg)
{
addr_base_reg = gpr.GetScopedReg();
MOV(addr_base_reg, addr_reg); MOV(addr_base_reg, addr_reg);
}
BitSet32 gprs_to_discard{}; BitSet32 gprs_to_discard{};
if (!jo.memcheck) if (!jo.memcheck)
@ -748,8 +752,6 @@ void JitArm64::stmw(UGeckoInstruction inst)
gpr.Unlock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30); gpr.Unlock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30);
if (!jo.fastmem) if (!jo.fastmem)
gpr.Unlock(ARM64Reg::W0); gpr.Unlock(ARM64Reg::W0);
if (!a_is_addr_base_reg)
gpr.Unlock(addr_base_reg);
} }
void JitArm64::dcbx(UGeckoInstruction inst) void JitArm64::dcbx(UGeckoInstruction inst)
@ -786,8 +788,8 @@ void JitArm64::dcbx(UGeckoInstruction inst)
// bdnz afterwards! So if we invalidate a single cache line, we don't adjust the registers at // bdnz afterwards! So if we invalidate a single cache line, we don't adjust the registers at
// all, if we invalidate 2 cachelines we adjust the registers by one step, and so on. // all, if we invalidate 2 cachelines we adjust the registers by one step, and so on.
const ARM64Reg reg_cycle_count = gpr.GetReg(); const auto reg_cycle_count = gpr.GetScopedReg();
const ARM64Reg reg_downcount = gpr.GetReg(); const auto reg_downcount = gpr.GetScopedReg();
// Figure out how many loops we want to do. // Figure out how many loops we want to do.
const u8 cycle_count_per_loop = const u8 cycle_count_per_loop =
@ -855,12 +857,9 @@ void JitArm64::dcbx(UGeckoInstruction inst)
SetJumpTarget(branch_out); SetJumpTarget(branch_out);
SetJumpTarget(branch_over); SetJumpTarget(branch_over);
} }
gpr.Unlock(reg_cycle_count, reg_downcount);
} }
constexpr ARM64Reg effective_addr = WB; constexpr ARM64Reg effective_addr = WB;
const ARM64Reg physical_addr = gpr.GetReg();
if (a) if (a)
ADD(effective_addr, gpr.R(a), gpr.R(b)); ADD(effective_addr, gpr.R(a), gpr.R(b));
@ -874,6 +873,8 @@ void JitArm64::dcbx(UGeckoInstruction inst)
ADD(gpr.R(b), gpr.R(b), WA, ArithOption(WA, ShiftType::LSL, 5)); // Rb += (WA * 32) ADD(gpr.R(b), gpr.R(b), WA, ArithOption(WA, ShiftType::LSL, 5)); // Rb += (WA * 32)
} }
auto physical_addr = gpr.GetScopedReg();
// Translate effective address to physical address. // Translate effective address to physical address.
const u8* loop_start = GetCodePtr(); const u8* loop_start = GetCodePtr();
FixupBranch bat_lookup_failed; FixupBranch bat_lookup_failed;
@ -939,7 +940,7 @@ void JitArm64::dcbx(UGeckoInstruction inst)
SwitchToNearCode(); SwitchToNearCode();
SetJumpTarget(near_addr); SetJumpTarget(near_addr);
gpr.Unlock(WA, WB, physical_addr); gpr.Unlock(WA, WB);
if (make_loop) if (make_loop)
gpr.Unlock(loop_counter); gpr.Unlock(loop_counter);
} }

View file

@ -268,14 +268,14 @@ void JitArm64::stfXX(UGeckoInstruction inst)
const bool have_single = fpr.IsSingle(inst.FS, true); const bool have_single = fpr.IsSingle(inst.FS, true);
ARM64Reg V0 = Arm64FPRCache::ScopedARM64Reg V0 =
fpr.R(inst.FS, want_single && have_single ? RegType::LowerPairSingle : RegType::LowerPair); fpr.R(inst.FS, want_single && have_single ? RegType::LowerPairSingle : RegType::LowerPair);
if (want_single && !have_single) if (want_single && !have_single)
{ {
const ARM64Reg single_reg = fpr.GetReg(); auto single_reg = fpr.GetScopedReg();
ConvertDoubleToSingleLower(inst.FS, single_reg, V0); ConvertDoubleToSingleLower(inst.FS, single_reg, V0);
V0 = single_reg; V0 = std::move(single_reg);
} }
gpr.Lock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30); gpr.Lock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30);
@ -425,9 +425,6 @@ void JitArm64::stfXX(UGeckoInstruction inst)
MOV(gpr.R(a), addr_reg); MOV(gpr.R(a), addr_reg);
} }
if (want_single && !have_single)
fpr.Unlock(V0);
gpr.Unlock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30); gpr.Unlock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30);
fpr.Unlock(ARM64Reg::Q0); fpr.Unlock(ARM64Reg::Q0);
if (!jo.fastmem) if (!jo.fastmem)

View file

@ -173,20 +173,21 @@ void JitArm64::psq_stXX(UGeckoInstruction inst)
const bool have_single = fpr.IsSingle(inst.RS); const bool have_single = fpr.IsSingle(inst.RS);
ARM64Reg VS = fpr.R(inst.RS, have_single ? RegType::Single : RegType::Register); Arm64FPRCache::ScopedARM64Reg VS =
fpr.R(inst.RS, have_single ? RegType::Single : RegType::Register);
if (js.assumeNoPairedQuantize) if (js.assumeNoPairedQuantize)
{ {
if (!have_single) if (!have_single)
{ {
const ARM64Reg single_reg = fpr.GetReg(); auto single_reg = fpr.GetScopedReg();
if (w) if (w)
m_float_emit.FCVT(32, 64, EncodeRegToDouble(single_reg), EncodeRegToDouble(VS)); m_float_emit.FCVT(32, 64, EncodeRegToDouble(single_reg), EncodeRegToDouble(VS));
else else
m_float_emit.FCVTN(32, EncodeRegToDouble(single_reg), EncodeRegToDouble(VS)); m_float_emit.FCVTN(32, EncodeRegToDouble(single_reg), EncodeRegToDouble(VS));
VS = single_reg; VS = std::move(single_reg);
} }
} }
else else
@ -279,9 +280,6 @@ void JitArm64::psq_stXX(UGeckoInstruction inst)
MOV(gpr.R(inst.RA), addr_reg); MOV(gpr.R(inst.RA), addr_reg);
} }
if (js.assumeNoPairedQuantize && !have_single)
fpr.Unlock(VS);
gpr.Unlock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30); gpr.Unlock(ARM64Reg::W1, ARM64Reg::W2, ARM64Reg::W30);
fpr.Unlock(ARM64Reg::Q0); fpr.Unlock(ARM64Reg::Q0);
if (!js.assumeNoPairedQuantize || !jo.fastmem) if (!js.assumeNoPairedQuantize || !jo.fastmem)

View file

@ -108,201 +108,196 @@ void JitArm64::ps_arith(UGeckoInstruction inst)
const ARM64Reg VC = use_c ? reg_encoder(fpr.R(c, type)) : ARM64Reg::INVALID_REG; const ARM64Reg VC = use_c ? reg_encoder(fpr.R(c, type)) : ARM64Reg::INVALID_REG;
const ARM64Reg VD = reg_encoder(fpr.RW(d, type)); const ARM64Reg VD = reg_encoder(fpr.RW(d, type));
ARM64Reg V0Q = ARM64Reg::INVALID_REG;
ARM64Reg V1Q = ARM64Reg::INVALID_REG;
ARM64Reg V2Q = ARM64Reg::INVALID_REG;
ARM64Reg rounded_c_reg = VC;
if (round_c)
{ {
ASSERT_MSG(DYNA_REC, !singles, "Tried to apply 25-bit precision to single"); Arm64FPRCache::ScopedARM64Reg V0Q = ARM64Reg::INVALID_REG;
Arm64FPRCache::ScopedARM64Reg V1Q = ARM64Reg::INVALID_REG;
Arm64FPRCache::ScopedARM64Reg V2Q = ARM64Reg::INVALID_REG;
V0Q = fpr.GetReg(); ARM64Reg rounded_c_reg = VC;
rounded_c_reg = reg_encoder(V0Q); if (round_c)
Force25BitPrecision(rounded_c_reg, VC);
}
ARM64Reg inaccurate_fma_reg = VD;
if (fma && inaccurate_fma && VD == VB)
{
if (V0Q == ARM64Reg::INVALID_REG)
V0Q = fpr.GetReg();
inaccurate_fma_reg = reg_encoder(V0Q);
}
ARM64Reg result_reg = VD;
const bool need_accurate_fma_reg =
fma && !inaccurate_fma && (msub || VD != VB) && (VD == VA || VD == rounded_c_reg);
const bool preserve_d =
m_accurate_nans && (VD == VA || (use_b && VD == VB) || (use_c && VD == VC));
if (need_accurate_fma_reg || preserve_d)
{
V1Q = fpr.GetReg();
result_reg = reg_encoder(V1Q);
}
if (m_accurate_nans)
{
if (V0Q == ARM64Reg::INVALID_REG)
V0Q = fpr.GetReg();
if (duplicated_c || VD == result_reg)
V2Q = fpr.GetReg();
}
switch (op5)
{
case 12: // ps_muls0: d = a * c.ps0
m_float_emit.FMUL(size, result_reg, VA, rounded_c_reg, 0);
break;
case 13: // ps_muls1: d = a * c.ps1
m_float_emit.FMUL(size, result_reg, VA, rounded_c_reg, 1);
break;
case 14: // ps_madds0: d = a * c.ps0 + b
if (inaccurate_fma)
{ {
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg, 0); ASSERT_MSG(DYNA_REC, !singles, "Tried to apply 25-bit precision to single");
m_float_emit.FADD(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
if (result_reg != VB)
m_float_emit.MOV(result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg, 0);
}
break;
case 15: // ps_madds1: d = a * c.ps1 + b
if (inaccurate_fma)
{
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg, 1);
m_float_emit.FADD(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
if (result_reg != VB)
m_float_emit.MOV(result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg, 1);
}
break;
case 18: // ps_div
m_float_emit.FDIV(size, result_reg, VA, VB);
break;
case 20: // ps_sub
m_float_emit.FSUB(size, result_reg, VA, VB);
break;
case 21: // ps_add
m_float_emit.FADD(size, result_reg, VA, VB);
break;
case 25: // ps_mul
m_float_emit.FMUL(size, result_reg, VA, rounded_c_reg);
break;
case 28: // ps_msub: d = a * c - b
case 30: // ps_nmsub: d = -(a * c - b)
if (inaccurate_fma)
{
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg);
m_float_emit.FSUB(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
m_float_emit.FNEG(size, result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg);
}
break;
case 29: // ps_madd: d = a * c + b
case 31: // ps_nmadd: d = -(a * c + b)
if (inaccurate_fma)
{
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg);
m_float_emit.FADD(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
if (result_reg != VB)
m_float_emit.MOV(result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg);
}
break;
default:
ASSERT_MSG(DYNA_REC, 0, "ps_arith - invalid op");
break;
}
FixupBranch nan_fixup; V0Q = fpr.GetScopedReg();
if (m_accurate_nans) rounded_c_reg = reg_encoder(V0Q);
{ Force25BitPrecision(rounded_c_reg, VC);
const ARM64Reg nan_temp_reg = singles ? EncodeRegToSingle(V0Q) : EncodeRegToDouble(V0Q);
const ARM64Reg nan_temp_reg_paired = reg_encoder(V0Q);
// Check if we need to handle NaNs
m_float_emit.FMAXP(nan_temp_reg, result_reg);
m_float_emit.FCMP(nan_temp_reg);
FixupBranch no_nan = B(CCFlags::CC_VC);
FixupBranch nan = B();
SetJumpTarget(no_nan);
SwitchToFarCode();
SetJumpTarget(nan);
// Pick the right NaNs
const auto check_input = [&](ARM64Reg input) {
m_float_emit.FCMEQ(size, nan_temp_reg_paired, input, input);
m_float_emit.BIF(result_reg, input, nan_temp_reg_paired);
};
ARM64Reg c_reg_for_nan_purposes = VC;
if (duplicated_c)
{
c_reg_for_nan_purposes = reg_encoder(V2Q);
m_float_emit.DUP(size, c_reg_for_nan_purposes, VC, op5 & 0x1);
} }
if (use_c) ARM64Reg inaccurate_fma_reg = VD;
check_input(c_reg_for_nan_purposes); if (fma && inaccurate_fma && VD == VB)
{
if (V0Q == ARM64Reg::INVALID_REG)
V0Q = fpr.GetScopedReg();
inaccurate_fma_reg = reg_encoder(V0Q);
}
if (use_b && (!use_c || VB != c_reg_for_nan_purposes)) ARM64Reg result_reg = VD;
check_input(VB); const bool need_accurate_fma_reg =
fma && !inaccurate_fma && (msub || VD != VB) && (VD == VA || VD == rounded_c_reg);
const bool preserve_d =
m_accurate_nans && (VD == VA || (use_b && VD == VB) || (use_c && VD == VC));
if (need_accurate_fma_reg || preserve_d)
{
V1Q = fpr.GetScopedReg();
result_reg = reg_encoder(V1Q);
}
if ((!use_b || VA != VB) && (!use_c || VA != c_reg_for_nan_purposes)) if (m_accurate_nans)
check_input(VA); {
if (V0Q == ARM64Reg::INVALID_REG)
V0Q = fpr.GetScopedReg();
// Make the NaNs quiet if (duplicated_c || VD == result_reg)
V2Q = fpr.GetScopedReg();
}
const ARM64Reg quiet_nan_reg = VD == result_reg ? reg_encoder(V2Q) : VD; switch (op5)
{
case 12: // ps_muls0: d = a * c.ps0
m_float_emit.FMUL(size, result_reg, VA, rounded_c_reg, 0);
break;
case 13: // ps_muls1: d = a * c.ps1
m_float_emit.FMUL(size, result_reg, VA, rounded_c_reg, 1);
break;
case 14: // ps_madds0: d = a * c.ps0 + b
if (inaccurate_fma)
{
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg, 0);
m_float_emit.FADD(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
if (result_reg != VB)
m_float_emit.MOV(result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg, 0);
}
break;
case 15: // ps_madds1: d = a * c.ps1 + b
if (inaccurate_fma)
{
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg, 1);
m_float_emit.FADD(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
if (result_reg != VB)
m_float_emit.MOV(result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg, 1);
}
break;
case 18: // ps_div
m_float_emit.FDIV(size, result_reg, VA, VB);
break;
case 20: // ps_sub
m_float_emit.FSUB(size, result_reg, VA, VB);
break;
case 21: // ps_add
m_float_emit.FADD(size, result_reg, VA, VB);
break;
case 25: // ps_mul
m_float_emit.FMUL(size, result_reg, VA, rounded_c_reg);
break;
case 28: // ps_msub: d = a * c - b
case 30: // ps_nmsub: d = -(a * c - b)
if (inaccurate_fma)
{
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg);
m_float_emit.FSUB(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
m_float_emit.FNEG(size, result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg);
}
break;
case 29: // ps_madd: d = a * c + b
case 31: // ps_nmadd: d = -(a * c + b)
if (inaccurate_fma)
{
m_float_emit.FMUL(size, inaccurate_fma_reg, VA, rounded_c_reg);
m_float_emit.FADD(size, result_reg, inaccurate_fma_reg, VB);
}
else
{
if (result_reg != VB)
m_float_emit.MOV(result_reg, VB);
m_float_emit.FMLA(size, result_reg, VA, rounded_c_reg);
}
break;
default:
ASSERT_MSG(DYNA_REC, 0, "ps_arith - invalid op");
break;
}
m_float_emit.FADD(size, quiet_nan_reg, result_reg, result_reg); FixupBranch nan_fixup;
m_float_emit.FCMEQ(size, nan_temp_reg_paired, result_reg, result_reg); if (m_accurate_nans)
{
const ARM64Reg nan_temp_reg = singles ? EncodeRegToSingle(V0Q) : EncodeRegToDouble(V0Q);
const ARM64Reg nan_temp_reg_paired = reg_encoder(V0Q);
// Check if we need to handle NaNs
m_float_emit.FMAXP(nan_temp_reg, result_reg);
m_float_emit.FCMP(nan_temp_reg);
FixupBranch no_nan = B(CCFlags::CC_VC);
FixupBranch nan = B();
SetJumpTarget(no_nan);
SwitchToFarCode();
SetJumpTarget(nan);
// Pick the right NaNs
const auto check_input = [&](ARM64Reg input) {
m_float_emit.FCMEQ(size, nan_temp_reg_paired, input, input);
m_float_emit.BIF(result_reg, input, nan_temp_reg_paired);
};
ARM64Reg c_reg_for_nan_purposes = VC;
if (duplicated_c)
{
c_reg_for_nan_purposes = reg_encoder(V2Q);
m_float_emit.DUP(size, c_reg_for_nan_purposes, VC, op5 & 0x1);
}
if (use_c)
check_input(c_reg_for_nan_purposes);
if (use_b && (!use_c || VB != c_reg_for_nan_purposes))
check_input(VB);
if ((!use_b || VA != VB) && (!use_c || VA != c_reg_for_nan_purposes))
check_input(VA);
// Make the NaNs quiet
const ARM64Reg quiet_nan_reg = VD == result_reg ? reg_encoder(V2Q) : VD;
m_float_emit.FADD(size, quiet_nan_reg, result_reg, result_reg);
m_float_emit.FCMEQ(size, nan_temp_reg_paired, result_reg, result_reg);
if (negate_result)
m_float_emit.FNEG(size, result_reg, result_reg);
if (VD == result_reg)
m_float_emit.BIF(VD, quiet_nan_reg, nan_temp_reg_paired);
else // quiet_nan_reg == VD
m_float_emit.BIT(VD, result_reg, nan_temp_reg_paired);
nan_fixup = B();
SwitchToNearCode();
}
// PowerPC's nmadd/nmsub perform rounding before the final negation, which is not the case
// for any of AArch64's FMA instructions, so we negate using a separate instruction.
if (negate_result) if (negate_result)
m_float_emit.FNEG(size, result_reg, result_reg); m_float_emit.FNEG(size, VD, result_reg);
if (VD == result_reg) else if (result_reg != VD)
m_float_emit.BIF(VD, quiet_nan_reg, nan_temp_reg_paired); m_float_emit.MOV(VD, result_reg);
else // quiet_nan_reg == VD
m_float_emit.BIT(VD, result_reg, nan_temp_reg_paired);
nan_fixup = B(); if (m_accurate_nans)
SetJumpTarget(nan_fixup);
SwitchToNearCode();
} }
// PowerPC's nmadd/nmsub perform rounding before the final negation, which is not the case
// for any of AArch64's FMA instructions, so we negate using a separate instruction.
if (negate_result)
m_float_emit.FNEG(size, VD, result_reg);
else if (result_reg != VD)
m_float_emit.MOV(VD, result_reg);
if (m_accurate_nans)
SetJumpTarget(nan_fixup);
if (V0Q != ARM64Reg::INVALID_REG)
fpr.Unlock(V0Q);
if (V1Q != ARM64Reg::INVALID_REG)
fpr.Unlock(V1Q);
if (V2Q != ARM64Reg::INVALID_REG)
fpr.Unlock(V2Q);
ASSERT_MSG(DYNA_REC, singles == singles_func(), ASSERT_MSG(DYNA_REC, singles == singles_func(),
"Register allocation turned singles into doubles in the middle of ps_arith"); "Register allocation turned singles into doubles in the middle of ps_arith");
@ -339,12 +334,11 @@ void JitArm64::ps_sel(UGeckoInstruction inst)
} }
else else
{ {
const ARM64Reg V0Q = fpr.GetReg(); const auto V0Q = fpr.GetScopedReg();
const ARM64Reg V0 = reg_encoder(V0Q); const ARM64Reg V0 = reg_encoder(V0Q);
m_float_emit.FCMGE(size, V0, VA); m_float_emit.FCMGE(size, V0, VA);
m_float_emit.BSL(V0, VC, VB); m_float_emit.BSL(V0, VC, VB);
m_float_emit.MOV(VD, V0); m_float_emit.MOV(VD, V0);
fpr.Unlock(V0Q);
} }
ASSERT_MSG(DYNA_REC, singles == (fpr.IsSingle(a) && fpr.IsSingle(b) && fpr.IsSingle(c)), ASSERT_MSG(DYNA_REC, singles == (fpr.IsSingle(a) && fpr.IsSingle(b) && fpr.IsSingle(c)),
@ -375,41 +369,45 @@ void JitArm64::ps_sumX(UGeckoInstruction inst)
const ARM64Reg VB = fpr.R(b, type); const ARM64Reg VB = fpr.R(b, type);
const ARM64Reg VC = fpr.R(c, type); const ARM64Reg VC = fpr.R(c, type);
const ARM64Reg VD = fpr.RW(d, type); const ARM64Reg VD = fpr.RW(d, type);
const ARM64Reg V0 = fpr.GetReg();
m_float_emit.DUP(size, reg_encoder(V0), reg_encoder(VB), 1); {
const auto V0 = fpr.GetScopedReg();
if (m_accurate_nans) m_float_emit.DUP(size, reg_encoder(V0), reg_encoder(VB), 1);
{
// If the first input is NaN, set the temp register for the second input to 0. This is because:
//
// - If the second input is also NaN, setting it to 0 ensures that the first NaN will be picked.
// - If only the first input is NaN, setting the second input to 0 has no effect on the result.
//
// Either way, we can then do an FADD as usual, and the FADD will make the NaN quiet.
m_float_emit.FCMP(scalar_reg_encoder(VA));
FixupBranch a_not_nan = B(CCFlags::CC_VC);
m_float_emit.MOVI(64, scalar_reg_encoder(V0), 0);
SetJumpTarget(a_not_nan);
}
if (upper) if (m_accurate_nans)
{ {
m_float_emit.FADD(scalar_reg_encoder(V0), scalar_reg_encoder(V0), scalar_reg_encoder(VA)); // If the first input is NaN, set the temp register for the second input to 0. This is
m_float_emit.TRN1(size, reg_encoder(VD), reg_encoder(VC), reg_encoder(V0)); // because:
} //
else if (d != c) // - If the second input is also NaN, setting it to 0 ensures that the first NaN will be
{ // picked.
m_float_emit.FADD(scalar_reg_encoder(VD), scalar_reg_encoder(V0), scalar_reg_encoder(VA)); // - If only the first input is NaN, setting the second input to 0 has no effect on the
m_float_emit.INS(size, VD, 1, VC, 1); // result.
} //
else // Either way, we can then do an FADD as usual, and the FADD will make the NaN quiet.
{ m_float_emit.FCMP(scalar_reg_encoder(VA));
m_float_emit.FADD(scalar_reg_encoder(V0), scalar_reg_encoder(V0), scalar_reg_encoder(VA)); FixupBranch a_not_nan = B(CCFlags::CC_VC);
m_float_emit.INS(size, VD, 0, V0, 0); m_float_emit.MOVI(64, scalar_reg_encoder(V0), 0);
} SetJumpTarget(a_not_nan);
}
fpr.Unlock(V0); if (upper)
{
m_float_emit.FADD(scalar_reg_encoder(V0), scalar_reg_encoder(V0), scalar_reg_encoder(VA));
m_float_emit.TRN1(size, reg_encoder(VD), reg_encoder(VC), reg_encoder(V0));
}
else if (d != c)
{
m_float_emit.FADD(scalar_reg_encoder(VD), scalar_reg_encoder(V0), scalar_reg_encoder(VA));
m_float_emit.INS(size, VD, 1, VC, 1);
}
else
{
m_float_emit.FADD(scalar_reg_encoder(V0), scalar_reg_encoder(V0), scalar_reg_encoder(VA));
m_float_emit.INS(size, VD, 0, V0, 0);
}
}
ASSERT_MSG(DYNA_REC, singles == (fpr.IsSingle(a) && fpr.IsSingle(b) && fpr.IsSingle(c)), ASSERT_MSG(DYNA_REC, singles == (fpr.IsSingle(a) && fpr.IsSingle(b) && fpr.IsSingle(c)),
"Register allocation turned singles into doubles in the middle of ps_sumX"); "Register allocation turned singles into doubles in the middle of ps_sumX");

View file

@ -254,19 +254,27 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r
// We've got two guest registers in a row to store // We've got two guest registers in a row to store
OpArg& reg1 = m_guest_registers[GUEST_GPR_OFFSET + i]; OpArg& reg1 = m_guest_registers[GUEST_GPR_OFFSET + i];
OpArg& reg2 = m_guest_registers[GUEST_GPR_OFFSET + i + 1]; OpArg& reg2 = m_guest_registers[GUEST_GPR_OFFSET + i + 1];
if (reg1.IsDirty() && reg2.IsDirty() && reg1.GetType() == RegType::Register && const bool reg1_imm = reg1.GetType() == RegType::Immediate;
reg2.GetType() == RegType::Register) const bool reg2_imm = reg2.GetType() == RegType::Immediate;
const bool reg1_zero = reg1_imm && reg1.GetImm() == 0;
const bool reg2_zero = reg2_imm && reg2.GetImm() == 0;
const bool flush_all = mode == FlushMode::All;
if (reg1.IsDirty() && reg2.IsDirty() &&
(reg1.GetType() == RegType::Register || (reg1_imm && (reg1_zero || flush_all))) &&
(reg2.GetType() == RegType::Register || (reg2_imm && (reg2_zero || flush_all))))
{ {
const size_t ppc_offset = GetGuestByIndex(i).ppc_offset; const size_t ppc_offset = GetGuestByIndex(i).ppc_offset;
if (ppc_offset <= 252) if (ppc_offset <= 252)
{ {
ARM64Reg RX1 = R(GetGuestByIndex(i)); ARM64Reg RX1 = reg1_zero ? ARM64Reg::WZR : R(GetGuestByIndex(i));
ARM64Reg RX2 = R(GetGuestByIndex(i + 1)); ARM64Reg RX2 = reg2_zero ? ARM64Reg::WZR : R(GetGuestByIndex(i + 1));
m_emit->STP(IndexType::Signed, RX1, RX2, PPC_REG, u32(ppc_offset)); m_emit->STP(IndexType::Signed, RX1, RX2, PPC_REG, u32(ppc_offset));
if (mode == FlushMode::All) if (flush_all)
{ {
UnlockRegister(EncodeRegTo32(RX1)); if (!reg1_zero)
UnlockRegister(EncodeRegTo32(RX2)); UnlockRegister(EncodeRegTo32(RX1));
if (!reg2_zero)
UnlockRegister(EncodeRegTo32(RX2));
reg1.Flush(); reg1.Flush();
reg2.Flush(); reg2.Flush();
} }

View file

@ -177,6 +177,59 @@ public:
// Requires unlocking after done // Requires unlocking after done
Arm64Gen::ARM64Reg GetReg(); Arm64Gen::ARM64Reg GetReg();
class ScopedARM64Reg
{
public:
inline ScopedARM64Reg() = default;
ScopedARM64Reg(const ScopedARM64Reg&) = delete;
explicit inline ScopedARM64Reg(Arm64RegCache& cache) : m_reg(cache.GetReg()), m_gpr(&cache) {}
inline ScopedARM64Reg(Arm64Gen::ARM64Reg reg) : m_reg(reg) {}
inline ScopedARM64Reg(ScopedARM64Reg&& scoped_reg) { *this = std::move(scoped_reg); }
inline ~ScopedARM64Reg() { Unlock(); }
inline ScopedARM64Reg& operator=(const ScopedARM64Reg&) = delete;
inline ScopedARM64Reg& operator=(Arm64Gen::ARM64Reg reg)
{
Unlock();
m_reg = reg;
return *this;
}
inline ScopedARM64Reg& operator=(ScopedARM64Reg&& scoped_reg)
{
// Taking ownership of an existing scoped register, no need to release.
m_reg = scoped_reg.m_reg;
m_gpr = scoped_reg.m_gpr;
scoped_reg.Invalidate();
return *this;
}
inline Arm64Gen::ARM64Reg GetReg() const { return m_reg; }
inline operator Arm64Gen::ARM64Reg() const { return GetReg(); }
inline void Unlock()
{
// Only unlock the register if GPR is set.
if (m_gpr != nullptr)
{
m_gpr->Unlock(m_reg);
}
Invalidate();
}
private:
inline void Invalidate()
{
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
m_gpr = nullptr;
}
Arm64Gen::ARM64Reg m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
Arm64RegCache* m_gpr = nullptr;
};
// Returns a temporary register
// Unlocking is implicitly handled through RAII
inline ScopedARM64Reg GetScopedReg() { return ScopedARM64Reg(*this); }
void UpdateLastUsed(BitSet32 regs_used); void UpdateLastUsed(BitSet32 regs_used);
// Get available host registers // Get available host registers

View file

@ -48,17 +48,16 @@ void JitArm64::FixGTBeforeSettingCRFieldBit(Arm64Gen::ARM64Reg reg)
// if the internal representation either has bit 63 set or has all bits set to zero. // if the internal representation either has bit 63 set or has all bits set to zero.
// If all bits are zero and we set some bit that's unrelated to GT, we need to set bit 63 so GT // If all bits are zero and we set some bit that's unrelated to GT, we need to set bit 63 so GT
// doesn't accidentally become considered set. Gross but necessary; this can break actual games. // doesn't accidentally become considered set. Gross but necessary; this can break actual games.
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64)); ORR(XA, reg, LogicalImm(1ULL << 63, GPRSize::B64));
CMP(reg, ARM64Reg::ZR); CMP(reg, ARM64Reg::ZR);
CSEL(reg, reg, XA, CC_NEQ); CSEL(reg, reg, XA, CC_NEQ);
gpr.Unlock(WA);
} }
void JitArm64::UpdateFPExceptionSummary(ARM64Reg fpscr) void JitArm64::UpdateFPExceptionSummary(ARM64Reg fpscr)
{ {
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
// fpscr.VX = (fpscr & FPSCR_VX_ANY) != 0 // fpscr.VX = (fpscr & FPSCR_VX_ANY) != 0
MOVI2R(WA, FPSCR_VX_ANY); MOVI2R(WA, FPSCR_VX_ANY);
@ -71,8 +70,6 @@ void JitArm64::UpdateFPExceptionSummary(ARM64Reg fpscr)
TST(WA, fpscr, ArithOption(fpscr, ShiftType::LSR, 22)); TST(WA, fpscr, ArithOption(fpscr, ShiftType::LSR, 22));
CSET(WA, CCFlags::CC_NEQ); CSET(WA, CCFlags::CC_NEQ);
BFI(fpscr, WA, MathUtil::IntLog2(FPSCR_FEX), 1); BFI(fpscr, WA, MathUtil::IntLog2(FPSCR_FEX), 1);
gpr.Unlock(WA);
} }
void JitArm64::UpdateRoundingMode() void JitArm64::UpdateRoundingMode()
@ -135,7 +132,7 @@ void JitArm64::mcrxr(UGeckoInstruction inst)
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
gpr.BindCRToRegister(inst.CRFD, false); gpr.BindCRToRegister(inst.CRFD, false);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
ARM64Reg XB = gpr.CR(inst.CRFD); ARM64Reg XB = gpr.CR(inst.CRFD);
ARM64Reg WB = EncodeRegTo32(XB); ARM64Reg WB = EncodeRegTo32(XB);
@ -155,8 +152,6 @@ void JitArm64::mcrxr(UGeckoInstruction inst)
// Clear XER[0-3] // Clear XER[0-3]
static_assert(PPCSTATE_OFF(xer_ca) + 1 == PPCSTATE_OFF(xer_so_ov)); static_assert(PPCSTATE_OFF(xer_ca) + 1 == PPCSTATE_OFF(xer_so_ov));
STRH(IndexType::Unsigned, ARM64Reg::WZR, PPC_REG, PPCSTATE_OFF(xer_ca)); STRH(IndexType::Unsigned, ARM64Reg::WZR, PPC_REG, PPCSTATE_OFF(xer_ca));
gpr.Unlock(WA);
} }
void JitArm64::mfsr(UGeckoInstruction inst) void JitArm64::mfsr(UGeckoInstruction inst)
@ -186,14 +181,12 @@ void JitArm64::mfsrin(UGeckoInstruction inst)
ARM64Reg RB = gpr.R(b); ARM64Reg RB = gpr.R(b);
ARM64Reg RD = gpr.R(d); ARM64Reg RD = gpr.R(d);
ARM64Reg index = gpr.GetReg(); auto index = gpr.GetScopedReg();
ARM64Reg addr = EncodeRegTo64(RD); ARM64Reg addr = EncodeRegTo64(RD);
UBFM(index, RB, 28, 31); UBFM(index, RB, 28, 31);
ADDI2R(addr, PPC_REG, PPCSTATE_OFF_SR(0), addr); ADDI2R(addr, PPC_REG, PPCSTATE_OFF_SR(0), addr);
LDR(RD, addr, ArithOption(EncodeRegTo64(index), true)); LDR(RD, addr, ArithOption(EncodeRegTo64(index), true));
gpr.Unlock(index);
} }
void JitArm64::mtsrin(UGeckoInstruction inst) void JitArm64::mtsrin(UGeckoInstruction inst)
@ -206,14 +199,12 @@ void JitArm64::mtsrin(UGeckoInstruction inst)
ARM64Reg RB = gpr.R(b); ARM64Reg RB = gpr.R(b);
ARM64Reg RD = gpr.R(d); ARM64Reg RD = gpr.R(d);
ARM64Reg index = gpr.GetReg(); auto index = gpr.GetScopedReg();
ARM64Reg addr = gpr.GetReg(); auto addr = gpr.GetScopedReg();
UBFM(index, RB, 28, 31); UBFM(index, RB, 28, 31);
ADDI2R(EncodeRegTo64(addr), PPC_REG, PPCSTATE_OFF_SR(0), EncodeRegTo64(addr)); ADDI2R(EncodeRegTo64(addr), PPC_REG, PPCSTATE_OFF_SR(0), EncodeRegTo64(addr));
STR(RD, EncodeRegTo64(addr), ArithOption(EncodeRegTo64(index), true)); STR(RD, EncodeRegTo64(addr), ArithOption(EncodeRegTo64(index), true));
gpr.Unlock(index, addr);
} }
void JitArm64::twx(UGeckoInstruction inst) void JitArm64::twx(UGeckoInstruction inst)
@ -223,7 +214,7 @@ void JitArm64::twx(UGeckoInstruction inst)
s32 a = inst.RA; s32 a = inst.RA;
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
if (inst.OPCD == 3) // twi if (inst.OPCD == 3) // twi
{ {
@ -278,8 +269,6 @@ void JitArm64::twx(UGeckoInstruction inst)
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG); fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
WriteExit(js.compilerPC + 4); WriteExit(js.compilerPC + 4);
} }
gpr.Unlock(WA);
} }
void JitArm64::mfspr(UGeckoInstruction inst) void JitArm64::mfspr(UGeckoInstruction inst)
@ -294,19 +283,19 @@ void JitArm64::mfspr(UGeckoInstruction inst)
case SPR_TL: case SPR_TL:
case SPR_TU: case SPR_TU:
{ {
ARM64Reg Wg = gpr.GetReg(); auto Wg = gpr.GetScopedReg();
ARM64Reg Xg = EncodeRegTo64(Wg); ARM64Reg Xg = EncodeRegTo64(Wg);
ARM64Reg Wresult = gpr.GetReg(); auto Wresult = gpr.GetScopedReg();
ARM64Reg Xresult = EncodeRegTo64(Wresult); ARM64Reg Xresult = EncodeRegTo64(Wresult);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WB = gpr.GetReg(); auto WB = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
ARM64Reg XB = EncodeRegTo64(WB); ARM64Reg XB = EncodeRegTo64(WB);
ARM64Reg VC = fpr.GetReg(); auto VC = fpr.GetScopedReg();
ARM64Reg VD = fpr.GetReg(); auto VD = fpr.GetScopedReg();
ARM64Reg SC = EncodeRegToSingle(VC); ARM64Reg SC = EncodeRegToSingle(VC);
ARM64Reg SD = EncodeRegToSingle(VD); ARM64Reg SD = EncodeRegToSingle(VD);
@ -371,8 +360,6 @@ void JitArm64::mfspr(UGeckoInstruction inst)
else else
LSR(EncodeRegTo64(gpr.R(n)), Xresult, 32); LSR(EncodeRegTo64(gpr.R(n)), Xresult, 32);
gpr.Unlock(Wg, Wresult, WA, WB);
fpr.Unlock(VC, VD);
break; break;
} }
} }
@ -381,22 +368,18 @@ void JitArm64::mfspr(UGeckoInstruction inst)
LSR(EncodeRegTo64(gpr.R(d)), Xresult, 32); LSR(EncodeRegTo64(gpr.R(d)), Xresult, 32);
else else
MOV(gpr.R(d), Wresult); MOV(gpr.R(d), Wresult);
gpr.Unlock(Wg, Wresult, WA, WB);
fpr.Unlock(VC, VD);
} }
break; break;
case SPR_XER: case SPR_XER:
{ {
gpr.BindToRegister(d, false); gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d); ARM64Reg RD = gpr.R(d);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
LDRH(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_stringctrl)); LDRH(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_stringctrl));
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ORR(RD, RD, WA, ArithOption(WA, ShiftType::LSL, XER_CA_SHIFT)); ORR(RD, RD, WA, ArithOption(WA, ShiftType::LSL, XER_CA_SHIFT));
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_so_ov)); LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_so_ov));
ORR(RD, RD, WA, ArithOption(WA, ShiftType::LSL, XER_OV_SHIFT)); ORR(RD, RD, WA, ArithOption(WA, ShiftType::LSL, XER_OV_SHIFT));
gpr.Unlock(WA);
} }
break; break;
case SPR_WPAR: case SPR_WPAR:
@ -462,14 +445,13 @@ void JitArm64::mtspr(UGeckoInstruction inst)
case SPR_XER: case SPR_XER:
{ {
ARM64Reg RD = gpr.R(inst.RD); ARM64Reg RD = gpr.R(inst.RD);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
AND(WA, RD, LogicalImm(0xFFFFFF7F, GPRSize::B32)); AND(WA, RD, LogicalImm(0xFFFFFF7F, GPRSize::B32));
STRH(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_stringctrl)); STRH(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_stringctrl));
UBFM(WA, RD, XER_CA_SHIFT, XER_CA_SHIFT + 1); UBFM(WA, RD, XER_CA_SHIFT, XER_CA_SHIFT + 1);
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca)); STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
UBFM(WA, RD, XER_OV_SHIFT, 31); // Same as WA = RD >> XER_OV_SHIFT UBFM(WA, RD, XER_OV_SHIFT, 31); // Same as WA = RD >> XER_OV_SHIFT
STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_so_ov)); STRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_so_ov));
gpr.Unlock(WA);
} }
break; break;
default: default:
@ -553,114 +535,112 @@ void JitArm64::crXXX(UGeckoInstruction inst)
return; return;
} }
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
ARM64Reg WB = gpr.GetReg();
ARM64Reg XB = EncodeRegTo64(WB);
// creqv or crnand or crnor
bool negateA = inst.SUBOP10 == 289 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
// crandc or crorc or crnand or crnor
bool negateB =
inst.SUBOP10 == 129 || inst.SUBOP10 == 417 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
// GetCRFieldBit
for (int i = 0; i < 2; i++)
{ {
int field = i ? inst.CRBB >> 2 : inst.CRBA >> 2; auto WB = gpr.GetScopedReg();
int bit = i ? 3 - (inst.CRBB & 3) : 3 - (inst.CRBA & 3); ARM64Reg XB = EncodeRegTo64(WB);
ARM64Reg out = i ? XB : XA;
bool negate = i ? negateB : negateA;
ARM64Reg XC = gpr.CR(field); // creqv or crnand or crnor
ARM64Reg WC = EncodeRegTo32(XC); bool negateA = inst.SUBOP10 == 289 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
switch (bit) // crandc or crorc or crnand or crnor
bool negateB =
inst.SUBOP10 == 129 || inst.SUBOP10 == 417 || inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
// GetCRFieldBit
for (int i = 0; i < 2; i++)
{ {
case PowerPC::CR_SO_BIT: // check bit 59 set int field = i ? inst.CRBB >> 2 : inst.CRBA >> 2;
UBFX(out, XC, PowerPC::CR_EMU_SO_BIT, 1); int bit = i ? 3 - (inst.CRBB & 3) : 3 - (inst.CRBA & 3);
if (negate) ARM64Reg out = i ? XB : XA;
EOR(out, out, LogicalImm(1, GPRSize::B64)); bool negate = i ? negateB : negateA;
break;
case PowerPC::CR_EQ_BIT: // check bits 31-0 == 0 ARM64Reg XC = gpr.CR(field);
CMP(WC, ARM64Reg::WZR); ARM64Reg WC = EncodeRegTo32(XC);
CSET(out, negate ? CC_NEQ : CC_EQ); switch (bit)
break; {
case PowerPC::CR_SO_BIT: // check bit 59 set
UBFX(out, XC, PowerPC::CR_EMU_SO_BIT, 1);
if (negate)
EOR(out, out, LogicalImm(1, GPRSize::B64));
break;
case PowerPC::CR_GT_BIT: // check val > 0 case PowerPC::CR_EQ_BIT: // check bits 31-0 == 0
CMP(XC, ARM64Reg::ZR); CMP(WC, ARM64Reg::WZR);
CSET(out, negate ? CC_LE : CC_GT); CSET(out, negate ? CC_NEQ : CC_EQ);
break; break;
case PowerPC::CR_LT_BIT: // check bit 62 set case PowerPC::CR_GT_BIT: // check val > 0
UBFX(out, XC, PowerPC::CR_EMU_LT_BIT, 1); CMP(XC, ARM64Reg::ZR);
if (negate) CSET(out, negate ? CC_LE : CC_GT);
EOR(out, out, LogicalImm(1, GPRSize::B64)); break;
break;
default: case PowerPC::CR_LT_BIT: // check bit 62 set
ASSERT_MSG(DYNA_REC, false, "Invalid CR bit"); UBFX(out, XC, PowerPC::CR_EMU_LT_BIT, 1);
if (negate)
EOR(out, out, LogicalImm(1, GPRSize::B64));
break;
default:
ASSERT_MSG(DYNA_REC, false, "Invalid CR bit");
}
} }
}
// Compute combined bit // Compute combined bit
switch (inst.SUBOP10) switch (inst.SUBOP10)
{ {
case 33: // crnor: ~(A || B) == (~A && ~B) case 33: // crnor: ~(A || B) == (~A && ~B)
case 129: // crandc: A && ~B case 129: // crandc: A && ~B
case 257: // crand: A && B case 257: // crand: A && B
AND(XA, XA, XB); AND(XA, XA, XB);
break; break;
case 193: // crxor: A ^ B case 193: // crxor: A ^ B
case 289: // creqv: ~(A ^ B) = ~A ^ B case 289: // creqv: ~(A ^ B) = ~A ^ B
EOR(XA, XA, XB); EOR(XA, XA, XB);
break; break;
case 225: // crnand: ~(A && B) == (~A || ~B) case 225: // crnand: ~(A && B) == (~A || ~B)
case 417: // crorc: A || ~B case 417: // crorc: A || ~B
case 449: // cror: A || B case 449: // cror: A || B
ORR(XA, XA, XB); ORR(XA, XA, XB);
break; break;
}
} }
// Store result bit in CRBD // Store result bit in CRBD
int field = inst.CRBD >> 2; int field = inst.CRBD >> 2;
int bit = 3 - (inst.CRBD & 3); int bit = 3 - (inst.CRBD & 3);
gpr.Unlock(WB);
WB = ARM64Reg::INVALID_REG;
gpr.BindCRToRegister(field, true); gpr.BindCRToRegister(field, true);
XB = gpr.CR(field); ARM64Reg CR = gpr.CR(field);
if (bit != PowerPC::CR_GT_BIT) if (bit != PowerPC::CR_GT_BIT)
FixGTBeforeSettingCRFieldBit(XB); FixGTBeforeSettingCRFieldBit(CR);
switch (bit) switch (bit)
{ {
case PowerPC::CR_SO_BIT: // set bit 59 to input case PowerPC::CR_SO_BIT: // set bit 59 to input
BFI(XB, XA, PowerPC::CR_EMU_SO_BIT, 1); BFI(CR, XA, PowerPC::CR_EMU_SO_BIT, 1);
break; break;
case PowerPC::CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input case PowerPC::CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input
AND(XB, XB, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64)); AND(CR, CR, LogicalImm(0xFFFF'FFFF'0000'0000, GPRSize::B64));
EOR(XA, XA, LogicalImm(1, GPRSize::B64)); EOR(XA, XA, LogicalImm(1, GPRSize::B64));
ORR(XB, XB, XA); ORR(CR, CR, XA);
break; break;
case PowerPC::CR_GT_BIT: // set bit 63 to !input case PowerPC::CR_GT_BIT: // set bit 63 to !input
EOR(XA, XA, LogicalImm(1, GPRSize::B64)); EOR(XA, XA, LogicalImm(1, GPRSize::B64));
BFI(XB, XA, 63, 1); BFI(CR, XA, 63, 1);
break; break;
case PowerPC::CR_LT_BIT: // set bit 62 to input case PowerPC::CR_LT_BIT: // set bit 62 to input
BFI(XB, XA, PowerPC::CR_EMU_LT_BIT, 1); BFI(CR, XA, PowerPC::CR_EMU_LT_BIT, 1);
break; break;
} }
ORR(XB, XB, LogicalImm(1ULL << 32, GPRSize::B64)); ORR(CR, CR, LogicalImm(1ULL << 32, GPRSize::B64));
gpr.Unlock(WA);
} }
void JitArm64::mfcr(UGeckoInstruction inst) void JitArm64::mfcr(UGeckoInstruction inst)
@ -670,8 +650,8 @@ void JitArm64::mfcr(UGeckoInstruction inst)
gpr.BindToRegister(inst.RD, false); gpr.BindToRegister(inst.RD, false);
ARM64Reg WA = gpr.R(inst.RD); ARM64Reg WA = gpr.R(inst.RD);
ARM64Reg WB = gpr.GetReg(); auto WB = gpr.GetScopedReg();
ARM64Reg WC = gpr.GetReg(); auto WC = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
ARM64Reg XB = EncodeRegTo64(WB); ARM64Reg XB = EncodeRegTo64(WB);
ARM64Reg XC = EncodeRegTo64(WC); ARM64Reg XC = EncodeRegTo64(WC);
@ -716,8 +696,6 @@ void JitArm64::mfcr(UGeckoInstruction inst)
else if (!js.op->crInUse[i]) else if (!js.op->crInUse[i])
gpr.StoreCRRegisters(BitSet8{i}, WC); gpr.StoreCRRegisters(BitSet8{i}, WC);
} }
gpr.Unlock(WB, WC);
} }
void JitArm64::mtcrf(UGeckoInstruction inst) void JitArm64::mtcrf(UGeckoInstruction inst)
@ -729,7 +707,7 @@ void JitArm64::mtcrf(UGeckoInstruction inst)
if (crm != 0) if (crm != 0)
{ {
ARM64Reg RS = gpr.R(inst.RS); ARM64Reg RS = gpr.R(inst.RS);
ARM64Reg WB = gpr.GetReg(); auto WB = gpr.GetScopedReg();
ARM64Reg XB = EncodeRegTo64(WB); ARM64Reg XB = EncodeRegTo64(WB);
MOVP2R(XB, PowerPC::ConditionRegister::s_crTable.data()); MOVP2R(XB, PowerPC::ConditionRegister::s_crTable.data());
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
@ -753,7 +731,6 @@ void JitArm64::mtcrf(UGeckoInstruction inst)
LDR(CR, XB, ArithOption(CR, true)); LDR(CR, XB, ArithOption(CR, true));
} }
} }
gpr.Unlock(WB);
} }
} }
@ -771,7 +748,7 @@ void JitArm64::mcrfs(UGeckoInstruction inst)
gpr.BindCRToRegister(field, false); gpr.BindCRToRegister(field, false);
ARM64Reg CR = gpr.CR(field); ARM64Reg CR = gpr.CR(field);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WCR = EncodeRegTo32(CR); ARM64Reg WCR = EncodeRegTo32(CR);
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
@ -789,8 +766,6 @@ void JitArm64::mcrfs(UGeckoInstruction inst)
MOVP2R(XA, PowerPC::ConditionRegister::s_crTable.data()); MOVP2R(XA, PowerPC::ConditionRegister::s_crTable.data());
LDR(CR, XA, ArithOption(CR, true)); LDR(CR, XA, ArithOption(CR, true));
gpr.Unlock(WA);
} }
void JitArm64::mffsx(UGeckoInstruction inst) void JitArm64::mffsx(UGeckoInstruction inst)
@ -799,7 +774,7 @@ void JitArm64::mffsx(UGeckoInstruction inst)
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg XA = EncodeRegTo64(WA); ARM64Reg XA = EncodeRegTo64(WA);
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
@ -808,8 +783,6 @@ void JitArm64::mffsx(UGeckoInstruction inst)
ORR(XA, XA, LogicalImm(0xFFF8'0000'0000'0000, GPRSize::B64)); ORR(XA, XA, LogicalImm(0xFFF8'0000'0000'0000, GPRSize::B64));
m_float_emit.FMOV(EncodeRegToDouble(VD), XA); m_float_emit.FMOV(EncodeRegToDouble(VD), XA);
gpr.Unlock(WA);
} }
void JitArm64::mtfsb0x(UGeckoInstruction inst) void JitArm64::mtfsb0x(UGeckoInstruction inst)
@ -824,17 +797,17 @@ void JitArm64::mtfsb0x(UGeckoInstruction inst)
if (mask == FPSCR_FEX || mask == FPSCR_VX) if (mask == FPSCR_FEX || mask == FPSCR_VX)
return; return;
ARM64Reg WA = gpr.GetReg(); {
auto WA = gpr.GetScopedReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
AND(WA, WA, LogicalImm(inverted_mask, GPRSize::B32)); AND(WA, WA, LogicalImm(inverted_mask, GPRSize::B32));
if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0) if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA); UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
}
gpr.Unlock(WA);
if (inst.CRBD >= 29) if (inst.CRBD >= 29)
UpdateRoundingMode(); UpdateRoundingMode();
@ -852,25 +825,24 @@ void JitArm64::mtfsb1x(UGeckoInstruction inst)
if (mask == FPSCR_FEX || mask == FPSCR_VX) if (mask == FPSCR_FEX || mask == FPSCR_VX)
return; return;
ARM64Reg WA = gpr.GetReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
if ((mask & FPSCR_ANY_X) != 0)
{ {
ARM64Reg WB = gpr.GetReg(); auto WA = gpr.GetScopedReg();
TST(WA, LogicalImm(mask, GPRSize::B32));
ORR(WB, WA, LogicalImm(1 << 31, GPRSize::B32)); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
CSEL(WA, WA, WB, CCFlags::CC_NEQ);
gpr.Unlock(WB); if ((mask & FPSCR_ANY_X) != 0)
{
auto WB = gpr.GetScopedReg();
TST(WA, LogicalImm(mask, GPRSize::B32));
ORR(WB, WA, LogicalImm(1 << 31, GPRSize::B32));
CSEL(WA, WA, WB, CCFlags::CC_NEQ);
}
ORR(WA, WA, LogicalImm(mask, GPRSize::B32));
if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
} }
ORR(WA, WA, LogicalImm(mask, GPRSize::B32));
if ((mask & (FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
gpr.Unlock(WA);
if (inst.CRBD >= 29) if (inst.CRBD >= 29)
UpdateRoundingMode(); UpdateRoundingMode();
@ -887,32 +859,31 @@ void JitArm64::mtfsfix(UGeckoInstruction inst)
u8 shift = 28 - 4 * inst.CRFD; u8 shift = 28 - 4 * inst.CRFD;
u32 mask = 0xF << shift; u32 mask = 0xF << shift;
ARM64Reg WA = gpr.GetReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
if (imm == 0xF)
{ {
ORR(WA, WA, LogicalImm(mask, GPRSize::B32)); auto WA = gpr.GetScopedReg();
}
else if (imm == 0x0)
{
const u32 inverted_mask = ~mask;
AND(WA, WA, LogicalImm(inverted_mask, GPRSize::B32));
}
else
{
ARM64Reg WB = gpr.GetReg();
MOVZ(WB, imm);
BFI(WA, WB, shift, 4);
gpr.Unlock(WB);
}
if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0) LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
gpr.Unlock(WA); if (imm == 0xF)
{
ORR(WA, WA, LogicalImm(mask, GPRSize::B32));
}
else if (imm == 0x0)
{
const u32 inverted_mask = ~mask;
AND(WA, WA, LogicalImm(inverted_mask, GPRSize::B32));
}
else
{
auto WB = gpr.GetScopedReg();
MOVZ(WB, imm);
BFI(WA, WB, shift, 4);
}
if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
}
// Field 7 contains NI and RN. // Field 7 contains NI and RN.
if (inst.CRFD == 7) if (inst.CRFD == 7)
@ -936,49 +907,43 @@ void JitArm64::mtfsfx(UGeckoInstruction inst)
if (mask == 0xFFFFFFFF) if (mask == 0xFFFFFFFF)
{ {
ARM64Reg VB = fpr.R(inst.FB, RegType::LowerPair); ARM64Reg VB = fpr.R(inst.FB, RegType::LowerPair);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
m_float_emit.FMOV(WA, EncodeRegToSingle(VB)); m_float_emit.FMOV(WA, EncodeRegToSingle(VB));
UpdateFPExceptionSummary(WA); UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
gpr.Unlock(WA);
} }
else if (mask != 0) else if (mask != 0)
{ {
ARM64Reg VB = fpr.R(inst.FB, RegType::LowerPair); ARM64Reg VB = fpr.R(inst.FB, RegType::LowerPair);
ARM64Reg WA = gpr.GetReg(); auto WA = gpr.GetScopedReg();
ARM64Reg WB = gpr.GetReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
m_float_emit.FMOV(WB, EncodeRegToSingle(VB));
if (LogicalImm imm = LogicalImm(mask, GPRSize::B32))
{ {
const u32 inverted_mask = ~mask; auto WB = gpr.GetScopedReg();
AND(WA, WA, LogicalImm(inverted_mask, GPRSize::B32));
AND(WB, WB, imm); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
m_float_emit.FMOV(WB, EncodeRegToSingle(VB));
if (LogicalImm imm = LogicalImm(mask, GPRSize::B32))
{
const u32 inverted_mask = ~mask;
AND(WA, WA, LogicalImm(inverted_mask, GPRSize::B32));
AND(WB, WB, imm);
}
else
{
auto WC = gpr.GetScopedReg();
MOVI2R(WC, mask);
BIC(WA, WA, WC);
AND(WB, WB, WC);
}
ORR(WA, WA, WB);
} }
else
{
ARM64Reg WC = gpr.GetReg();
MOVI2R(WC, mask);
BIC(WA, WA, WC);
AND(WB, WB, WC);
gpr.Unlock(WC);
}
ORR(WA, WA, WB);
gpr.Unlock(WB);
if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0) if ((mask & (FPSCR_FEX | FPSCR_VX | FPSCR_ANY_X | FPSCR_ANY_E)) != 0)
UpdateFPExceptionSummary(WA); UpdateFPExceptionSummary(WA);
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr)); STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(fpscr));
gpr.Unlock(WA);
} }
if (inst.FM & 1) if (inst.FM & 1)

View file

@ -86,7 +86,7 @@ void Host_UpdateDisasmDialog()
{ {
} }
void Host_JitCacheCleared() void Host_JitCacheInvalidation()
{ {
} }

View file

@ -21,6 +21,7 @@
#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h" #include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h" #include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h" #include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
@ -36,7 +37,7 @@ AchievementSettingsWidget::AchievementSettingsWidget(QWidget* parent) : QWidget(
// If hardcore is enabled when the emulator starts, make sure it turns off what it needs to // If hardcore is enabled when the emulator starts, make sure it turns off what it needs to
if (Config::Get(Config::RA_HARDCORE_ENABLED)) if (Config::Get(Config::RA_HARDCORE_ENABLED))
ToggleHardcore(); UpdateHardcoreMode();
} }
void AchievementSettingsWidget::UpdateData(int login_failed_code) void AchievementSettingsWidget::UpdateData(int login_failed_code)
@ -258,11 +259,7 @@ void AchievementSettingsWidget::ToggleRAIntegration()
instance.Init(); instance.Init();
else else
instance.Shutdown(); instance.Shutdown();
if (Config::Get(Config::RA_HARDCORE_ENABLED)) UpdateHardcoreMode();
{
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
emit Settings::Instance().HardcoreStateChanged();
}
} }
void AchievementSettingsWidget::Login() void AchievementSettingsWidget::Login()
@ -276,24 +273,31 @@ void AchievementSettingsWidget::Login()
void AchievementSettingsWidget::Logout() void AchievementSettingsWidget::Logout()
{ {
AchievementManager::GetInstance().Logout(); auto confirm = ModalMessageBox::question(
SaveSettings(); this, tr("Confirm Logout"), tr("Are you sure you want to log out of RetroAchievements?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton, Qt::ApplicationModal);
if (confirm == QMessageBox::Yes)
{
AchievementManager::GetInstance().Logout();
SaveSettings();
}
} }
void AchievementSettingsWidget::ToggleHardcore() void AchievementSettingsWidget::ToggleHardcore()
{ {
SaveSettings();
AchievementManager::GetInstance().SetHardcoreMode();
if (Config::Get(Config::RA_HARDCORE_ENABLED)) if (Config::Get(Config::RA_HARDCORE_ENABLED))
{ {
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f) auto confirm = ModalMessageBox::question(
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f); this, tr("Confirm Hardcore Off"), tr("Are you sure you want to turn hardcore mode off?"),
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false); QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton, Qt::ApplicationModal);
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, false); if (confirm != QMessageBox::Yes)
Settings::Instance().SetDebugModeEnabled(false); {
SignalBlocking(m_common_hardcore_enabled_input)->setChecked(true);
return;
}
} }
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance())); SaveSettings();
emit Settings::Instance().HardcoreStateChanged(); UpdateHardcoreMode();
} }
void AchievementSettingsWidget::ToggleUnofficial() void AchievementSettingsWidget::ToggleUnofficial()
@ -323,4 +327,15 @@ void AchievementSettingsWidget::ToggleProgress()
SaveSettings(); SaveSettings();
} }
void AchievementSettingsWidget::UpdateHardcoreMode()
{
AchievementManager::GetInstance().SetHardcoreMode();
if (Config::Get(Config::RA_HARDCORE_ENABLED))
{
Settings::Instance().SetDebugModeEnabled(false);
}
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
emit Settings::Instance().HardcoreStateChanged();
}
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS

View file

@ -39,6 +39,8 @@ private:
void ToggleDiscordPresence(); void ToggleDiscordPresence();
void ToggleProgress(); void ToggleProgress();
void UpdateHardcoreMode();
QGroupBox* m_common_box; QGroupBox* m_common_box;
QVBoxLayout* m_common_layout; QVBoxLayout* m_common_layout;
ToolTipCheckBox* m_common_integration_enabled_input; ToolTipCheckBox* m_common_integration_enabled_input;

View file

@ -318,6 +318,8 @@ add_executable(dolphin-mpn
QtUtils/ParallelProgressDialog.h QtUtils/ParallelProgressDialog.h
QtUtils/PartiallyClosableTabWidget.cpp QtUtils/PartiallyClosableTabWidget.cpp
QtUtils/PartiallyClosableTabWidget.h QtUtils/PartiallyClosableTabWidget.h
QtUtils/QtUtils.cpp
QtUtils/QtUtils.h
QtUtils/SetWindowDecorations.cpp QtUtils/SetWindowDecorations.cpp
QtUtils/SetWindowDecorations.h QtUtils/SetWindowDecorations.h
QtUtils/SignalBlocking.h QtUtils/SignalBlocking.h

View file

@ -140,20 +140,14 @@ void ARCodeWidget::SortAlphabetically()
void ARCodeWidget::SortEnabledCodesFirst() void ARCodeWidget::SortEnabledCodesFirst()
{ {
std::stable_sort(m_ar_codes.begin(), m_ar_codes.end(), [](const auto& a, const auto& b) { std::ranges::stable_partition(m_ar_codes, std::identity{}, &ActionReplay::ARCode::enabled);
return a.enabled && a.enabled != b.enabled;
});
UpdateList(); UpdateList();
SaveCodes(); SaveCodes();
} }
void ARCodeWidget::SortDisabledCodesFirst() void ARCodeWidget::SortDisabledCodesFirst()
{ {
std::stable_sort(m_ar_codes.begin(), m_ar_codes.end(), [](const auto& a, const auto& b) { std::ranges::stable_partition(m_ar_codes, std::logical_not{}, &ActionReplay::ARCode::enabled);
return !a.enabled && a.enabled != b.enabled;
});
UpdateList(); UpdateList();
SaveCodes(); SaveCodes();
} }

View file

@ -317,20 +317,14 @@ void GeckoCodeWidget::SortAlphabetically()
void GeckoCodeWidget::SortEnabledCodesFirst() void GeckoCodeWidget::SortEnabledCodesFirst()
{ {
std::stable_sort(m_gecko_codes.begin(), m_gecko_codes.end(), [](const auto& a, const auto& b) { std::ranges::stable_partition(m_gecko_codes, std::identity{}, &Gecko::GeckoCode::enabled);
return a.enabled && a.enabled != b.enabled;
});
UpdateList(); UpdateList();
SaveCodes(); SaveCodes();
} }
void GeckoCodeWidget::SortDisabledCodesFirst() void GeckoCodeWidget::SortDisabledCodesFirst()
{ {
std::stable_sort(m_gecko_codes.begin(), m_gecko_codes.end(), [](const auto& a, const auto& b) { std::ranges::stable_partition(m_gecko_codes, std::logical_not{}, &Gecko::GeckoCode::enabled);
return !a.enabled && a.enabled != b.enabled;
});
UpdateList(); UpdateList();
SaveCodes(); SaveCodes();
} }

View file

@ -241,7 +241,7 @@ void GeneralWidget::AddDescriptions()
"recommended. Different games and different GPUs will behave differently on each " "recommended. Different games and different GPUs will behave differently on each "
"backend, so for the best emulation experience it is recommended to try each and " "backend, so for the best emulation experience it is recommended to try each and "
"select the backend that is least problematic.<br><br><dolphin_emphasis>If unsure, " "select the backend that is least problematic.<br><br><dolphin_emphasis>If unsure, "
"select OpenGL.</dolphin_emphasis>"); "select %1.</dolphin_emphasis>");
static const char TR_FULLSCREEN_DESCRIPTION[] = static const char TR_FULLSCREEN_DESCRIPTION[] =
QT_TR_NOOP("Uses the entire screen for rendering.<br><br>If disabled, a " QT_TR_NOOP("Uses the entire screen for rendering.<br><br>If disabled, a "
"render window will be created instead.<br><br><dolphin_emphasis>If " "render window will be created instead.<br><br><dolphin_emphasis>If "
@ -313,7 +313,9 @@ void GeneralWidget::AddDescriptions()
"unsure, leave this unchecked.</dolphin_emphasis>"); "unsure, leave this unchecked.</dolphin_emphasis>");
m_backend_combo->SetTitle(tr("Backend")); m_backend_combo->SetTitle(tr("Backend"));
m_backend_combo->SetDescription(tr(TR_BACKEND_DESCRIPTION)); m_backend_combo->SetDescription(
tr(TR_BACKEND_DESCRIPTION)
.arg(QString::fromStdString(VideoBackendBase::GetDefaultBackendDisplayName())));
m_adapter_combo->SetTitle(tr("Adapter")); m_adapter_combo->SetTitle(tr("Adapter"));

View file

@ -223,7 +223,7 @@ void HacksWidget::AddDescriptions()
"Ignores any requests from the CPU to read from or write to the EFB. " "Ignores any requests from the CPU to read from or write to the EFB. "
"<br><br>Improves performance in some games, but will disable all EFB-based " "<br><br>Improves performance in some games, but will disable all EFB-based "
"graphical effects or gameplay-related features.<br><br><dolphin_emphasis>If unsure, " "graphical effects or gameplay-related features.<br><br><dolphin_emphasis>If unsure, "
"leave this unchecked.</dolphin_emphasis>"); "leave this checked.</dolphin_emphasis>");
static const char TR_IGNORE_FORMAT_CHANGE_DESCRIPTION[] = QT_TR_NOOP( static const char TR_IGNORE_FORMAT_CHANGE_DESCRIPTION[] = QT_TR_NOOP(
"Ignores any changes to the EFB format.<br><br>Improves performance in many games " "Ignores any changes to the EFB format.<br><br>Improves performance in many games "
"without " "without "

View file

@ -313,7 +313,7 @@ void JITWidget::SaveQSettings() const
void JITWidget::ConnectSlots() void JITWidget::ConnectSlots()
{ {
auto* const host = Host::GetInstance(); auto* const host = Host::GetInstance();
connect(host, &Host::JitCacheCleared, this, &JITWidget::OnJitCacheCleared); connect(host, &Host::JitCacheInvalidation, this, &JITWidget::OnJitCacheInvalidation);
connect(host, &Host::UpdateDisasmDialog, this, &JITWidget::OnUpdateDisasmDialog); connect(host, &Host::UpdateDisasmDialog, this, &JITWidget::OnUpdateDisasmDialog);
connect(host, &Host::PPCSymbolsChanged, this, &JITWidget::OnPPCSymbolsUpdated); connect(host, &Host::PPCSymbolsChanged, this, &JITWidget::OnPPCSymbolsUpdated);
connect(host, &Host::PPCBreakpointsChanged, this, &JITWidget::OnPPCBreakpointsChanged); connect(host, &Host::PPCBreakpointsChanged, this, &JITWidget::OnPPCBreakpointsChanged);
@ -326,7 +326,7 @@ void JITWidget::ConnectSlots()
void JITWidget::DisconnectSlots() void JITWidget::DisconnectSlots()
{ {
auto* const host = Host::GetInstance(); auto* const host = Host::GetInstance();
disconnect(host, &Host::JitCacheCleared, this, &JITWidget::OnJitCacheCleared); disconnect(host, &Host::JitCacheInvalidation, this, &JITWidget::OnJitCacheInvalidation);
disconnect(host, &Host::UpdateDisasmDialog, this, &JITWidget::OnUpdateDisasmDialog); disconnect(host, &Host::UpdateDisasmDialog, this, &JITWidget::OnUpdateDisasmDialog);
disconnect(host, &Host::PPCSymbolsChanged, this, &JITWidget::OnPPCSymbolsUpdated); disconnect(host, &Host::PPCSymbolsChanged, this, &JITWidget::OnPPCSymbolsUpdated);
disconnect(host, &Host::PPCBreakpointsChanged, this, &JITWidget::OnPPCBreakpointsChanged); disconnect(host, &Host::PPCBreakpointsChanged, this, &JITWidget::OnPPCBreakpointsChanged);
@ -340,7 +340,7 @@ void JITWidget::Show()
{ {
ConnectSlots(); ConnectSlots();
// Handle every slot that may have missed a signal while this widget was hidden. // Handle every slot that may have missed a signal while this widget was hidden.
// OnJitCacheCleared() can be skipped. // OnJitCacheInvalidation() can be skipped.
// OnUpdateDisasmDialog() can be skipped. // OnUpdateDisasmDialog() can be skipped.
// OnPPCSymbolsUpdated() can be skipped. // OnPPCSymbolsUpdated() can be skipped.
// OnPPCBreakpointsChanged() can be skipped. // OnPPCBreakpointsChanged() can be skipped.
@ -446,7 +446,7 @@ void JITWidget::OnStatusBarPressed()
ShowFreeMemoryStatus(); ShowFreeMemoryStatus();
} }
void JITWidget::OnJitCacheCleared() void JITWidget::OnJitCacheInvalidation()
{ {
if (Core::GetState(m_system) != Core::State::Paused) if (Core::GetState(m_system) != Core::State::Paused)
return; return;

View file

@ -102,7 +102,7 @@ private:
void OnStatusBarPressed(); void OnStatusBarPressed();
// Conditionally connected slots (external signals) // Conditionally connected slots (external signals)
void OnJitCacheCleared(); void OnJitCacheInvalidation();
void OnUpdateDisasmDialog(); void OnUpdateDisasmDialog();
void OnPPCSymbolsUpdated(); void OnPPCSymbolsUpdated();
void OnPPCBreakpointsChanged(); void OnPPCBreakpointsChanged();

View file

@ -112,7 +112,7 @@ void JitBlockTableModel::UpdateSymbols()
void JitBlockTableModel::ConnectSlots() void JitBlockTableModel::ConnectSlots()
{ {
auto* const host = Host::GetInstance(); auto* const host = Host::GetInstance();
connect(host, &Host::JitCacheCleared, this, &JitBlockTableModel::OnJitCacheCleared); connect(host, &Host::JitCacheInvalidation, this, &JitBlockTableModel::OnJitCacheInvalidation);
connect(host, &Host::JitProfileDataWiped, this, &JitBlockTableModel::OnJitProfileDataWiped); connect(host, &Host::JitProfileDataWiped, this, &JitBlockTableModel::OnJitProfileDataWiped);
connect(host, &Host::UpdateDisasmDialog, this, &JitBlockTableModel::OnUpdateDisasmDialog); connect(host, &Host::UpdateDisasmDialog, this, &JitBlockTableModel::OnUpdateDisasmDialog);
connect(host, &Host::PPCSymbolsChanged, this, &JitBlockTableModel::OnPPCSymbolsUpdated); connect(host, &Host::PPCSymbolsChanged, this, &JitBlockTableModel::OnPPCSymbolsUpdated);
@ -125,7 +125,7 @@ void JitBlockTableModel::ConnectSlots()
void JitBlockTableModel::DisconnectSlots() void JitBlockTableModel::DisconnectSlots()
{ {
auto* const host = Host::GetInstance(); auto* const host = Host::GetInstance();
disconnect(host, &Host::JitCacheCleared, this, &JitBlockTableModel::OnJitCacheCleared); disconnect(host, &Host::JitCacheInvalidation, this, &JitBlockTableModel::OnJitCacheInvalidation);
disconnect(host, &Host::JitProfileDataWiped, this, &JitBlockTableModel::OnJitProfileDataWiped); disconnect(host, &Host::JitProfileDataWiped, this, &JitBlockTableModel::OnJitProfileDataWiped);
disconnect(host, &Host::UpdateDisasmDialog, this, &JitBlockTableModel::OnUpdateDisasmDialog); disconnect(host, &Host::UpdateDisasmDialog, this, &JitBlockTableModel::OnUpdateDisasmDialog);
disconnect(host, &Host::PPCSymbolsChanged, this, &JitBlockTableModel::OnPPCSymbolsUpdated); disconnect(host, &Host::PPCSymbolsChanged, this, &JitBlockTableModel::OnPPCSymbolsUpdated);
@ -169,7 +169,7 @@ void JitBlockTableModel::OnFilterSymbolTextChanged(const QString& string)
m_filtering_by_symbols = !string.isEmpty(); m_filtering_by_symbols = !string.isEmpty();
} }
void JitBlockTableModel::OnJitCacheCleared() void JitBlockTableModel::OnJitCacheInvalidation()
{ {
Update(Core::GetState(m_system)); Update(Core::GetState(m_system));
} }
@ -187,7 +187,9 @@ void JitBlockTableModel::OnUpdateDisasmDialog()
void JitBlockTableModel::OnPPCSymbolsUpdated() void JitBlockTableModel::OnPPCSymbolsUpdated()
{ {
UpdateSymbols(); // Previously, this was only a call to `UpdateSymbols`, but HLE patch engine code can
// invalidate JIT blocks when specific symbols are loaded. What can be done about it?
Update(Core::GetState(m_system));
} }
void JitBlockTableModel::OnPPCBreakpointsChanged() void JitBlockTableModel::OnPPCBreakpointsChanged()

View file

@ -106,7 +106,7 @@ private:
void Hide(); void Hide();
// Conditionally connected slots (external signals) // Conditionally connected slots (external signals)
void OnJitCacheCleared(); void OnJitCacheInvalidation();
void OnJitProfileDataWiped(); void OnJitProfileDataWiped();
void OnUpdateDisasmDialog(); void OnUpdateDisasmDialog();
void OnPPCSymbolsUpdated(); void OnPPCSymbolsUpdated();

View file

@ -205,6 +205,7 @@
<ClCompile Include="QtUtils\ModalMessageBox.cpp" /> <ClCompile Include="QtUtils\ModalMessageBox.cpp" />
<ClCompile Include="QtUtils\NonDefaultQPushButton.cpp" /> <ClCompile Include="QtUtils\NonDefaultQPushButton.cpp" />
<ClCompile Include="QtUtils\PartiallyClosableTabWidget.cpp" /> <ClCompile Include="QtUtils\PartiallyClosableTabWidget.cpp" />
<ClCompile Include="QtUtils\QtUtils.cpp" />
<ClCompile Include="QtUtils\SetWindowDecorations.cpp" /> <ClCompile Include="QtUtils\SetWindowDecorations.cpp" />
<ClCompile Include="QtUtils\UTF8CodePointCountValidator.cpp" /> <ClCompile Include="QtUtils\UTF8CodePointCountValidator.cpp" />
<ClCompile Include="QtUtils\WindowActivationEventFilter.cpp" /> <ClCompile Include="QtUtils\WindowActivationEventFilter.cpp" />
@ -412,6 +413,7 @@
<ClInclude Include="QtUtils\FromStdString.h" /> <ClInclude Include="QtUtils\FromStdString.h" />
<QtMoc Include="QtUtils\ParallelProgressDialog.h" /> <QtMoc Include="QtUtils\ParallelProgressDialog.h" />
<QtMoc Include="QtUtils\PartiallyClosableTabWidget.h" /> <QtMoc Include="QtUtils\PartiallyClosableTabWidget.h" />
<ClInclude Include="QtUtils\QtUtils.h" />
<ClInclude Include="QtUtils\SetWindowDecorations.h" /> <ClInclude Include="QtUtils\SetWindowDecorations.h" />
<QtMoc Include="QtUtils\UTF8CodePointCountValidator.h" /> <QtMoc Include="QtUtils\UTF8CodePointCountValidator.h" />
<QtMoc Include="QtUtils\WindowActivationEventFilter.h" /> <QtMoc Include="QtUtils\WindowActivationEventFilter.h" />

View file

@ -256,9 +256,9 @@ void Host_UpdateDisasmDialog()
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->UpdateDisasmDialog(); }); QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->UpdateDisasmDialog(); });
} }
void Host_JitCacheCleared() void Host_JitCacheInvalidation()
{ {
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->JitCacheCleared(); }); QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->JitCacheInvalidation(); });
} }
void Host_JitProfileDataWiped() void Host_JitProfileDataWiped()

View file

@ -40,7 +40,7 @@ signals:
void RequestStop(); void RequestStop();
void RequestRenderSize(int w, int h); void RequestRenderSize(int w, int h);
void UpdateDisasmDialog(); void UpdateDisasmDialog();
void JitCacheCleared(); void JitCacheInvalidation();
void JitProfileDataWiped(); void JitProfileDataWiped();
void PPCSymbolsChanged(); void PPCSymbolsChanged();
void PPCBreakpointsChanged(); void PPCBreakpointsChanged();

View file

@ -257,7 +257,8 @@ int main(int argc, char* argv[])
Settings::Instance().InitDefaultPalette(); Settings::Instance().InitDefaultPalette();
Settings::Instance().ApplyStyle(); Settings::Instance().ApplyStyle();
MainWindow win{std::move(boot), static_cast<const char*>(options.get("movie"))}; MainWindow win{Core::System::GetInstance(), std::move(boot),
static_cast<const char*>(options.get("movie"))};

View file

@ -67,6 +67,7 @@
#include "Core/State.h" #include "Core/State.h"
#include "Core/System.h" #include "Core/System.h"
#include "Core/WiiUtils.h" #include "Core/WiiUtils.h"
#include "DiscIO/DirectoryBlob.h" #include "DiscIO/DirectoryBlob.h"
#include "DiscIO/NANDImporter.h" #include "DiscIO/NANDImporter.h"
#include "DiscIO/RiivolutionPatcher.h" #include "DiscIO/RiivolutionPatcher.h"
@ -80,7 +81,6 @@
#include "DolphinQt/Config/LogConfigWidget.h" #include "DolphinQt/Config/LogConfigWidget.h"
#include "DolphinQt/Config/LogWidget.h" #include "DolphinQt/Config/LogWidget.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/Config/PropertiesDialog.h"
#include "DolphinQt/Config/SettingsWindow.h" #include "DolphinQt/Config/SettingsWindow.h"
#include "DolphinQt/Debugger/AssemblerWidget.h" #include "DolphinQt/Debugger/AssemblerWidget.h"
#include "DolphinQt/Debugger/BreakpointWidget.h" #include "DolphinQt/Debugger/BreakpointWidget.h"
@ -145,8 +145,6 @@
#undef None #undef None
#endif #endif
#include <qprocess.h>
#if defined(__unix__) || defined(__unix) || defined(__APPLE__) #if defined(__unix__) || defined(__unix) || defined(__APPLE__)
void MainWindow::OnSignal() void MainWindow::OnSignal()
{ {
@ -219,9 +217,9 @@ static std::vector<std::string> StringListToStdVector(QStringList list)
return result; return result;
} }
MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters, MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boot_parameters,
const std::string& movie_path) const std::string& movie_path)
: QMainWindow(nullptr) : QMainWindow(nullptr), m_system(system)
{ {
setWindowTitle(QString::fromStdString(Common::GetScmRevStr())); setWindowTitle(QString::fromStdString(Common::GetScmRevStr()));
setWindowIcon(Resources::GetAppIcon()); setWindowIcon(Resources::GetAppIcon());
@ -275,6 +273,8 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().Init(); AchievementManager::GetInstance().Init();
if (AchievementManager::GetInstance().IsHardcoreModeActive())
Settings::Instance().SetDebugModeEnabled(false);
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS
#if defined(__unix__) || defined(__unix) || defined(__APPLE__) #if defined(__unix__) || defined(__unix) || defined(__APPLE__)
@ -292,7 +292,7 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
if (!movie_path.empty()) if (!movie_path.empty())
{ {
std::optional<std::string> savestate_path; std::optional<std::string> savestate_path;
if (Core::System::GetInstance().GetMovie().PlayInput(movie_path, &savestate_path)) if (m_system.GetMovie().PlayInput(movie_path, &savestate_path))
{ {
m_pending_boot->boot_session_data.SetSavestateData(std::move(savestate_path), m_pending_boot->boot_session_data.SetSavestateData(std::move(savestate_path),
DeleteSavestateAfterBoot::No); DeleteSavestateAfterBoot::No);
@ -465,17 +465,17 @@ void MainWindow::CreateComponents()
m_wii_tas_input_windows[i] = new WiiTASInputWindow(nullptr, i); m_wii_tas_input_windows[i] = new WiiTASInputWindow(nullptr, i);
} }
m_jit_widget = new JITWidget(Core::System::GetInstance(), this); m_jit_widget = new JITWidget(m_system, this);
m_log_widget = new LogWidget(this); m_log_widget = new LogWidget(this);
m_log_config_widget = new LogConfigWidget(this); m_log_config_widget = new LogConfigWidget(this);
m_memory_widget = new MemoryWidget(Core::System::GetInstance(), this); m_memory_widget = new MemoryWidget(m_system, this);
m_network_widget = new NetworkWidget(this); m_network_widget = new NetworkWidget(this);
m_register_widget = new RegisterWidget(this); m_register_widget = new RegisterWidget(this);
m_thread_widget = new ThreadWidget(this); m_thread_widget = new ThreadWidget(this);
m_watch_widget = new WatchWidget(this); m_watch_widget = new WatchWidget(this);
m_breakpoint_widget = new BreakpointWidget(this); m_breakpoint_widget = new BreakpointWidget(this);
m_code_widget = new CodeWidget(this); m_code_widget = new CodeWidget(this);
m_cheats_manager = new CheatsManager(Core::System::GetInstance(), this); m_cheats_manager = new CheatsManager(m_system, this);
m_assembler_widget = new AssemblerWidget(this); m_assembler_widget = new AssemblerWidget(this);
const auto request_watch = [this](QString name, u32 addr) { const auto request_watch = [this](QString name, u32 addr) {
@ -512,7 +512,7 @@ void MainWindow::CreateComponents()
connect(m_memory_widget, &MemoryWidget::RequestWatch, request_watch); connect(m_memory_widget, &MemoryWidget::RequestWatch, request_watch);
connect(m_breakpoint_widget, &BreakpointWidget::ShowCode, [this](u32 address) { connect(m_breakpoint_widget, &BreakpointWidget::ShowCode, [this](u32 address) {
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused) if (Core::GetState(m_system) == Core::State::Paused)
m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate);
}); });
connect(m_breakpoint_widget, &BreakpointWidget::ShowMemory, m_memory_widget, connect(m_breakpoint_widget, &BreakpointWidget::ShowMemory, m_memory_widget,
@ -567,6 +567,8 @@ void MainWindow::ConnectMenuBar()
connect(m_menu_bar, &MenuBar::ImportNANDBackup, this, &MainWindow::OnImportNANDBackup); connect(m_menu_bar, &MenuBar::ImportNANDBackup, this, &MainWindow::OnImportNANDBackup);
connect(m_menu_bar, &MenuBar::PerformOnlineUpdate, this, &MainWindow::PerformOnlineUpdate); connect(m_menu_bar, &MenuBar::PerformOnlineUpdate, this, &MainWindow::PerformOnlineUpdate);
connect(m_menu_bar, &MenuBar::BootWiiSystemMenu, this, &MainWindow::BootWiiSystemMenu); connect(m_menu_bar, &MenuBar::BootWiiSystemMenu, this, &MainWindow::BootWiiSystemMenu);
connect(m_menu_bar, &MenuBar::StartNetPlay, this, &MainWindow::ShowNetPlaySetupDialog);
connect(m_menu_bar, &MenuBar::BrowseNetPlay, this, &MainWindow::ShowNetPlayBrowser);
connect(m_menu_bar, &MenuBar::ShowFIFOPlayer, this, &MainWindow::ShowFIFOPlayer); connect(m_menu_bar, &MenuBar::ShowFIFOPlayer, this, &MainWindow::ShowFIFOPlayer);
connect(m_menu_bar, &MenuBar::ShowSkylanderPortal, this, &MainWindow::ShowSkylanderPortal); connect(m_menu_bar, &MenuBar::ShowSkylanderPortal, this, &MainWindow::ShowSkylanderPortal);
connect(m_menu_bar, &MenuBar::ShowInfinityBase, this, &MainWindow::ShowInfinityBase); connect(m_menu_bar, &MenuBar::ShowInfinityBase, this, &MainWindow::ShowInfinityBase);
@ -651,7 +653,7 @@ void MainWindow::ConnectHotkeys()
connect(m_hotkey_scheduler, &HotkeyScheduler::ConnectWiiRemote, this, connect(m_hotkey_scheduler, &HotkeyScheduler::ConnectWiiRemote, this,
&MainWindow::OnConnectWiiRemote); &MainWindow::OnConnectWiiRemote);
connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleReadOnlyMode, [this] { connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleReadOnlyMode, [this] {
auto& movie = Core::System::GetInstance().GetMovie(); auto& movie = m_system.GetMovie();
bool read_only = !movie.IsReadOnly(); bool read_only = !movie.IsReadOnly();
movie.SetReadOnly(read_only); movie.SetReadOnly(read_only);
emit ReadOnlyModeChanged(read_only); emit ReadOnlyModeChanged(read_only);
@ -696,8 +698,6 @@ void MainWindow::ConnectToolBar()
connect(m_tool_bar, &ToolBar::ControllersPressed, this, &MainWindow::ShowControllersWindow); connect(m_tool_bar, &ToolBar::ControllersPressed, this, &MainWindow::ShowControllersWindow);
connect(m_tool_bar, &ToolBar::GraphicsPressed, this, &MainWindow::ShowGraphicsWindow); connect(m_tool_bar, &ToolBar::GraphicsPressed, this, &MainWindow::ShowGraphicsWindow);
connect(m_tool_bar, &ToolBar::StartNetPlayPressed, this, &MainWindow::ShowNetPlaySetupDialog);
connect(m_tool_bar, &ToolBar::ViewGeckoCodes, this, &MainWindow::ShowGeckoCodes);
connect(m_tool_bar, &ToolBar::StepPressed, m_code_widget, &CodeWidget::Step); connect(m_tool_bar, &ToolBar::StepPressed, m_code_widget, &CodeWidget::Step);
connect(m_tool_bar, &ToolBar::StepOverPressed, m_code_widget, &CodeWidget::StepOver); connect(m_tool_bar, &ToolBar::StepOverPressed, m_code_widget, &CodeWidget::StepOver);
connect(m_tool_bar, &ToolBar::StepOutPressed, m_code_widget, &CodeWidget::StepOut); connect(m_tool_bar, &ToolBar::StepOutPressed, m_code_widget, &CodeWidget::StepOut);
@ -808,14 +808,12 @@ void MainWindow::ChangeDisc()
if (paths.empty()) if (paths.empty())
return; return;
auto& system = Core::System::GetInstance(); m_system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{m_system}, paths);
system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{system}, paths);
} }
void MainWindow::EjectDisc() void MainWindow::EjectDisc()
{ {
auto& system = Core::System::GetInstance(); m_system.GetDVDInterface().EjectDisc(Core::CPUThreadGuard{m_system}, DVD::EjectCause::User);
system.GetDVDInterface().EjectDisc(Core::CPUThreadGuard{system}, DVD::EjectCause::User);
} }
void MainWindow::OpenUserFolder() void MainWindow::OpenUserFolder()
@ -840,9 +838,9 @@ void MainWindow::Play(const std::optional<std::string>& savestate_path)
// Otherwise, play the default game. // Otherwise, play the default game.
// Otherwise, play the last played game, if there is one. // Otherwise, play the last played game, if there is one.
// Otherwise, prompt for a new game. // Otherwise, prompt for a new game.
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused) if (Core::GetState(m_system) == Core::State::Paused)
{ {
Core::SetState(Core::System::GetInstance(), Core::State::Running); Core::SetState(m_system, Core::State::Running);
} }
else else
{ {
@ -870,12 +868,12 @@ void MainWindow::Play(const std::optional<std::string>& savestate_path)
void MainWindow::Pause() void MainWindow::Pause()
{ {
Core::SetState(Core::System::GetInstance(), Core::State::Paused); Core::SetState(m_system, Core::State::Paused);
} }
void MainWindow::TogglePause() void MainWindow::TogglePause()
{ {
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused) if (Core::GetState(m_system) == Core::State::Paused)
{ {
Play(); Play();
} }
@ -918,7 +916,7 @@ void MainWindow::OnStopComplete()
bool MainWindow::RequestStop() bool MainWindow::RequestStop()
{ {
if (Core::IsUninitialized(Core::System::GetInstance())) if (Core::IsUninitialized(m_system))
{ {
Core::QueueHostJob([this](Core::System&) { OnStopComplete(); }, true); Core::QueueHostJob([this](Core::System&) { OnStopComplete(); }, true);
return true; return true;
@ -944,13 +942,13 @@ bool MainWindow::RequestStop()
Common::ScopeGuard confirm_lock([this] { m_stop_confirm_showing = false; }); Common::ScopeGuard confirm_lock([this] { m_stop_confirm_showing = false; });
const Core::State state = Core::GetState(Core::System::GetInstance()); const Core::State state = Core::GetState(m_system);
// Only pause the game, if NetPlay is not running // Only pause the game, if NetPlay is not running
bool pause = !Settings::Instance().GetNetPlayClient(); bool pause = !Settings::Instance().GetNetPlayClient();
if (pause) if (pause)
Core::SetState(Core::System::GetInstance(), Core::State::Paused); Core::SetState(m_system, Core::State::Paused);
if (rendered_widget_was_active) if (rendered_widget_was_active)
{ {
@ -980,7 +978,7 @@ bool MainWindow::RequestStop()
m_render_widget->SetWaitingForMessageBox(false); m_render_widget->SetWaitingForMessageBox(false);
if (pause) if (pause)
Core::SetState(Core::System::GetInstance(), state); Core::SetState(m_system, state);
return false; return false;
} }
@ -1001,112 +999,8 @@ bool MainWindow::RequestStop()
// Unpause because gracefully shutting down needs the game to actually request a shutdown. // Unpause because gracefully shutting down needs the game to actually request a shutdown.
// TODO: Do not unpause in debug mode to allow debugging until the complete shutdown. // TODO: Do not unpause in debug mode to allow debugging until the complete shutdown.
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused) if (Core::GetState(m_system) == Core::State::Paused)
Core::SetState(Core::System::GetInstance(), Core::State::Running); Core::SetState(m_system, Core::State::Running);
// Tell NetPlay about the power event
if (NetPlay::IsNetPlayRunning())
NetPlay::SendPowerButtonEvent();
return true;
}
ForceStop();
#ifdef Q_OS_WIN
// Allow windows to idle or turn off display again
SetThreadExecutionState(ES_CONTINUOUS);
#endif
return true;
}
bool MainWindow::RequestStopNetplay()
{
if (!Core::IsRunning(Core::System::GetInstance()))
{
Core::QueueHostJob([this](Core::System&) { OnStopComplete(); }, true);
return true;
}
const bool rendered_widget_was_active =
m_render_widget->isActiveWindow() && !m_render_widget->isFullScreen();
QWidget* confirm_parent = (!m_rendering_to_main && rendered_widget_was_active) ?
m_render_widget :
static_cast<QWidget*>(this);
const bool was_cursor_locked = m_render_widget->IsCursorLocked();
if (!m_render_widget->isFullScreen())
m_render_widget_geometry = m_render_widget->saveGeometry();
else
FullScreen();
if (Config::Get(Config::MAIN_CONFIRM_ON_STOP))
{
if (std::exchange(m_stop_confirm_showing, true))
return true;
Common::ScopeGuard confirm_lock([this] { m_stop_confirm_showing = false; });
const Core::State state = Core::GetState(Core::System::GetInstance());
// Only pause the game, if NetPlay is not running
bool pause = !Settings::Instance().GetNetPlayClient();
if (pause)
Core::SetState(Core::System::GetInstance(), Core::State::Paused);
if (rendered_widget_was_active)
{
// We have to do this before creating the message box, otherwise we might receive the window
// activation event before we know we need to lock the cursor again.
m_render_widget->SetCursorLockedOnNextActivation(was_cursor_locked);
}
// This is to avoid any "race conditions" between the "Window Activate" message and the
// message box returning, which could break cursor locking depending on the order
m_render_widget->SetWaitingForMessageBox(true);
auto confirm = ModalMessageBox::question(
confirm_parent, tr("Quitter!"),
m_stop_requested ? tr("A user closed down their game from the netplay lobby. "
"This means the Netplay session has ended! "
"Do you want to stop the current emulation?") :
tr("A user closed down their game from the netplay lobby. "
"This means the Netplay session has ended! "
"Do you want to stop the current emulation?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton, Qt::ApplicationModal);
// If a user confirmed stopping the emulation, we do not capture the cursor again,
// even if the render widget will stay alive for a while.
// If a used rejected stopping the emulation, we instead capture the cursor again,
// and let them continue playing as if nothing had happened
// (assuming cursor locking is on).
if (confirm != QMessageBox::Yes)
{
m_render_widget->SetWaitingForMessageBox(false);
if (pause)
Core::SetState(Core::System::GetInstance(), state);
return false;
}
else
{
m_render_widget->SetCursorLockedOnNextActivation(false);
// This needs to be after SetCursorLockedOnNextActivation(false) as it depends on it
m_render_widget->SetWaitingForMessageBox(false);
}
}
OnStopRecording();
// TODO: Add Debugger shutdown
if (!m_stop_requested && UICommon::TriggerSTMPowerEvent())
{
m_stop_requested = true;
// Unpause because gracefully shutting down needs the game to actually request a shutdown.
// TODO: Do not unpause in debug mode to allow debugging until the complete shutdown.
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused)
Core::SetState(Core::System::GetInstance(), Core::State::Running);
// Tell NetPlay about the power event // Tell NetPlay about the power event
if (NetPlay::IsNetPlayRunning()) if (NetPlay::IsNetPlayRunning())
@ -1125,21 +1019,20 @@ bool MainWindow::RequestStopNetplay()
void MainWindow::ForceStop() void MainWindow::ForceStop()
{ {
Core::Stop(Core::System::GetInstance()); Core::Stop(m_system);
} }
void MainWindow::Reset() void MainWindow::Reset()
{ {
auto& system = Core::System::GetInstance(); auto& movie = m_system.GetMovie();
auto& movie = system.GetMovie();
if (movie.IsRecordingInput()) if (movie.IsRecordingInput())
movie.SetReset(true); movie.SetReset(true);
system.GetProcessorInterface().ResetButton_Tap_FromUser(); m_system.GetProcessorInterface().ResetButton_Tap();
} }
void MainWindow::FrameAdvance() void MainWindow::FrameAdvance()
{ {
Core::DoFrameStep(Core::System::GetInstance()); Core::DoFrameStep(m_system);
} }
void MainWindow::FullScreen() void MainWindow::FullScreen()
@ -1230,7 +1123,7 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
} }
// If we're running, only start a new game once we've stopped the last. // If we're running, only start a new game once we've stopped the last.
if (!Core::IsUninitialized(Core::System::GetInstance())) if (!Core::IsUninitialized(m_system))
{ {
if (!RequestStop()) if (!RequestStop())
return; return;
@ -1244,7 +1137,7 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
ShowRenderWidget(); ShowRenderWidget();
// Boot up, show an error if it fails to load the game. // Boot up, show an error if it fails to load the game.
if (!BootManager::BootCore(Core::System::GetInstance(), std::move(parameters), if (!BootManager::BootCore(m_system, std::move(parameters),
::GetWindowSystemInfo(m_render_widget->windowHandle()))) ::GetWindowSystemInfo(m_render_widget->windowHandle())))
{ {
ModalMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok); ModalMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok);
@ -1400,7 +1293,7 @@ void MainWindow::ShowSettingsWindow()
InstallHotkeyFilter(m_settings_window); InstallHotkeyFilter(m_settings_window);
} }
//SetQWidgetWindowDecorations(m_settings_window); SetQWidgetWindowDecorations(m_settings_window);
m_settings_window->show(); m_settings_window->show();
m_settings_window->raise(); m_settings_window->raise();
m_settings_window->activateWindow(); m_settings_window->activateWindow();
@ -1483,8 +1376,7 @@ void MainWindow::ShowFIFOPlayer()
{ {
if (!m_fifo_window) if (!m_fifo_window)
{ {
m_fifo_window = new FIFOPlayerWindow(Core::System::GetInstance().GetFifoPlayer(), m_fifo_window = new FIFOPlayerWindow(m_system.GetFifoPlayer(), m_system.GetFifoRecorder());
Core::System::GetInstance().GetFifoRecorder());
connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this, connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this,
[this](const QString& path) { StartGame(path, ScanForSecondDisc::No); }); [this](const QString& path) { StartGame(path, ScanForSecondDisc::No); });
} }
@ -1530,7 +1422,7 @@ void MainWindow::StateLoad()
this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)"));
Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString()); Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString());
if (!path.isEmpty()) if (!path.isEmpty())
State::LoadAs(Core::System::GetInstance(), path.toStdString()); State::LoadAs(m_system, path.toStdString());
} }
void MainWindow::StateSave() void MainWindow::StateSave()
@ -1542,47 +1434,47 @@ void MainWindow::StateSave()
this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)"));
Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString()); Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString());
if (!path.isEmpty()) if (!path.isEmpty())
State::SaveAs(Core::System::GetInstance(), path.toStdString()); State::SaveAs(m_system, path.toStdString());
} }
void MainWindow::StateLoadSlot() void MainWindow::StateLoadSlot()
{ {
State::Load(Core::System::GetInstance(), m_state_slot); State::Load(m_system, m_state_slot);
} }
void MainWindow::StateSaveSlot() void MainWindow::StateSaveSlot()
{ {
State::Save(Core::System::GetInstance(), m_state_slot); State::Save(m_system, m_state_slot);
} }
void MainWindow::StateLoadSlotAt(int slot) void MainWindow::StateLoadSlotAt(int slot)
{ {
State::Load(Core::System::GetInstance(), slot); State::Load(m_system, slot);
} }
void MainWindow::StateLoadLastSavedAt(int slot) void MainWindow::StateLoadLastSavedAt(int slot)
{ {
State::LoadLastSaved(Core::System::GetInstance(), slot); State::LoadLastSaved(m_system, slot);
} }
void MainWindow::StateSaveSlotAt(int slot) void MainWindow::StateSaveSlotAt(int slot)
{ {
State::Save(Core::System::GetInstance(), slot); State::Save(m_system, slot);
} }
void MainWindow::StateLoadUndo() void MainWindow::StateLoadUndo()
{ {
State::UndoLoadState(Core::System::GetInstance()); State::UndoLoadState(m_system);
} }
void MainWindow::StateSaveUndo() void MainWindow::StateSaveUndo()
{ {
State::UndoSaveState(Core::System::GetInstance()); State::UndoSaveState(m_system);
} }
void MainWindow::StateSaveOldest() void MainWindow::StateSaveOldest()
{ {
State::SaveFirstSaved(Core::System::GetInstance()); State::SaveFirstSaved(m_system);
} }
void MainWindow::SetStateSlot(int slot) void MainWindow::SetStateSlot(int slot)
@ -1636,11 +1528,10 @@ void MainWindow::NetPlayInit()
m_netplay_discord = new DiscordHandler(this); m_netplay_discord = new DiscordHandler(this);
#endif #endif
connect(m_netplay_dialog, &NetPlayDialog::Stop, this, &MainWindow::RequestStopNetplay); connect(m_netplay_dialog, &NetPlayDialog::Stop, this, &MainWindow::ForceStop);
connect(m_netplay_dialog, &NetPlayDialog::rejected, this, &MainWindow::NetPlayQuit); connect(m_netplay_dialog, &NetPlayDialog::rejected, this, &MainWindow::NetPlayQuit);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Join, this, &MainWindow::NetPlayJoin); connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Join, this, &MainWindow::NetPlayJoin);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Host, this, &MainWindow::NetPlayHost); connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Host, this, &MainWindow::NetPlayHost);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::JoinBrowser, this, &MainWindow::NetPlayJoin);
#ifdef USE_DISCORD_PRESENCE #ifdef USE_DISCORD_PRESENCE
connect(m_netplay_discord, &DiscordHandler::Join, this, &MainWindow::NetPlayJoin); connect(m_netplay_discord, &DiscordHandler::Join, this, &MainWindow::NetPlayJoin);
@ -1655,7 +1546,7 @@ void MainWindow::NetPlayInit()
bool MainWindow::NetPlayJoin() bool MainWindow::NetPlayJoin()
{ {
if (!Core::IsUninitialized(Core::System::GetInstance())) if (!Core::IsUninitialized(m_system))
{ {
ModalMessageBox::critical(nullptr, tr("Error"), ModalMessageBox::critical(nullptr, tr("Error"),
tr("Can't start a NetPlay Session while a game is still running!")); tr("Can't start a NetPlay Session while a game is still running!"));
@ -1722,7 +1613,7 @@ bool MainWindow::NetPlayJoin()
bool MainWindow::NetPlayHost(const UICommon::GameFile& game) bool MainWindow::NetPlayHost(const UICommon::GameFile& game)
{ {
if (!Core::IsUninitialized(Core::System::GetInstance())) if (!Core::IsUninitialized(m_system))
{ {
ModalMessageBox::critical(nullptr, tr("Error"), ModalMessageBox::critical(nullptr, tr("Error"),
tr("Can't start a NetPlay Session while a game is still running!")); tr("Can't start a NetPlay Session while a game is still running!"));
@ -1784,7 +1675,7 @@ void MainWindow::NetPlayQuit()
void MainWindow::UpdateScreenSaverInhibition() void MainWindow::UpdateScreenSaverInhibition()
{ {
const bool inhibit = Config::Get(Config::MAIN_DISABLE_SCREENSAVER) && const bool inhibit = Config::Get(Config::MAIN_DISABLE_SCREENSAVER) &&
(Core::GetState(Core::System::GetInstance()) == Core::State::Running); (Core::GetState(m_system) == Core::State::Running);
if (inhibit == m_is_screensaver_inhibited) if (inhibit == m_is_screensaver_inhibited)
return; return;
@ -1945,7 +1836,7 @@ void MainWindow::OnPlayRecording()
if (dtm_file.isEmpty()) if (dtm_file.isEmpty())
return; return;
auto& movie = Core::System::GetInstance().GetMovie(); auto& movie = m_system.GetMovie();
if (!movie.IsReadOnly()) if (!movie.IsReadOnly())
{ {
// let's make the read-only flag consistent at the start of a movie. // let's make the read-only flag consistent at the start of a movie.
@ -1964,10 +1855,9 @@ void MainWindow::OnPlayRecording()
void MainWindow::OnStartRecording() void MainWindow::OnStartRecording()
{ {
auto& system = Core::System::GetInstance(); auto& movie = m_system.GetMovie();
auto& movie = system.GetMovie(); if (Core::GetState(m_system) == Core::State::Starting ||
if (Core::GetState(system) == Core::State::Starting || Core::GetState(m_system) == Core::State::Stopping || movie.IsRecordingInput() ||
Core::GetState(system) == Core::State::Stopping || movie.IsRecordingInput() ||
movie.IsPlayingInput()) movie.IsPlayingInput())
{ {
return; return;
@ -1999,14 +1889,14 @@ void MainWindow::OnStartRecording()
{ {
emit RecordingStatusChanged(true); emit RecordingStatusChanged(true);
if (Core::IsUninitialized(system)) if (Core::IsUninitialized(m_system))
Play(); Play();
} }
} }
void MainWindow::OnStopRecording() void MainWindow::OnStopRecording()
{ {
auto& movie = Core::System::GetInstance().GetMovie(); auto& movie = m_system.GetMovie();
if (movie.IsRecordingInput()) if (movie.IsRecordingInput())
OnExportRecording(); OnExportRecording();
if (movie.IsMovieActive()) if (movie.IsMovieActive())
@ -2016,13 +1906,12 @@ void MainWindow::OnStopRecording()
void MainWindow::OnExportRecording() void MainWindow::OnExportRecording()
{ {
auto& system = Core::System::GetInstance(); const Core::CPUThreadGuard guard(m_system);
const Core::CPUThreadGuard guard(system);
QString dtm_file = DolphinFileDialog::getSaveFileName( QString dtm_file = DolphinFileDialog::getSaveFileName(
this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)")); this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)"));
if (!dtm_file.isEmpty()) if (!dtm_file.isEmpty())
system.GetMovie().SaveRecording(dtm_file.toStdString()); m_system.GetMovie().SaveRecording(dtm_file.toStdString());
} }
void MainWindow::OnActivateChat() void MainWindow::OnActivateChat()
@ -2060,11 +1949,10 @@ void MainWindow::ShowTASInput()
} }
} }
auto& system = Core::System::GetInstance();
for (int i = 0; i < num_wii_controllers; i++) for (int i = 0; i < num_wii_controllers; i++)
{ {
if (Config::Get(Config::GetInfoForWiimoteSource(i)) == WiimoteSource::Emulated && if (Config::Get(Config::GetInfoForWiimoteSource(i)) == WiimoteSource::Emulated &&
(!Core::IsRunning(system) || system.IsWii())) (!Core::IsRunning(m_system) || m_system.IsWii()))
{ {
SetQWidgetWindowDecorations(m_wii_tas_input_windows[i]); SetQWidgetWindowDecorations(m_wii_tas_input_windows[i]);
m_wii_tas_input_windows[i]->show(); m_wii_tas_input_windows[i]->show();
@ -2076,7 +1964,7 @@ void MainWindow::ShowTASInput()
void MainWindow::OnConnectWiiRemote(int id) void MainWindow::OnConnectWiiRemote(int id)
{ {
const Core::CPUThreadGuard guard(Core::System::GetInstance()); const Core::CPUThreadGuard guard(m_system);
if (const auto bt = WiiUtils::GetBluetoothEmuDevice()) if (const auto bt = WiiUtils::GetBluetoothEmuDevice())
{ {
const auto wm = bt->AccessWiimoteByIndex(id); const auto wm = bt->AccessWiimoteByIndex(id);
@ -2157,16 +2045,3 @@ void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game)
AddRiivolutionPatches(boot_params.get(), std::move(w.GetPatches())); AddRiivolutionPatches(boot_params.get(), std::move(w.GetPatches()));
StartGame(std::move(boot_params)); StartGame(std::move(boot_params));
} }
void MainWindow::ShowGeckoCodes()
{
if (!m_gecko_dialog)
{
m_gecko_dialog = new GeckoDialog(this);
InstallHotkeyFilter(m_gecko_dialog);
}
m_gecko_dialog->show();
m_gecko_dialog->raise();
m_gecko_dialog->activateWindow();
}

View file

@ -55,6 +55,11 @@ class WatchWidget;
class WiiTASInputWindow; class WiiTASInputWindow;
struct WindowSystemInfo; struct WindowSystemInfo;
namespace Core
{
class System;
}
namespace DiscIO namespace DiscIO
{ {
enum class Region; enum class Region;
@ -75,7 +80,7 @@ class MainWindow final : public QMainWindow
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(std::unique_ptr<BootParameters> boot_parameters, explicit MainWindow(Core::System& system, std::unique_ptr<BootParameters> boot_parameters,
const std::string& movie_path); const std::string& movie_path);
~MainWindow(); ~MainWindow();
@ -216,6 +221,8 @@ private:
QSize sizeHint() const override; QSize sizeHint() const override;
void ShowGeckoCodes(); void ShowGeckoCodes();
Core::System& m_system;
#ifdef HAVE_XRANDR #ifdef HAVE_XRANDR
std::unique_ptr<X11Utils::XRRConfiguration> m_xrr_config; std::unique_ptr<X11Utils::XRRConfiguration> m_xrr_config;
#endif #endif

View file

@ -0,0 +1,22 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/QtUtils/QtUtils.h"
#include <QDateTimeEdit>
namespace QtUtils
{
void ShowFourDigitYear(QDateTimeEdit* widget)
{
if (!widget->displayFormat().contains(QStringLiteral("yyyy")))
{
// Always show the full year, no matter what the locale specifies. Otherwise, two-digit years
// will always be interpreted as in the 21st century.
widget->setDisplayFormat(
widget->displayFormat().replace(QStringLiteral("yy"), QStringLiteral("yyyy")));
}
}
} // namespace QtUtils

View file

@ -0,0 +1,13 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
class QDateTimeEdit;
namespace QtUtils
{
void ShowFourDigitYear(QDateTimeEdit* widget);
}

View file

@ -24,6 +24,7 @@
#include "Core/System.h" #include "Core/System.h"
#include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/QtUtils/QtUtils.h"
#include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
@ -168,13 +169,7 @@ void AdvancedPane::CreateLayout()
m_custom_rtc_datetime->setDisplayFormat(m_custom_rtc_datetime->displayFormat().replace( m_custom_rtc_datetime->setDisplayFormat(m_custom_rtc_datetime->displayFormat().replace(
QStringLiteral("mm"), QStringLiteral("mm:ss"))); QStringLiteral("mm"), QStringLiteral("mm:ss")));
if (!m_custom_rtc_datetime->displayFormat().contains(QStringLiteral("yyyy"))) QtUtils::ShowFourDigitYear(m_custom_rtc_datetime);
{
// Always show the full year, no matter what the locale specifies. Otherwise, two-digit years
// will always be interpreted as in the 21st century.
m_custom_rtc_datetime->setDisplayFormat(m_custom_rtc_datetime->displayFormat().replace(
QStringLiteral("yy"), QStringLiteral("yyyy")));
}
m_custom_rtc_datetime->setDateTimeRange(QDateTime({2000, 1, 1}, {0, 0, 0}, Qt::UTC), m_custom_rtc_datetime->setDateTimeRange(QDateTime({2000, 1, 1}, {0, 0, 0}, Qt::UTC),
QDateTime({2099, 12, 31}, {23, 59, 59}, Qt::UTC)); QDateTime({2099, 12, 31}, {23, 59, 59}, Qt::UTC));
m_custom_rtc_datetime->setTimeSpec(Qt::UTC); m_custom_rtc_datetime->setTimeSpec(Qt::UTC);

View file

@ -163,7 +163,7 @@ void AudioPane::CreateWidgets()
auto* misc_layout = new QGridLayout; auto* misc_layout = new QGridLayout;
misc_box->setLayout(misc_layout); misc_box->setLayout(misc_layout);
m_speed_up_mute_enable = new QCheckBox(tr("Mute When Disabling Speed Limit.")); m_speed_up_mute_enable = new QCheckBox(tr("Mute When Disabling Speed Limit"));
m_speed_up_mute_enable->setToolTip( m_speed_up_mute_enable->setToolTip(
tr("Mutes the audio when overriding the emulation speed limit (default hotkey: Tab).")); tr("Mutes the audio when overriding the emulation speed limit (default hotkey: Tab)."));

View file

@ -17,6 +17,7 @@
#include "Core/IOS/USB/Emulated/Skylanders/Skylander.h" #include "Core/IOS/USB/Emulated/Skylanders/Skylander.h"
#include "Core/System.h" #include "Core/System.h"
#include "DolphinQt/QtUtils/QtUtils.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h" #include "DolphinQt/QtUtils/SetWindowDecorations.h"
SkylanderModifyDialog::SkylanderModifyDialog(QWidget* parent, u8 slot) SkylanderModifyDialog::SkylanderModifyDialog(QWidget* parent, u8 slot)
@ -168,8 +169,9 @@ void SkylanderModifyDialog::PopulateSkylanderOptions(QVBoxLayout* layout)
edit_nick->setValidator( edit_nick->setValidator(
new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^\\p{L}{0,15}$")), this)); new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^\\p{L}{0,15}$")), this));
edit_playtime->setValidator(new QIntValidator(0, INT_MAX, this)); edit_playtime->setValidator(new QIntValidator(0, INT_MAX, this));
edit_last_reset->setDisplayFormat(QStringLiteral("dd/MM/yyyy hh:mm"));
edit_last_placed->setDisplayFormat(QStringLiteral("dd/MM/yyyy hh:mm")); QtUtils::ShowFourDigitYear(edit_last_reset);
QtUtils::ShowFourDigitYear(edit_last_placed);
edit_toy_code->setToolTip(tr("The toy code for this figure. Only available for real figures.")); edit_toy_code->setToolTip(tr("The toy code for this figure. Only available for real figures."));
edit_money->setToolTip(tr("The amount of money this Skylander has. Between 0 and 65000")); edit_money->setToolTip(tr("The amount of money this Skylander has. Between 0 and 65000"));

Some files were not shown because too many files have changed in this diff Show more