mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d683010a8b | |||
| ec254a3adb | |||
| 5e10da3cac | |||
| 5322bce4b8 | |||
| 276dcdd8ea | |||
| 59254cd1e7 | |||
| 9a3af3a6a3 | |||
| b473c18d6e | |||
| c984c387d7 | |||
| 5856beac54 | |||
| 16e7e034d7 | |||
| 47c6a73971 | |||
| 813a35abca |
@@ -1,55 +0,0 @@
|
||||
diff --git a/include/mcl/assert.hpp b/include/mcl/assert.hpp
|
||||
index f77dbe7..9ec0b9c 100644
|
||||
--- a/include/mcl/assert.hpp
|
||||
+++ b/include/mcl/assert.hpp
|
||||
@@ -23,8 +23,11 @@ template<typename... Ts>
|
||||
|
||||
} // namespace mcl::detail
|
||||
|
||||
+#ifndef UNREACHABLE
|
||||
#define UNREACHABLE() ASSERT_FALSE("Unreachable code!")
|
||||
+#endif
|
||||
|
||||
+#ifndef ASSERT
|
||||
#define ASSERT(expr) \
|
||||
[&] { \
|
||||
if (std::is_constant_evaluated()) { \
|
||||
@@ -37,7 +40,9 @@ template<typename... Ts>
|
||||
} \
|
||||
} \
|
||||
}()
|
||||
+#endif
|
||||
|
||||
+#ifndef ASSERT_MSG
|
||||
#define ASSERT_MSG(expr, ...) \
|
||||
[&] { \
|
||||
if (std::is_constant_evaluated()) { \
|
||||
@@ -50,13 +55,24 @@ template<typename... Ts>
|
||||
} \
|
||||
} \
|
||||
}()
|
||||
+#endif
|
||||
|
||||
+#ifndef ASSERT_FALSE
|
||||
#define ASSERT_FALSE(...) ::mcl::detail::assert_terminate("false", __VA_ARGS__)
|
||||
+#endif
|
||||
|
||||
#if defined(NDEBUG) || defined(MCL_IGNORE_ASSERTS)
|
||||
-# define DEBUG_ASSERT(expr) ASSUME(expr)
|
||||
-# define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
|
||||
+# ifndef DEBUG_ASSERT
|
||||
+# define DEBUG_ASSERT(expr) ASSUME(expr)
|
||||
+# endif
|
||||
+# ifndef DEBUG_ASSERT_MSG
|
||||
+# define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
|
||||
+# endif
|
||||
#else
|
||||
-# define DEBUG_ASSERT(expr) ASSERT(expr)
|
||||
-# define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
|
||||
+# ifndef DEBUG_ASSERT
|
||||
+# define DEBUG_ASSERT(expr) ASSERT(expr)
|
||||
+# endif
|
||||
+# ifndef DEBUG_ASSERT_MSG
|
||||
+# define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
|
||||
+# endif
|
||||
#endif
|
||||
@@ -526,7 +526,6 @@ if (NOT YUZU_STATIC_ROOM)
|
||||
find_package(SPIRV-Tools)
|
||||
find_package(sirit)
|
||||
find_package(gamemode)
|
||||
find_package(mcl)
|
||||
find_package(frozen)
|
||||
|
||||
if (ARCHITECTURE_riscv64)
|
||||
|
||||
@@ -76,7 +76,6 @@ Certain other dependencies will be fetched by CPM regardless. System packages *c
|
||||
* This package is known to be broken on the AUR.
|
||||
* [cpp-jwt](https://github.com/arun11299/cpp-jwt) 1.4+ - if `ENABLE_WEB_SERVICE` is on
|
||||
* [unordered-dense](https://github.com/martinus/unordered_dense)
|
||||
* [mcl](https://github.com/azahar-emu/mcl) - subject to removal
|
||||
|
||||
On amd64:
|
||||
|
||||
|
||||
Vendored
-3
@@ -76,9 +76,6 @@ if (ARCHITECTURE_riscv64)
|
||||
AddJsonPackage(biscuit)
|
||||
endif()
|
||||
|
||||
# mcl
|
||||
AddJsonPackage(mcl)
|
||||
|
||||
# Vulkan stuff
|
||||
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
|
||||
|
||||
|
||||
Vendored
-12
@@ -208,18 +208,6 @@
|
||||
"version": "0.9.1",
|
||||
"git_version": "0.19.0"
|
||||
},
|
||||
"mcl": {
|
||||
"version": "0.1.12",
|
||||
"repo": "azahar-emu/mcl",
|
||||
"sha": "7b08d83418",
|
||||
"hash": "9c6ba624cb22ef622f78046a82abb99bf5026284ba17dfacaf46ac842cbd3b0f515f5ba45a1598c7671318a78a2e648db72ce8d10e7537f34e39800bdcb57694",
|
||||
"options": [
|
||||
"MCL_INSTALL OFF"
|
||||
],
|
||||
"patches": [
|
||||
"0001-assert-macro.patch"
|
||||
]
|
||||
},
|
||||
"libusb": {
|
||||
"repo": "libusb/libusb",
|
||||
"tag": "v%VERSION%",
|
||||
|
||||
@@ -133,8 +133,11 @@ class GamePropertiesFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
val isChangingConfigurations = activity?.isChangingConfigurations == true
|
||||
super.onDestroy()
|
||||
gamesViewModel.reloadGames(true)
|
||||
if (!isChangingConfigurations) {
|
||||
gamesViewModel.reloadGames(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPlayTime() {
|
||||
|
||||
@@ -473,8 +473,6 @@
|
||||
<string name="renderer_accuracy_description">يتحكم في وضع محاكاة وحدة معالجة الرسومات. تعمل معظم الألعاب بشكل جيد مع وضعي سريع أو متوازن، لكن الوضع الدقيق لا يزال مطلوبًا لبعض الألعاب. تميل الجسيمات إلى العرض بشكل صحيح فقط عند استخدام الوضع الدقيق.</string>
|
||||
<string name="dma_accuracy">دقة DMA</string>
|
||||
<string name="dma_accuracy_description">يتحكم في دقة DMA. يمكن أن تؤدي الدقة الآمنة إلى حل المشكلات في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا لم تكن متأكدًا، فاترك هذا الخيار على الإعداد الافتراضي.</string>
|
||||
<string name="frame_pacing_mode">وضع توقيت الإطارات</string>
|
||||
<string name="frame_pacing_mode_description">يتحكم في كيفية إدارة المحاكي لسرعة الإطارات لتقليل التقطع وجعل معدل الإطارات أكثر سلاسة واتساقًا.</string>
|
||||
<string name="anisotropic_filtering">تصفية متباينة الخواص</string>
|
||||
<string name="anisotropic_filtering_description">يحسن جودة الأنسجة عند عرضها بزوايا مائلة</string>
|
||||
<string name="vram_usage_mode">وضع استخدام ذاكرة VRAM</string>
|
||||
@@ -999,13 +997,6 @@
|
||||
<string name="dma_accuracy_unsafe">غير آمن</string>
|
||||
<string name="dma_accuracy_safe">آمن</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">تلقائي</string>
|
||||
<string name="frame_pacing_mode_target_30">30 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_60">60 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_90">90 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_120">120 إطارًا في الثانية</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">غير مضغوط</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -452,8 +452,6 @@
|
||||
<string name="renderer_accuracy_description">Určuje režim emulovaného GPU. Většina her běží bez problémů v rychlém, nebo vyváženém režimu, ale některé stále vyžadují přesný režim. Částicové efekty se většinou zobrazují korektně pouze v přesném režimu. </string>
|
||||
<string name="dma_accuracy">Přesnost DMA</string>
|
||||
<string name="dma_accuracy_description">Ovládá přesnost DMA. Bezpečná přesnost může vyřešit problémy v některých hrách, ale v některých případech může také ovlivnit výkon. Pokud si nejste jisti, použijte výchozí nastavení.</string>
|
||||
<string name="frame_pacing_mode">Režim Framepacingu</string>
|
||||
<string name="frame_pacing_mode_description">Řídí způsob jakým emulátor spravuje časování snímku aby snížil trhání a zlepšení plynulost a stabilitu snímkové frekvence.</string>
|
||||
<string name="anisotropic_filtering">Anizotropní filtrování</string>
|
||||
<string name="anisotropic_filtering_description">Zlepšuje kvalitu textur při pohledu pod úhlem</string>
|
||||
<string name="vram_usage_mode">Režim využití VRAM</string>
|
||||
|
||||
@@ -467,8 +467,6 @@
|
||||
<string name="renderer_accuracy_description">Controla el modo de la emulación de la GPU. La mayoría de los juegos se renderizan correctamente en los modos Rápido o Equilibrado, pero algunos requieren Preciso. Las partículas tienden a renderizarse correctamente solo con el modo Preciso.</string>
|
||||
<string name="dma_accuracy">Precisión de DMA</string>
|
||||
<string name="dma_accuracy_description">Controla la precisión de DMA. La precisión segura puede solucionar problemas en algunos juegos, pero también puede afectar al rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.</string>
|
||||
<string name="frame_pacing_mode">Modo de ritmo de fotogramas</string>
|
||||
<string name="frame_pacing_mode_description">Controla cómo el emulador gestiona el ritmo de los fotogramas para reducir los tirones y hacer que la velocidad de los fotogramas sea más suave y consistente.</string>
|
||||
<string name="anisotropic_filtering">Filtrado anisotrópico</string>
|
||||
<string name="anisotropic_filtering_description">Mejora la calidad de las texturas al ser observadas desde ángulos oblicuos</string>
|
||||
<string name="vram_usage_mode">Modo de uso de VRAM</string>
|
||||
@@ -993,13 +991,6 @@
|
||||
<string name="dma_accuracy_unsafe">Inseguro</string>
|
||||
<string name="dma_accuracy_safe">Seguro</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Automático</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Sin compresión</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -468,8 +468,6 @@
|
||||
<string name="renderer_accuracy_description">Управляет режимом эмуляции графического процессора. Большинство игр нормально отображаются в режимах «Быстрый» или «Сбалансированный», но для некоторых требуется режим «Точный». Частицы обычно корректно отображаются только в режиме «Точный».</string>
|
||||
<string name="dma_accuracy">Точность DMA</string>
|
||||
<string name="dma_accuracy_description">Управляет точностью DMA. Безопасная точность может исправить проблемы в некоторых играх, но в некоторых случаях также может повлиять на производительность. Если не уверены, оставьте значение По умолчанию.</string>
|
||||
<string name="frame_pacing_mode">Режим синхронизации кадров</string>
|
||||
<string name="frame_pacing_mode_description">Управляет синхронизацией кадров в эмуляторе для уменьшения рывков и обеспечения более плавной и стабильной частоты кадров.</string>
|
||||
<string name="anisotropic_filtering">Анизотропная фильтрация</string>
|
||||
<string name="anisotropic_filtering_description">Улучшает качество текстур под углом</string>
|
||||
<string name="vram_usage_mode">Режим VRAM</string>
|
||||
@@ -994,13 +992,6 @@
|
||||
<string name="dma_accuracy_unsafe">Небезопасно</string>
|
||||
<string name="dma_accuracy_safe">Безопасный</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Авто</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Без сжатия</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -469,8 +469,6 @@
|
||||
<string name="renderer_accuracy_description">Керує режимом емуляції ГП. Більшість ігор добре візуалізуються з режимами «Швидко» або «Збалансовано», але деякі ігри можуть потребувати режиму «Точно». Частинки зазвичай правильно візуалізуються лише з режимом «Точно».</string>
|
||||
<string name="dma_accuracy">Точність DMA</string>
|
||||
<string name="dma_accuracy_description">Керує точністю DMA. Безпечна точність може виправити проблеми в деяких іграх, але в деяких випадках також може вплинути на продуктивність. Якщо не впевнені, залиште це значення за замовчуванням.</string>
|
||||
<string name="frame_pacing_mode">Режим виведення кадрів</string>
|
||||
<string name="frame_pacing_mode_description">Керує тим, як емулятор виконує виведення кадрів, щоб зменшити затримки й забезпечити плавнішу й стабільнішу частоту кадрів.</string>
|
||||
<string name="anisotropic_filtering">Анізотропне фільтрування</string>
|
||||
<string name="anisotropic_filtering_description">Покращує якість текстур під кутом.</string>
|
||||
<string name="vram_usage_mode">Режим використання VRAM</string>
|
||||
@@ -995,13 +993,6 @@
|
||||
<string name="dma_accuracy_unsafe">Небезпечно</string>
|
||||
<string name="dma_accuracy_safe">Безпечно</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Автоматично</string>
|
||||
<string name="frame_pacing_mode_target_30">30 к/с</string>
|
||||
<string name="frame_pacing_mode_target_60">60 к/с</string>
|
||||
<string name="frame_pacing_mode_target_90">90 к/с</string>
|
||||
<string name="frame_pacing_mode_target_120">120 к/с</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Без стиснення</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -462,8 +462,6 @@
|
||||
<string name="renderer_accuracy_description">控制 GPU 模拟的精确度。大部分游戏在性能或平衡模式下可以正常渲染,但部分游戏需要设置为精确。粒子效果通常只有在精确模式下才能正确显示。</string>
|
||||
<string name="dma_accuracy">DMA 精度</string>
|
||||
<string name="dma_accuracy_description">控制 DMA 精度。安全精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。</string>
|
||||
<string name="frame_pacing_mode">帧同步模式</string>
|
||||
<string name="frame_pacing_mode_description">控制模拟器如何管理帧同步,以减少卡顿,使帧率表现更加平稳顺滑。</string>
|
||||
<string name="anisotropic_filtering">各向异性过滤</string>
|
||||
<string name="anisotropic_filtering_description">提高斜角的纹理质量</string>
|
||||
<string name="vram_usage_mode">显存使用模式</string>
|
||||
@@ -988,13 +986,6 @@
|
||||
<string name="dma_accuracy_unsafe">不安全</string>
|
||||
<string name="dma_accuracy_safe">安全</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">自动</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">不压缩</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -533,20 +533,6 @@
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="framePacingModeNames">
|
||||
<item>@string/frame_pacing_mode_target_Auto</item>
|
||||
<item>@string/frame_pacing_mode_target_30</item>
|
||||
<item>@string/frame_pacing_mode_target_60</item>
|
||||
<item>@string/frame_pacing_mode_target_90</item>
|
||||
<item>@string/frame_pacing_mode_target_120</item>
|
||||
</string-array>
|
||||
<integer-array name="framePacingModeValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="appletEntries">
|
||||
<item>@string/applet_hle</item>
|
||||
|
||||
@@ -479,8 +479,6 @@
|
||||
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
|
||||
<string name="dma_accuracy">DMA Accuracy</string>
|
||||
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
|
||||
<string name="frame_pacing_mode">Frame Pacing Mode</string>
|
||||
<string name="frame_pacing_mode_description">Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent.</string>
|
||||
<string name="anisotropic_filtering">Anisotropic filtering</string>
|
||||
<string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
|
||||
<string name="vram_usage_mode">VRAM Usage Mode</string>
|
||||
@@ -1038,13 +1036,6 @@
|
||||
<string name="dma_accuracy_unsafe">Unsafe</string>
|
||||
<string name="dma_accuracy_safe">Safe</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Auto</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Decoding Method Choices -->
|
||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||
<string name="accelerate_astc_gpu" translatable="false">GPU</string>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,10 +11,11 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
|
||||
AudioCore::AudioCore(Core::System& system) {
|
||||
audio_manager.emplace();
|
||||
CreateSinks();
|
||||
// Must be created after the sinks
|
||||
adsp = std::make_unique<ADSP::ADSP>(system, *output_sink);
|
||||
adsp.emplace(system, *output_sink);
|
||||
}
|
||||
|
||||
AudioCore ::~AudioCore() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,10 +18,7 @@ class System;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioManager;
|
||||
/**
|
||||
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
*/
|
||||
/// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
class AudioCore {
|
||||
public:
|
||||
explicit AudioCore(Core::System& system);
|
||||
@@ -50,27 +50,22 @@ public:
|
||||
*/
|
||||
Sink::Sink& GetInputSink();
|
||||
|
||||
/**
|
||||
* Get the ADSP.
|
||||
*
|
||||
* @return Ref to the ADSP.
|
||||
*/
|
||||
/// @brief Get the ADSP.
|
||||
/// @return Ref to the ADSP.
|
||||
ADSP::ADSP& ADSP();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the sinks on startup.
|
||||
*/
|
||||
/// @brief Create the sinks on startup.
|
||||
void CreateSinks();
|
||||
|
||||
/// Main audio manager for audio in/out
|
||||
std::unique_ptr<AudioManager> audio_manager;
|
||||
std::optional<AudioManager> audio_manager;
|
||||
/// Sink used for audio renderer and audio out
|
||||
std::unique_ptr<Sink::Sink> output_sink;
|
||||
/// Sink used for audio input
|
||||
std::unique_ptr<Sink::Sink> input_sink;
|
||||
/// The ADSP in the sysmodule
|
||||
std::unique_ptr<ADSP::ADSP> adsp;
|
||||
std::optional<ADSP::ADSP> adsp;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -41,8 +44,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,81 +1,77 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioManager::AudioManager() {
|
||||
thread = std::jthread([this]() { ThreadFunc(); });
|
||||
thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("AudioManager");
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
while (!stop_token.stop_requested()) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = Event::Type(i);
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
running = false;
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
thread.join();
|
||||
if (thread.joinable()) {
|
||||
thread.request_stop();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
|
||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
|
||||
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
||||
events.SetAudioEvent(type, signalled);
|
||||
}
|
||||
|
||||
void AudioManager::ThreadFunc() {
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = static_cast<Event::Type>(i);
|
||||
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -66,13 +69,6 @@ public:
|
||||
void SetEvent(Event::Type type, bool signalled);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Main thread, waiting on a manager signal and calling the registered function.
|
||||
*/
|
||||
void ThreadFunc();
|
||||
|
||||
/// Is the main thread running?
|
||||
std::atomic<bool> running{};
|
||||
/// Unused
|
||||
bool needs_update{};
|
||||
/// Events to be set and signalled
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -40,8 +43,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <sys/random.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/mach.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
// FreeBSD
|
||||
@@ -503,8 +505,7 @@ public:
|
||||
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||
#elif defined(__OpenBSD__)
|
||||
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||
#elif defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
#elif defined(__FreeBSD__)
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#elif defined(__APPLE__)
|
||||
// macOS doesn't have memfd_create, use anonymous temporary file
|
||||
@@ -571,9 +572,9 @@ public:
|
||||
if (True(perms & MemoryPermission::Execute))
|
||||
prot_flags |= PROT_EXEC;
|
||||
#endif
|
||||
int flags = (fd > 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {} {}", strerror(errno), fd);
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
@@ -587,9 +588,8 @@ public:
|
||||
auto [merged_pointer, merged_size] =
|
||||
free_manager.FreeBlock(virtual_base + virtual_offset, length);
|
||||
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
|
||||
|
||||
@@ -186,7 +186,7 @@ bool IsFastmemEnabled() {
|
||||
// Only 4kb systems support host MMU right now
|
||||
// TODO: Support this
|
||||
return getpagesize() == 4096;
|
||||
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__)
|
||||
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__) && !defined(__FreeBSD__)
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
|
||||
@@ -186,7 +186,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
|
||||
config.fastmem_pointer = page_table->fastmem_arena ?
|
||||
std::optional<uintptr_t>{reinterpret_cast<uintptr_t>(page_table->fastmem_arena)} :
|
||||
std::optional<uintptr_t>{uintptr_t(page_table->fastmem_arena)} :
|
||||
std::nullopt;
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
@@ -286,10 +286,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
// Curated optimizations
|
||||
case Settings::CpuAccuracy::Auto:
|
||||
config.unsafe_optimizations = true;
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__) || defined(__NetBSD__)
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
#endif
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
@@ -304,6 +300,10 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!Settings::IsFastmemEnabled()) {
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
m_jit.emplace(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -338,10 +338,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
// Safe optimisations
|
||||
case Settings::CpuAccuracy::Auto:
|
||||
config.unsafe_optimizations = true;
|
||||
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
#endif
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
@@ -355,6 +351,10 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!Settings::IsFastmemEnabled()) {
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
m_jit.emplace(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -773,7 +773,11 @@ std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, m
|
||||
bool was_executed = false;
|
||||
|
||||
auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction);
|
||||
was_executed = decoder.get().call(visitor, instruction);
|
||||
if (decoder) {
|
||||
was_executed = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
was_executed = false;
|
||||
}
|
||||
return was_executed ? std::optional<u64>(pc + 4) : std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -347,7 +347,7 @@ struct System::Impl {
|
||||
|
||||
// Register with applet manager
|
||||
// All threads are started, begin main process execution, now that we're in the clear
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
|
||||
+48
-36
@@ -26,42 +26,15 @@ CpuManager::~CpuManager() = default;
|
||||
void CpuManager::Initialize() {
|
||||
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
||||
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
||||
for (std::size_t core = 0; core < num_cores; core++) {
|
||||
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) {
|
||||
// Initialization
|
||||
system.RegisterCoreThread(core);
|
||||
Common::SetCurrentThreadName(("CPUCore_" + std::to_string(core)).c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||
#ifdef __ANDROID__
|
||||
// Aimed specifically for Snapdragon 8 Elite devices
|
||||
// This kills performance on desktop, but boosts perf for UMA devices
|
||||
// like the S8E. Mediatek and Mali likely won't suffer.
|
||||
Common::PinCurrentThreadToPerformanceCore(core);
|
||||
#endif
|
||||
auto& data = core_data[core];
|
||||
data.host_context = Common::Fiber::ThreadToFiber();
|
||||
// Running
|
||||
if (gpu_barrier->Sync(token)) {
|
||||
if (!is_async_gpu && !is_multicore) {
|
||||
system.GPU().ObtainContext();
|
||||
}
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||
Kernel::SetCurrentThread(kernel, thread);
|
||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||
}
|
||||
// Cleanup
|
||||
data.host_context->Exit();
|
||||
});
|
||||
}
|
||||
for (std::size_t core = 0; core < num_cores; core++)
|
||||
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) { RunThread(token, core); });
|
||||
}
|
||||
|
||||
void CpuManager::Shutdown() {
|
||||
for (auto it = core_data.begin(); it != core_data.end(); ++it) {
|
||||
if (it->host_thread.joinable()) {
|
||||
it->host_thread.request_stop();
|
||||
it->host_thread.join();
|
||||
for (std::size_t core = 0; core < num_cores; core++) {
|
||||
if (core_data[core].host_thread.joinable()) {
|
||||
core_data[core].host_thread.request_stop();
|
||||
core_data[core].host_thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,10 +77,12 @@ void CpuManager::MultiCoreRunGuestThread() {
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
|
||||
while (true) {
|
||||
auto& physical_core = kernel.CurrentPhysicalCore();
|
||||
while (!physical_core.IsInterrupted()) {
|
||||
physical_core.RunThread(thread);
|
||||
auto* physical_core = &kernel.CurrentPhysicalCore();
|
||||
while (!physical_core->IsInterrupted()) {
|
||||
physical_core->RunThread(thread);
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
|
||||
HandleInterrupt();
|
||||
}
|
||||
}
|
||||
@@ -208,4 +183,41 @@ void CpuManager::ShutdownThread() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
||||
/// Initialization
|
||||
system.RegisterCoreThread(core);
|
||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||
#ifdef __ANDROID__
|
||||
// Aimed specifically for Snapdragon 8 Elite devices
|
||||
// This kills performance on desktop, but boosts perf for UMA devices
|
||||
// like the S8E. Mediatek and Mali likely won't suffer.
|
||||
Common::PinCurrentThreadToPerformanceCore(core);
|
||||
#endif
|
||||
auto& data = core_data[core];
|
||||
data.host_context = Common::Fiber::ThreadToFiber();
|
||||
|
||||
// Cleanup
|
||||
SCOPE_EXIT {
|
||||
data.host_context->Exit();
|
||||
};
|
||||
|
||||
// Running
|
||||
if (!gpu_barrier->Sync(token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_async_gpu && !is_multicore) {
|
||||
system.GPU().ObtainContext();
|
||||
}
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||
Kernel::SetCurrentThread(kernel, thread);
|
||||
|
||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -87,6 +84,7 @@ private:
|
||||
void GuestActivate();
|
||||
void HandleInterrupt();
|
||||
void ShutdownThread();
|
||||
void RunThread(std::stop_token stop_token, std::size_t core);
|
||||
|
||||
struct CoreData {
|
||||
std::shared_ptr<Common::Fiber> host_context;
|
||||
|
||||
@@ -170,6 +170,9 @@ struct KernelCore::Impl {
|
||||
schedulers[core_id].reset();
|
||||
}
|
||||
|
||||
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
|
||||
|
||||
// Close kernel objects that were not freed on shutdown
|
||||
{
|
||||
std::scoped_lock lk{registered_in_use_objects_lock};
|
||||
@@ -352,6 +355,22 @@ struct KernelCore::Impl {
|
||||
application_process->Open();
|
||||
}
|
||||
|
||||
/// Sets the host thread ID for the caller.
|
||||
u32 SetHostThreadId(std::size_t core_id) {
|
||||
// This should only be called during core init.
|
||||
ASSERT(tls_data.host_thread_id == UINT8_MAX);
|
||||
|
||||
// The first four slots are reserved for CPU core threads
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
tls_data.host_thread_id = u8(core_id);
|
||||
return tls_data.host_thread_id;
|
||||
}
|
||||
|
||||
/// Gets the host thread ID for the caller
|
||||
u32 GetHostThreadId() const {
|
||||
return tls_data.host_thread_id;
|
||||
}
|
||||
|
||||
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||
KThread* GetHostDummyThread(KThread* existing_thread) {
|
||||
if (tls_data.thread == nullptr) {
|
||||
@@ -367,12 +386,9 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
/// Registers a CPU core thread by allocating a host thread ID for it
|
||||
void RegisterCoreThread(std::size_t core_id) noexcept {
|
||||
// This should only be called during core init.
|
||||
ASSERT(tls_data.host_thread_id == UINT8_MAX);
|
||||
// The first four slots are reserved for CPU core threads
|
||||
void RegisterCoreThread(std::size_t core_id) {
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
const auto this_id = tls_data.host_thread_id = u8(core_id);
|
||||
const auto this_id = SetHostThreadId(core_id);
|
||||
if (!is_multicore)
|
||||
single_core_thread_id = this_id;
|
||||
}
|
||||
@@ -382,8 +398,8 @@ struct KernelCore::Impl {
|
||||
(void)GetHostDummyThread(existing_thread);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 GetCurrentHostThreadID() noexcept {
|
||||
auto const this_id = tls_data.host_thread_id;
|
||||
[[nodiscard]] u32 GetCurrentHostThreadID() {
|
||||
auto const this_id = GetHostThreadId();
|
||||
if (!is_multicore && single_core_thread_id == this_id)
|
||||
return u32(system.GetCpuManager().CurrentCore());
|
||||
return this_id;
|
||||
@@ -778,6 +794,9 @@ struct KernelCore::Impl {
|
||||
|
||||
std::array<std::optional<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
|
||||
|
||||
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
|
||||
|
||||
// Kernel memory management
|
||||
std::optional<KMemoryManager> memory_manager;
|
||||
|
||||
@@ -911,7 +930,11 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
|
||||
}
|
||||
|
||||
size_t KernelCore::CurrentPhysicalCoreIndex() const {
|
||||
return impl->GetCurrentHostThreadID();
|
||||
const u32 core_id = impl->GetCurrentHostThreadID();
|
||||
if (core_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return Core::Hardware::NUM_CPU_CORES - 1;
|
||||
}
|
||||
return core_id;
|
||||
}
|
||||
|
||||
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -119,9 +119,10 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||
|
||||
// Since scheduling may occur here, we cannot use any cached
|
||||
// state after returning from calls we make.
|
||||
|
||||
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||
// or if the thread is unable to continue for any reason.
|
||||
if (breakpoint || (prefetch_abort && may_abort)) {
|
||||
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||
// or if the thread is unable to continue for any reason.
|
||||
if (breakpoint) {
|
||||
interface->RewindBreakpointInstruction();
|
||||
}
|
||||
@@ -132,19 +133,26 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||
}
|
||||
thread->RequestSuspend(SuspendType::Debug);
|
||||
return;
|
||||
} else if (data_abort) {
|
||||
// Notify the debugger and go to sleep on data abort.
|
||||
}
|
||||
|
||||
// Notify the debugger and go to sleep on data abort.
|
||||
if (data_abort) {
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.GetDebugger().NotifyThreadWatchpoint(thread, *interface->HaltedWatchpoint());
|
||||
}
|
||||
thread->RequestSuspend(SuspendType::Debug);
|
||||
return;
|
||||
} else if (supervisor_call) {
|
||||
// Handle system calls: Perform call.
|
||||
}
|
||||
|
||||
// Handle system calls.
|
||||
if (supervisor_call) {
|
||||
// Perform call.
|
||||
Svc::Call(system, interface->GetSvcNumber());
|
||||
return;
|
||||
} else if (interrupt || m_is_single_core) {
|
||||
// Handle external interrupt sources.
|
||||
}
|
||||
|
||||
// Handle external interrupt sources.
|
||||
if (interrupt || m_is_single_core) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -152,14 +160,16 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||
|
||||
void PhysicalCore::LoadContext(const KThread* thread) {
|
||||
auto* const process = thread->GetOwnerProcess();
|
||||
if (process) {
|
||||
if (!process) {
|
||||
// Kernel threads do not run on emulated CPU cores.
|
||||
auto* interface = process->GetArmInterface(m_core_index);
|
||||
if (interface) {
|
||||
interface->SetContext(thread->GetContext());
|
||||
interface->SetTpidrroEl0(GetInteger(thread->GetTlsAddress()));
|
||||
interface->SetWatchpointArray(&process->GetWatchpoints());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto* interface = process->GetArmInterface(m_core_index);
|
||||
if (interface) {
|
||||
interface->SetContext(thread->GetContext());
|
||||
interface->SetTpidrroEl0(GetInteger(thread->GetTlsAddress()));
|
||||
interface->SetWatchpointArray(&process->GetWatchpoints());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,12 +179,14 @@ void PhysicalCore::LoadSvcArguments(const KProcess& process, std::span<const uin
|
||||
|
||||
void PhysicalCore::SaveContext(KThread* thread) const {
|
||||
auto* const process = thread->GetOwnerProcess();
|
||||
if (process) {
|
||||
if (!process) {
|
||||
// Kernel threads do not run on emulated CPU cores.
|
||||
auto* interface = process->GetArmInterface(m_core_index);
|
||||
if (interface) {
|
||||
interface->GetContext(thread->GetContext());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto* interface = process->GetArmInterface(m_core_index);
|
||||
if (interface) {
|
||||
interface->GetContext(thread->GetContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,11 +206,13 @@ void PhysicalCore::CloneFpuStatus(KThread* dst) const {
|
||||
|
||||
void PhysicalCore::LogBacktrace() {
|
||||
auto* process = GetCurrentProcessPointer(m_kernel);
|
||||
if (process) {
|
||||
auto* interface = process->GetArmInterface(m_core_index);
|
||||
if (interface) {
|
||||
interface->LogBacktrace(process);
|
||||
}
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* interface = process->GetArmInterface(m_core_index);
|
||||
if (interface) {
|
||||
interface->LogBacktrace(process);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +221,7 @@ void PhysicalCore::Idle() {
|
||||
m_on_interrupt.wait(lk, [this] { return m_is_interrupted; });
|
||||
}
|
||||
|
||||
[[noinline]] bool PhysicalCore::IsInterrupted() const {
|
||||
bool PhysicalCore::IsInterrupted() const {
|
||||
return m_is_interrupted;
|
||||
}
|
||||
|
||||
@@ -226,10 +240,12 @@ void PhysicalCore::Interrupt() {
|
||||
m_on_interrupt.notify_one();
|
||||
|
||||
// If there is no thread running, we are done.
|
||||
if (arm_interface != nullptr) {
|
||||
// Interrupt the CPU.
|
||||
arm_interface->SignalInterrupt(thread);
|
||||
if (arm_interface == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Interrupt the CPU.
|
||||
arm_interface->SignalInterrupt(thread);
|
||||
}
|
||||
|
||||
void PhysicalCore::ClearInterrupt() {
|
||||
|
||||
@@ -268,7 +268,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
@@ -34,16 +36,15 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
|
||||
|
||||
} // namespace
|
||||
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
: m_window_system(window_system) {
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
|
||||
// TODO: am reads this from the home button state in hid, which is controller-agnostic.
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change =
|
||||
[this](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
this->OnButtonStateChanged();
|
||||
}
|
||||
},
|
||||
.on_change = [this, &window_system](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
std::unique_lock lk{m_mutex};
|
||||
OnButtonStateChanged(window_system);
|
||||
}
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
|
||||
@@ -52,25 +53,35 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
m_player1_key = m_player1->SetCallback(engine_callback);
|
||||
|
||||
m_thread = std::thread([this] { this->ThreadLoop(); });
|
||||
m_thread = std::jthread([this, &window_system](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("ButtonPoller");
|
||||
while (!stop_token.stop_requested()) {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (stop_token.stop_requested())
|
||||
break;
|
||||
OnButtonStateChanged(window_system);
|
||||
std::this_thread::sleep_for(5ms);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ButtonPoller::~ButtonPoller() {
|
||||
m_handheld->DeleteCallback(m_handheld_key);
|
||||
m_player1->DeleteCallback(m_player1_key);
|
||||
m_stop = true;
|
||||
m_cv.notify_all();
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.request_stop();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonPoller::OnButtonStateChanged() {
|
||||
std::lock_guard lk{m_mutex};
|
||||
const bool home_button =
|
||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
||||
m_player1->GetCaptureButtons().capture.Value();
|
||||
void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
auto const home_button = m_handheld->GetHomeButtons().home.Value()
|
||||
|| m_player1->GetHomeButtons().home.Value();
|
||||
auto const capture_button = m_handheld->GetCaptureButtons().capture.Value()
|
||||
|| m_player1->GetCaptureButtons().capture.Value();
|
||||
|
||||
// Buttons pressed which were not previously pressed
|
||||
if (home_button && !m_home_button_press_start) {
|
||||
@@ -90,7 +101,7 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
m_home_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
@@ -98,7 +109,7 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
m_capture_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
@@ -107,9 +118,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (!home_button && m_home_button_press_start) {
|
||||
if(!m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing
|
||||
: SystemButtonType::HomeButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::HomeButtonShortPressing : SystemButtonType::HomeButtonLongPressing);
|
||||
}
|
||||
m_home_button_press_start = std::nullopt;
|
||||
m_home_button_long_sent = false;
|
||||
@@ -117,9 +127,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (!capture_button && m_capture_button_press_start) {
|
||||
if (!m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing
|
||||
: SystemButtonType::CaptureButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::CaptureButtonShortPressing : SystemButtonType::CaptureButtonLongPressing);
|
||||
}
|
||||
m_capture_button_press_start = std::nullopt;
|
||||
m_capture_button_long_sent = false;
|
||||
@@ -130,16 +139,4 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
// }
|
||||
}
|
||||
|
||||
void ButtonPoller::ThreadLoop() {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
while (!m_stop) {
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (m_stop) break;
|
||||
lk.unlock();
|
||||
OnButtonStateChanged();
|
||||
lk.lock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -30,31 +30,23 @@ class ButtonPoller {
|
||||
public:
|
||||
explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
|
||||
~ButtonPoller();
|
||||
void OnButtonStateChanged(WindowSystem& window_system);
|
||||
|
||||
private:
|
||||
void OnButtonStateChanged();
|
||||
void ThreadLoop();
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
int m_handheld_key{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int m_player1_key{};
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::jthread m_thread;
|
||||
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
||||
|
||||
bool m_home_button_long_sent{};
|
||||
bool m_capture_button_long_sent{};
|
||||
bool m_power_button_long_sent{};
|
||||
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_stop{false};
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mutex;
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int32_t m_handheld_key{};
|
||||
int32_t m_player1_key{};
|
||||
bool m_home_button_long_sent : 1 = false;
|
||||
bool m_capture_button_long_sent : 1 = false;
|
||||
bool m_power_button_long_sent : 1 = false;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,12 +18,24 @@ enum class UserDataTag : u32 {
|
||||
};
|
||||
|
||||
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
||||
: m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system),
|
||||
m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) {
|
||||
: m_system(system), m_context(system, "am:EventObserver")
|
||||
, m_window_system(window_system)
|
||||
, m_wakeup_event(m_context)
|
||||
, m_wakeup_holder(m_wakeup_event.GetHandle())
|
||||
{
|
||||
m_window_system.SetEventObserver(this);
|
||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
||||
m_thread = std::thread([&] { this->ThreadFunc(); });
|
||||
m_thread = std::thread([this] {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
while (true) {
|
||||
auto* signaled_holder = this->WaitSignaled();
|
||||
if (!signaled_holder) {
|
||||
break;
|
||||
}
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EventObserver::~EventObserver() {
|
||||
@@ -146,17 +161,4 @@ void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
|
||||
delete holder;
|
||||
}
|
||||
|
||||
void EventObserver::ThreadFunc() {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
|
||||
while (true) {
|
||||
auto* signaled_holder = this->WaitSignaled();
|
||||
if (!signaled_holder) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -41,9 +44,6 @@ private:
|
||||
private:
|
||||
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
|
||||
private:
|
||||
// System reference and context.
|
||||
Core::System& m_system;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
@@ -16,7 +20,7 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
[[nodiscard]] FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||
if (!slot.has_value()) {
|
||||
return FileSys::StorageId::None;
|
||||
@@ -36,31 +40,23 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
[[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (!out_loader) {
|
||||
return nullptr;
|
||||
if (out_loader) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_optional<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to load the process.
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
@@ -70,7 +66,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
@@ -79,9 +75,8 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
||||
nca.GetKeyGeneration());
|
||||
return nullptr;
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,42 +85,32 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
auto process =
|
||||
CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index);
|
||||
if (!process) {
|
||||
return nullptr;
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
} else {
|
||||
out_control.resize(sizeof(FileSys::RawNACP));
|
||||
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
||||
}
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process->GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
}
|
||||
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
} else {
|
||||
out_control.resize(sizeof(FileSys::RawNACP));
|
||||
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
||||
}
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process->GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(
|
||||
storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(
|
||||
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
|
||||
return process;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -24,12 +27,7 @@ class Process;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index);
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -21,8 +21,7 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor,
|
||||
Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor, Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
// Get the program NCA from storage.
|
||||
@@ -35,11 +34,10 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process =
|
||||
CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
@@ -47,8 +45,7 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
|
||||
window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -90,12 +87,10 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
|
||||
auto process =
|
||||
CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
@@ -103,8 +98,7 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
|
||||
m_window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -121,26 +121,23 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
};
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2100);
|
||||
if (!process) {
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = mode;
|
||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
window_system.TrackApplet(applet, false);
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
}
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = mode;
|
||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
window_system.TrackApplet(applet, false);
|
||||
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -14,7 +14,9 @@ namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audout:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -43,7 +43,7 @@ private:
|
||||
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
||||
std::optional<AudioCore::AudioOut::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,7 +18,9 @@ namespace Service::Audio {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioRendererManager::IAudioRendererManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audren:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -30,7 +33,7 @@ private:
|
||||
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
||||
u32 revision, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::Renderer::Manager> impl;
|
||||
std::optional<AudioCore::Renderer::Manager> impl;
|
||||
u32 num_audio_devices{0};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,14 +13,6 @@
|
||||
|
||||
namespace Service {
|
||||
|
||||
Process::Process(Core::System& system)
|
||||
: m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
|
||||
m_process_started() {}
|
||||
|
||||
Process::~Process() {
|
||||
this->Finalize();
|
||||
}
|
||||
|
||||
bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) {
|
||||
// First, ensure we are not holding another process.
|
||||
this->Finalize();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -22,8 +25,8 @@ namespace Service {
|
||||
|
||||
class Process {
|
||||
public:
|
||||
explicit Process(Core::System& system);
|
||||
~Process();
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
@@ -50,8 +53,8 @@ public:
|
||||
private:
|
||||
Core::System& m_system;
|
||||
Kernel::KProcess* m_process{};
|
||||
s32 m_main_thread_priority{};
|
||||
u64 m_main_thread_stack_size{};
|
||||
s32 m_main_thread_priority{};
|
||||
bool m_process_started{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -373,6 +373,8 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
(void)m_container.SetLayerZIndex(layer_id, 100000);
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -78,7 +78,9 @@ if (MSVC)
|
||||
/Zc:throwingNew # Assumes new (without std::nothrow) never returns null.
|
||||
/volatile:iso # Use strict standard-abiding volatile semantics
|
||||
/bigobj # Increase number of sections in .obj files
|
||||
/DNOMINMAX)
|
||||
/DNOMINMAX
|
||||
/GR-
|
||||
)
|
||||
|
||||
if (CXX_CLANG)
|
||||
list(APPEND DYNARMIC_CXX_FLAGS
|
||||
@@ -91,8 +93,10 @@ else()
|
||||
-Wextra
|
||||
-Wcast-qual
|
||||
-pedantic
|
||||
-Wno-missing-braces)
|
||||
|
||||
-Wno-missing-braces
|
||||
-fno-rtti
|
||||
#-fno-exceptions
|
||||
)
|
||||
if (CXX_GCC)
|
||||
# GCC produces bogus -Warray-bounds warnings from xbyak headers for code paths that are not
|
||||
# actually reachable. Specifically, it happens in cases where some code casts an Operand&
|
||||
@@ -102,7 +106,6 @@ else()
|
||||
list(APPEND DYNARMIC_CXX_FLAGS -Wno-array-bounds)
|
||||
list(APPEND DYNARMIC_CXX_FLAGS -Wstack-usage=4096)
|
||||
endif()
|
||||
|
||||
if (CXX_CLANG)
|
||||
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
|
||||
# And this in turns limits the size of a std::array.
|
||||
@@ -117,7 +120,6 @@ if (NOT Boost_FOUND)
|
||||
endif()
|
||||
|
||||
find_package(fmt 8 CONFIG)
|
||||
find_package(mcl 0.1.12 REQUIRED)
|
||||
find_package(unordered_dense REQUIRED)
|
||||
|
||||
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
string(TOUPPER "${arch}" arch)
|
||||
file(READ "${input_file}" f_contents)
|
||||
file(WRITE "${output_file}" "#include <mcl/macro/architecture.hpp>\n#if defined(MCL_ARCHITECTURE_${arch})\n${f_contents}\n#endif\n")
|
||||
file(WRITE "${output_file}" "#if defined(ARCHITECTURE_${arch})\n${f_contents}\n#endif\n")
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
include(TargetArchitectureSpecificSources)
|
||||
|
||||
add_library(dynarmic STATIC
|
||||
mcl/bit.hpp
|
||||
mcl/function_info.hpp
|
||||
mcl/integer_of_size.hpp
|
||||
mcl/intrusive_list.hpp
|
||||
mcl/is_instance_of_template.hpp
|
||||
|
||||
backend/block_range_information.cpp
|
||||
backend/block_range_information.h
|
||||
backend/exception_handler.h
|
||||
@@ -353,7 +359,7 @@ set_target_properties(dynarmic PROPERTIES
|
||||
target_compile_options(dynarmic PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||
|
||||
target_link_libraries(dynarmic PRIVATE unordered_dense::unordered_dense)
|
||||
target_link_libraries(dynarmic PUBLIC fmt::fmt merry::mcl)
|
||||
target_link_libraries(dynarmic PUBLIC fmt::fmt)
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(dynarmic PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/scope_exit.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/arm64/a32_address_space.h"
|
||||
@@ -32,31 +31,35 @@ struct Jit::Impl final {
|
||||
, core(conf) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||
is_executing = true;
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
|
||||
jit_interface->is_executing = true;
|
||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
is_executing = false;
|
||||
jit_interface->is_executing = false;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||
is_executing = true;
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
|
||||
jit_interface->is_executing = true;
|
||||
HaltReason hr = core.Step(current_address_space, current_state, &halt_reason);
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
is_executing = false;
|
||||
jit_interface->is_executing = false;
|
||||
return hr;
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
@@ -66,12 +69,12 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&halt_reason, u32(hr));
|
||||
Atomic::Or(&halt_reason, static_cast<u32>(hr));
|
||||
Atomic::Barrier();
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&halt_reason, ~u32(hr));
|
||||
Atomic::And(&halt_reason, ~static_cast<u32>(hr));
|
||||
Atomic::Barrier();
|
||||
}
|
||||
|
||||
@@ -118,15 +121,21 @@ struct Jit::Impl final {
|
||||
private:
|
||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
|
||||
ClearHalt(HaltReason::CacheInvalidation);
|
||||
|
||||
if (invalidate_entire_cache) {
|
||||
current_address_space.ClearCache();
|
||||
|
||||
invalidate_entire_cache = false;
|
||||
invalid_cache_ranges.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!invalid_cache_ranges.empty()) {
|
||||
current_address_space.InvalidateCacheRanges(invalid_cache_ranges);
|
||||
|
||||
invalid_cache_ranges.clear();
|
||||
return;
|
||||
}
|
||||
@@ -140,9 +149,10 @@ private:
|
||||
A32Core core;
|
||||
|
||||
volatile u32 halt_reason = 0;
|
||||
|
||||
std::mutex invalidation_mutex;
|
||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
bool is_executing = false;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
||||
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/scope_exit.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/arm64/a64_address_space.h"
|
||||
@@ -27,15 +26,14 @@ using namespace Backend::Arm64;
|
||||
|
||||
struct Jit::Impl final {
|
||||
Impl(Jit*, A64::UserConfig conf)
|
||||
: conf(conf)
|
||||
, current_address_space(conf)
|
||||
, core(conf)
|
||||
{}
|
||||
: conf(conf)
|
||||
, current_address_space(conf)
|
||||
, core(conf) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
is_executing = true;
|
||||
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
is_executing = false;
|
||||
@@ -44,8 +42,8 @@ struct Jit::Impl final {
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
||||
is_executing = true;
|
||||
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||
HaltReason hr = core.Step(current_address_space, current_state, &halt_reason);
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
is_executing = false;
|
||||
@@ -53,11 +51,13 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(std::uint64_t start_address, std::size_t length) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u64>::closed(start_address, start_address + length - 1));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
@@ -67,13 +67,11 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&halt_reason, u32(hr));
|
||||
Atomic::Barrier();
|
||||
Atomic::Or(&halt_reason, static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&halt_reason, ~u32(hr));
|
||||
Atomic::Barrier();
|
||||
Atomic::And(&halt_reason, ~static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
std::uint64_t PC() const {
|
||||
@@ -136,6 +134,10 @@ struct Jit::Impl final {
|
||||
current_state.exclusive_state = false;
|
||||
}
|
||||
|
||||
bool IsExecuting() const {
|
||||
return is_executing;
|
||||
}
|
||||
|
||||
std::string Disassemble() const {
|
||||
return {};
|
||||
}
|
||||
@@ -143,15 +145,21 @@ struct Jit::Impl final {
|
||||
private:
|
||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
|
||||
ClearHalt(HaltReason::CacheInvalidation);
|
||||
|
||||
if (invalidate_entire_cache) {
|
||||
current_address_space.ClearCache();
|
||||
|
||||
invalidate_entire_cache = false;
|
||||
invalid_cache_ranges.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!invalid_cache_ranges.empty()) {
|
||||
current_address_space.InvalidateCacheRanges(invalid_cache_ranges);
|
||||
|
||||
invalid_cache_ranges.clear();
|
||||
return;
|
||||
}
|
||||
@@ -164,6 +172,8 @@ private:
|
||||
A64Core core;
|
||||
|
||||
volatile u32 halt_reason = 0;
|
||||
|
||||
std::mutex invalidation_mutex;
|
||||
boost::icl::interval_set<u64> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
bool is_executing = false;
|
||||
@@ -284,6 +294,10 @@ void Jit::ClearExclusiveState() {
|
||||
impl->ClearExclusiveState();
|
||||
}
|
||||
|
||||
bool Jit::IsExecuting() const {
|
||||
return impl->IsExecuting();
|
||||
}
|
||||
|
||||
std::string Jit::Disassemble() const {
|
||||
return impl->Disassemble();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <oaknut/oaknut.hpp>
|
||||
@@ -37,7 +36,7 @@ constexpr auto Rscratch0() {
|
||||
} else if constexpr (bitsize == 64) {
|
||||
return Xscratch0;
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<bitsize>>);
|
||||
return Xscratch0; //UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +47,7 @@ constexpr auto Rscratch1() {
|
||||
} else if constexpr (bitsize == 64) {
|
||||
return Xscratch1;
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<bitsize>>);
|
||||
return Xscratch1; //UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <bit>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/function_info.hpp>
|
||||
#include "dynarmic/mcl/function_info.hpp"
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -642,7 +642,7 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(oaknut::CodeGenerator& code, Emi
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -708,7 +708,7 @@ void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext&
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto& operand_arg = args[0];
|
||||
auto& shift_arg = args[1];
|
||||
|
||||
@@ -209,9 +209,9 @@ void CallbackOnlyEmitExclusiveWriteMemory(oaknut::CodeGenerator& code, EmitConte
|
||||
ctx.reg_alloc.DefineAsRegister(inst, X0);
|
||||
}
|
||||
|
||||
constexpr size_t page_bits = 12;
|
||||
constexpr size_t page_size = 1 << page_bits;
|
||||
constexpr size_t page_mask = (1 << page_bits) - 1;
|
||||
constexpr size_t page_table_const_bits = 12;
|
||||
constexpr size_t page_table_const_size = 1 << page_table_const_bits;
|
||||
constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
|
||||
|
||||
// This function may use Xscratch0 as a scratch register
|
||||
// Trashes NZCV
|
||||
@@ -242,28 +242,28 @@ void EmitDetectMisalignedVAddr(oaknut::CodeGenerator& code, EmitContext& ctx, oa
|
||||
code.TST(Xaddr, align_mask);
|
||||
code.B(NE, *fallback);
|
||||
} else {
|
||||
// If (addr & page_mask) > page_size - byte_size, use fallback.
|
||||
code.AND(Xscratch0, Xaddr, page_mask);
|
||||
code.CMP(Xscratch0, page_size - bitsize / 8);
|
||||
// If (addr & page_table_const_mask) > page_table_const_size - byte_size, use fallback.
|
||||
code.AND(Xscratch0, Xaddr, page_table_const_mask);
|
||||
code.CMP(Xscratch0, page_table_const_size - bitsize / 8);
|
||||
code.B(HI, *fallback);
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs Xscratch0 = page_table[addr >> page_bits]
|
||||
// Outputs Xscratch0 = page_table[addr >> page_table_const_bits]
|
||||
// May use Xscratch1 as scratch register
|
||||
// Address to read/write = [ret0 + ret1], ret0 is always Xscratch0 and ret1 is either Xaddr or Xscratch1
|
||||
// Trashes NZCV
|
||||
template<size_t bitsize>
|
||||
std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::CodeGenerator& code, EmitContext& ctx, oaknut::XReg Xaddr, const SharedLabel& fallback) {
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits;
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
EmitDetectMisalignedVAddr<bitsize>(code, ctx, Xaddr, fallback);
|
||||
|
||||
if (ctx.conf.silently_mirror_page_table || unused_top_bits == 0) {
|
||||
code.UBFX(Xscratch0, Xaddr, page_bits, valid_page_index_bits);
|
||||
code.UBFX(Xscratch0, Xaddr, page_table_const_bits, valid_page_index_bits);
|
||||
} else {
|
||||
code.LSR(Xscratch0, Xaddr, page_bits);
|
||||
code.LSR(Xscratch0, Xaddr, page_table_const_bits);
|
||||
code.TST(Xscratch0, u64(~u64(0)) << valid_page_index_bits);
|
||||
code.B(NE, *fallback);
|
||||
}
|
||||
@@ -283,7 +283,7 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return std::make_pair(Xscratch0, Xaddr);
|
||||
}
|
||||
code.AND(Xscratch1, Xaddr, page_mask);
|
||||
code.AND(Xscratch1, Xaddr, page_table_const_mask);
|
||||
return std::make_pair(Xscratch0, Xscratch1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -6,7 +6,6 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
||||
@@ -46,7 +45,7 @@ static void EmitTwoOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, IR:
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->D2(), Qoperand->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -69,7 +68,7 @@ static void EmitTwoOpArrangedWiden(oaknut::CodeGenerator& code, EmitContext& ctx
|
||||
} else if constexpr (size == 32) {
|
||||
emit(Qresult->D2(), Qoperand->toD().S2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -84,7 +83,7 @@ static void EmitTwoOpArrangedNarrow(oaknut::CodeGenerator& code, EmitContext& ct
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->toD().S2(), Qoperand->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -107,7 +106,7 @@ static void EmitTwoOpArrangedPairWiden(oaknut::CodeGenerator& code, EmitContext&
|
||||
} else if constexpr (size == 32) {
|
||||
emit(Qresult->D2(), Qoperand->S4());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -122,7 +121,7 @@ static void EmitTwoOpArrangedLower(oaknut::CodeGenerator& code, EmitContext& ctx
|
||||
} else if constexpr (size == 32) {
|
||||
emit(Qresult->toD().S2(), Qoperand->toD().S2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -150,7 +149,7 @@ static void EmitThreeOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, I
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->D2(), Qa->D2(), Qb->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -175,7 +174,7 @@ static void EmitThreeOpArrangedWiden(oaknut::CodeGenerator& code, EmitContext& c
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->Q1(), Qa->toD().D1(), Qb->toD().D1());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -198,7 +197,7 @@ static void EmitThreeOpArrangedLower(oaknut::CodeGenerator& code, EmitContext& c
|
||||
} else if constexpr (size == 32) {
|
||||
emit(Qresult->toD().S2(), Qa->toD().S2(), Qb->toD().S2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -220,7 +219,7 @@ static void EmitSaturatedAccumulate(oaknut::CodeGenerator&, EmitContext& ctx, IR
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qaccumulator->D2(), Qoperand->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +240,7 @@ static void EmitImmShift(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* ins
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->D2(), Qoperand->D2(), shift_amount);
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +268,7 @@ static void EmitReduce(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst,
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Vresult, Qoperand->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,8 @@
|
||||
*/
|
||||
|
||||
#include <bit>
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include <mcl/mp/typelist/cartesian_product.hpp>
|
||||
#include <mcl/mp/typelist/get.hpp>
|
||||
#include <mcl/mp/typelist/lift_sequence.hpp>
|
||||
#include <mcl/mp/typelist/list.hpp>
|
||||
#include <mcl/mp/typelist/lower_to_tuple.hpp>
|
||||
#include <mcl/type_traits/function_info.hpp>
|
||||
#include <mcl/type_traits/integer_of_size.hpp>
|
||||
#include "dynarmic/mcl/function_info.hpp"
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
||||
@@ -38,8 +32,6 @@
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
using namespace oaknut::util;
|
||||
namespace mp = mcl::mp;
|
||||
|
||||
using A64FullVectorWidth = std::integral_constant<size_t, 128>;
|
||||
|
||||
// Array alias that always sizes itself according to the given type T
|
||||
@@ -83,7 +75,7 @@ static void EmitTwoOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, IR:
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->D2(), Qa->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -111,7 +103,7 @@ static void EmitThreeOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, I
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->D2(), Qa->D2(), Qb->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -134,7 +126,7 @@ static void EmitFMA(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* ins
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->D2(), Qm->D2(), Qn->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -156,7 +148,7 @@ static void EmitFromFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Ins
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qto->D2(), Qfrom->D2(), fbits);
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -178,7 +170,7 @@ void EmitToFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst)
|
||||
} else if constexpr (fsize == 64) {
|
||||
return Qto->D2();
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<fsize>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}();
|
||||
auto Vfrom = [&] {
|
||||
@@ -187,7 +179,7 @@ void EmitToFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst)
|
||||
} else if constexpr (fsize == 64) {
|
||||
return Qfrom->D2();
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<fsize>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}();
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2022 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
||||
@@ -39,7 +41,7 @@ static void Emit(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst, EmitF
|
||||
} else if constexpr (size == 64) {
|
||||
emit(Qresult->D2(), Qa->D2(), Qb->D2());
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -10,11 +10,10 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
|
||||
#include <mcl/hash/xmrx.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
#include "dynarmic/ir/location_descriptor.h"
|
||||
|
||||
@@ -22,9 +21,16 @@ namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
using DoNotFastmemMarker = std::tuple<IR::LocationDescriptor, unsigned>;
|
||||
|
||||
constexpr size_t xmrx(size_t x) noexcept {
|
||||
x ^= x >> 32;
|
||||
x *= 0xff51afd7ed558ccd;
|
||||
x ^= mcl::bit::rotate_right(x, 47) ^ mcl::bit::rotate_right(x, 23);
|
||||
return x;
|
||||
}
|
||||
|
||||
struct DoNotFastmemMarkerHash {
|
||||
size_t operator()(const DoNotFastmemMarker& value) const {
|
||||
return mcl::hash::xmrx(std::get<0>(value).Value() ^ static_cast<u64>(std::get<1>(value)));
|
||||
[[nodiscard]] size_t operator()(const DoNotFastmemMarker& value) const noexcept {
|
||||
return xmrx(std::get<0>(value).Value() ^ u64(std::get<1>(value)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
#include <iterator>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include <bit>
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/arm64/abi.h"
|
||||
@@ -299,7 +298,7 @@ int RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +365,7 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
} else if constexpr (required_kind == HostLoc::Kind::Flags) {
|
||||
UNREACHABLE(); //A simple read from flags is likely a logic error
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +389,7 @@ int RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||
flags.SetupLocation(value);
|
||||
return 0;
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,7 +409,7 @@ int RegAlloc::RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst*
|
||||
} else if constexpr (kind == HostLoc::Kind::Flags) {
|
||||
ASSERT(false && "Incorrect function for ReadWrite of flags");
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/is_instance_of_template.hpp>
|
||||
#include "dynarmic/mcl/is_instance_of_template.hpp"
|
||||
#include <oaknut/oaknut.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <mcl/macro/architecture.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -16,11 +16,10 @@
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <bit>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <bit>
|
||||
#include <mcl/macro/architecture.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <mcl/macro/architecture.hpp>
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
# include "dynarmic/backend/x64/mig/mach_exc_server.c"
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -6,8 +6,6 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <mcl/macro/architecture.hpp>
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
# include "dynarmic/backend/x64/exception_handler_windows.cpp"
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
|
||||
@@ -99,7 +99,7 @@ void A32AddressSpace::SetCursorPtr(CodePtr ptr) {
|
||||
}
|
||||
|
||||
size_t A32AddressSpace::GetRemainingSize() {
|
||||
return conf.code_cache_size - (GetCursorPtr<sptr>() - GetMemPtr<sptr>());
|
||||
return conf.code_cache_size - (GetCursorPtr<std::intptr_t>() - GetMemPtr<std::intptr_t>());
|
||||
}
|
||||
|
||||
EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) {
|
||||
|
||||
@@ -41,25 +41,25 @@ private:
|
||||
|
||||
template<typename T>
|
||||
T GetMemPtr() {
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uptr> || std::is_same_v<T, sptr>);
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, std::uintptr_t> || std::is_same_v<T, std::intptr_t>);
|
||||
return reinterpret_cast<T>(as.GetBufferPointer(0));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T GetMemPtr() const {
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uptr> || std::is_same_v<T, sptr>);
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, std::uintptr_t> || std::is_same_v<T, std::intptr_t>);
|
||||
return reinterpret_cast<const T>(as.GetBufferPointer(0));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T GetCursorPtr() {
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uptr> || std::is_same_v<T, sptr>);
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, std::uintptr_t> || std::is_same_v<T, std::intptr_t>);
|
||||
return reinterpret_cast<T>(as.GetCursorPointer());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T GetCursorPtr() const {
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uptr> || std::is_same_v<T, sptr>);
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, std::uintptr_t> || std::is_same_v<T, std::intptr_t>);
|
||||
return reinterpret_cast<const T>(as.GetCursorPointer());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/scope_exit.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/riscv64/a32_address_space.h"
|
||||
@@ -32,29 +31,31 @@ struct Jit::Impl final {
|
||||
, core(conf) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
is_executing = true;
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
jit_interface->is_executing = true;
|
||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||
RequestCacheInvalidation();
|
||||
is_executing = false;
|
||||
jit_interface->is_executing = false;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!is_executing);
|
||||
is_executing = true;
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
jit_interface->is_executing = true;
|
||||
UNIMPLEMENTED();
|
||||
RequestCacheInvalidation();
|
||||
is_executing = false;
|
||||
jit_interface->is_executing = false;
|
||||
return HaltReason{};
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(u32 start_address, size_t length) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
@@ -120,10 +121,12 @@ private:
|
||||
A32JitState current_state{};
|
||||
A32AddressSpace current_address_space;
|
||||
A32Core core;
|
||||
|
||||
volatile u32 halt_reason = 0;
|
||||
|
||||
std::mutex invalidation_mutex;
|
||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
bool is_executing = false;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "dynarmic/backend/riscv64/a32_jitstate.h"
|
||||
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::RV64 {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
|
||||
template<typename T>
|
||||
T ptr() const noexcept {
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uptr> || std::is_same_v<T, sptr>);
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, std::uintptr_t> || std::is_same_v<T, std::intptr_t>);
|
||||
return reinterpret_cast<T>(mem);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <biscuit/assembler.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
|
||||
#include "dynarmic/backend/riscv64/a32_jitstate.h"
|
||||
#include "dynarmic/backend/riscv64/abi.h"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2024 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -164,12 +167,12 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(biscuit::Assembler&, EmitContext
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <array>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/common/always_false.h"
|
||||
@@ -164,9 +163,8 @@ u32 RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||
} else if constexpr (kind == HostLoc::Kind::Fpr) {
|
||||
UNIMPLEMENTED();
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -225,7 +223,7 @@ u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||
fprs[new_location_index].realized = true;
|
||||
return new_location_index;
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +250,7 @@ u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||
setup_location(fprs[new_location_index]);
|
||||
return new_location_index;
|
||||
} else {
|
||||
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <biscuit/registers.hpp>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/is_instance_of_template.hpp>
|
||||
#include "dynarmic/mcl/is_instance_of_template.hpp"
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "dynarmic/backend/riscv64/stack_layout.h"
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include <mcl/scope_exit.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <mcl/type_traits/integer_of_size.hpp>
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/a32_emit_x64.h"
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <fmt/format.h>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <bit>
|
||||
#include <mcl/scope_exit.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/common/llvm_disassemble.h"
|
||||
|
||||
@@ -75,9 +74,9 @@ struct Jit::Impl {
|
||||
~Impl() = default;
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||
is_executing = true;
|
||||
jit_interface->is_executing = true;
|
||||
const CodePtr current_codeptr = [this] {
|
||||
// RSB optimization
|
||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
|
||||
@@ -90,43 +89,43 @@ struct Jit::Impl {
|
||||
}();
|
||||
const HaltReason hr = block_of_code.RunCode(&jit_state, current_codeptr);
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
is_executing = false;
|
||||
jit_interface->is_executing = false;
|
||||
return hr;
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&jit_state.halt_reason)));
|
||||
is_executing = true;
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||
jit_interface->is_executing = true;
|
||||
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
is_executing = false;
|
||||
jit_interface->is_executing = false;
|
||||
return hr;
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, u32(start_address + length - 1)));
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ASSERT(!is_executing);
|
||||
ASSERT(!jit_interface->is_executing);
|
||||
jit_state = {};
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&jit_state.halt_reason, u32(hr));
|
||||
Atomic::Barrier();
|
||||
Atomic::Or(&jit_state.halt_reason, static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&jit_state.halt_reason, ~u32(hr));
|
||||
Atomic::Barrier();
|
||||
Atomic::And(&jit_state.halt_reason, ~static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
void ClearExclusiveState() {
|
||||
@@ -210,10 +209,14 @@ private:
|
||||
|
||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
|
||||
ClearHalt(HaltReason::CacheInvalidation);
|
||||
|
||||
if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
jit_state.ResetRSB();
|
||||
if (invalidate_entire_cache) {
|
||||
block_of_code.ClearCache();
|
||||
@@ -234,10 +237,11 @@ private:
|
||||
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
|
||||
const A32::UserConfig conf;
|
||||
Jit* jit_interface;
|
||||
|
||||
// Requests made during execution to invalidate the cache are queued up here.
|
||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
bool is_executing = false;
|
||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||
std::mutex invalidation_mutex;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "dynarmic/backend/x64/a32_jitstate.h"
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/scope_exit.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/integer_of_size.hpp>
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include "dynarmic/backend/x64/a64_jitstate.h"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <mcl/type_traits/integer_of_size.hpp>
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/a64_emit_x64.h"
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "dynarmic/common/fp/fpcr.h"
|
||||
#include "dynarmic/common/llvm_disassemble.h"
|
||||
#include <bit>
|
||||
#include <mcl/scope_exit.hpp>
|
||||
|
||||
#include "dynarmic/backend/x64/a64_emit_x64.h"
|
||||
#include "dynarmic/backend/x64/a64_jitstate.h"
|
||||
@@ -86,7 +85,6 @@ public:
|
||||
}
|
||||
return CodePtr((uintptr_t(GetCurrentBlock()) + 15) & ~uintptr_t(15));
|
||||
}();
|
||||
|
||||
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
is_executing = false;
|
||||
@@ -95,7 +93,7 @@ public:
|
||||
|
||||
HaltReason Step() {
|
||||
ASSERT(!is_executing);
|
||||
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&jit_state.halt_reason)));
|
||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||
is_executing = true;
|
||||
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
||||
PerformRequestedCacheInvalidation(hr);
|
||||
@@ -104,12 +102,14 @@ public:
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(u64 start_address, size_t length) {
|
||||
const auto end_address = u64(start_address + length - 1);
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
const auto end_address = static_cast<u64>(start_address + length - 1);
|
||||
const auto range = boost::icl::discrete_interval<u64>::closed(start_address, end_address);
|
||||
invalid_cache_ranges.add(range);
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
@@ -121,13 +121,11 @@ public:
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&jit_state.halt_reason, u32(hr));
|
||||
Atomic::Barrier();
|
||||
Atomic::Or(&jit_state.halt_reason, static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&jit_state.halt_reason, ~u32(hr));
|
||||
Atomic::Barrier();
|
||||
Atomic::And(&jit_state.halt_reason, ~static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
u64 GetSP() const {
|
||||
@@ -215,6 +213,10 @@ public:
|
||||
jit_state.exclusive_state = 0;
|
||||
}
|
||||
|
||||
bool IsExecuting() const {
|
||||
return is_executing;
|
||||
}
|
||||
|
||||
std::string Disassemble() const {
|
||||
const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||
auto const* p = reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||
@@ -263,10 +265,14 @@ private:
|
||||
|
||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
|
||||
ClearHalt(HaltReason::CacheInvalidation);
|
||||
|
||||
if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
jit_state.ResetRSB();
|
||||
if (invalidate_entire_cache) {
|
||||
block_of_code.ClearCache();
|
||||
@@ -285,9 +291,10 @@ private:
|
||||
BlockOfCode block_of_code;
|
||||
A64EmitX64 emitter;
|
||||
Optimization::PolyfillOptions polyfill_options;
|
||||
boost::icl::interval_set<u64> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
bool is_executing = false;
|
||||
bool invalidate_entire_cache = false;
|
||||
boost::icl::interval_set<u64> invalid_cache_ranges;
|
||||
std::mutex invalidation_mutex;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf)
|
||||
@@ -399,6 +406,10 @@ void Jit::ClearExclusiveState() {
|
||||
impl->ClearExclusiveState();
|
||||
}
|
||||
|
||||
bool Jit::IsExecuting() const {
|
||||
return impl->IsExecuting();
|
||||
}
|
||||
|
||||
std::string Jit::Disassemble() const {
|
||||
return impl->Disassemble();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -5,7 +8,7 @@
|
||||
|
||||
#include "dynarmic/backend/x64/a64_jitstate.h"
|
||||
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
|
||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/a32_jitstate.h"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
#include "dynarmic/backend/x64/abi.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/common/fp/rounding_mode.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include <bit>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/function_info.hpp>
|
||||
#include "dynarmic/mcl/function_info.hpp"
|
||||
|
||||
#include "dynarmic/backend/x64/callback.h"
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <boost/variant/detail/apply_visitor_binary.hpp>
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include <mcl/scope_exit.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <mcl/bitsizeof.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
@@ -663,7 +663,7 @@ void EmitX64::EmitArithmeticShiftRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitBitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -736,7 +736,7 @@ void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitBitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto& operand_arg = args[0];
|
||||
auto& shift_arg = args[1];
|
||||
|
||||
@@ -11,14 +11,8 @@
|
||||
#include <utility>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include <mcl/mp/typelist/cartesian_product.hpp>
|
||||
#include <mcl/mp/typelist/get.hpp>
|
||||
#include <mcl/mp/typelist/lift_sequence.hpp>
|
||||
#include <mcl/mp/typelist/list.hpp>
|
||||
#include <mcl/mp/typelist/lower_to_tuple.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/integer_of_size.hpp>
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/abi.h"
|
||||
@@ -41,7 +35,6 @@
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
namespace mp = mcl::mp;
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <mcl/macro/concatenate_tokens.hpp>
|
||||
#define CONCATENATE_TOKENS(x, y) CONCATENATE_TOKENS_IMPL(x, y)
|
||||
#define CONCATENATE_TOKENS_IMPL(x, y) x##y
|
||||
|
||||
#define AxxEmitX64 CONCATENATE_TOKENS(Axx, EmitX64)
|
||||
#define AxxEmitContext CONCATENATE_TOKENS(Axx, EmitContext)
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
@@ -22,9 +24,9 @@ namespace {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
constexpr size_t page_bits = 12;
|
||||
constexpr size_t page_size = 1 << page_bits;
|
||||
constexpr size_t page_mask = (1 << page_bits) - 1;
|
||||
constexpr size_t page_table_const_bits = 12;
|
||||
constexpr size_t page_table_const_size = 1 << page_table_const_bits;
|
||||
constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
|
||||
|
||||
template<typename EmitContext>
|
||||
void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr, Xbyak::Reg64 tmp) {
|
||||
@@ -50,7 +52,7 @@ void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsi
|
||||
code.test(vaddr, align_mask);
|
||||
|
||||
if (ctx.conf.only_detect_misalignment_via_page_table_on_page_boundary) {
|
||||
const u32 page_align_mask = static_cast<u32>(page_size - 1) & ~align_mask;
|
||||
const u32 page_align_mask = static_cast<u32>(page_table_const_size - 1) & ~align_mask;
|
||||
|
||||
SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel();
|
||||
|
||||
@@ -83,7 +85,7 @@ template<>
|
||||
// TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
|
||||
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
||||
@@ -96,13 +98,13 @@ template<>
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.and_(tmp, static_cast<u32>(page_mask));
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
return page + tmp.cvt64();
|
||||
}
|
||||
|
||||
template<>
|
||||
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A64EmitContext>(BlockOfCode& code, A64EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits;
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||
@@ -112,29 +114,29 @@ template<>
|
||||
|
||||
if (unused_top_bits == 0) {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
} else if (ctx.conf.silently_mirror_page_table) {
|
||||
if (valid_page_index_bits >= 32) {
|
||||
if (code.HasHostFeature(HostFeature::BMI2)) {
|
||||
const Xbyak::Reg64 bit_count = ctx.reg_alloc.ScratchGpr(code);
|
||||
code.mov(bit_count, unused_top_bits);
|
||||
code.bzhi(tmp, vaddr, bit_count);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
ctx.reg_alloc.Release(bit_count);
|
||||
} else {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shl(tmp, int(unused_top_bits));
|
||||
code.shr(tmp, int(unused_top_bits + page_bits));
|
||||
code.shr(tmp, int(unused_top_bits + page_table_const_bits));
|
||||
}
|
||||
} else {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.and_(tmp, u32((1 << valid_page_index_bits) - 1));
|
||||
}
|
||||
} else {
|
||||
ASSERT(valid_page_index_bits < 32);
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.test(tmp, u32(-(1 << valid_page_index_bits)));
|
||||
code.jnz(abort, code.T_NEAR);
|
||||
}
|
||||
@@ -151,7 +153,7 @@ template<>
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr);
|
||||
code.and_(tmp, static_cast<u32>(page_mask));
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
return page + tmp;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include <limits>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/integer_of_size.hpp>
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
#include "dynarmic/backend/x64/emit_x64.h"
|
||||
@@ -25,12 +25,12 @@ using namespace Xbyak::util;
|
||||
|
||||
namespace {
|
||||
|
||||
enum class Op {
|
||||
enum class SaturationOp {
|
||||
Add,
|
||||
Sub,
|
||||
};
|
||||
|
||||
template<Op op, size_t size, bool has_overflow_inst = false>
|
||||
template<SaturationOp op, size_t size, bool has_overflow_inst = false>
|
||||
void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
@@ -51,7 +51,7 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
|
||||
|
||||
// overflow now contains 0x7F... if a was positive, or 0x80... if a was negative
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == SaturationOp::Add) {
|
||||
code.add(result, addend);
|
||||
} else {
|
||||
code.sub(result, addend);
|
||||
@@ -75,16 +75,16 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
}
|
||||
|
||||
template<Op op, size_t size>
|
||||
template<SaturationOp op, size_t size>
|
||||
void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
Xbyak::Reg op_result = ctx.reg_alloc.UseScratchGpr(code, args[0]).changeBit(size);
|
||||
Xbyak::Reg addend = ctx.reg_alloc.UseScratchGpr(code, args[1]).changeBit(size);
|
||||
|
||||
constexpr u64 boundary = op == Op::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
|
||||
constexpr u64 boundary = op == SaturationOp::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == SaturationOp::Add) {
|
||||
code.add(op_result, addend);
|
||||
} else {
|
||||
code.sub(op_result, addend);
|
||||
@@ -106,11 +106,11 @@ void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst
|
||||
} // anonymous namespace
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAddWithFlag32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 32, true>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 32, true>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSubWithFlag32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 32, true>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 32, true>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -192,19 +192,19 @@ void EmitX64::EmitUnsignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 8>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 16>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 32>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 64>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh16(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -256,51 +256,51 @@ void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh32(EmitContext& ctx,
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 8>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 16>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 8>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 16>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 32>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 64>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 8>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 16>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
@@ -13,11 +13,9 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/bit/bit_count.hpp>
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include <mcl/bitsizeof.hpp>
|
||||
#include "dynarmic/mcl/bit.hpp"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include <mcl/type_traits/function_info.hpp>
|
||||
#include "dynarmic/mcl/function_info.hpp"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/abi.h"
|
||||
@@ -5856,3 +5854,5 @@ void EmitX64::EmitZeroVector(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
#undef ICODE
|
||||
|
||||
@@ -13,14 +13,8 @@
|
||||
#include <utility>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include <mcl/mp/typelist/cartesian_product.hpp>
|
||||
#include <mcl/mp/typelist/get.hpp>
|
||||
#include <mcl/mp/typelist/lift_sequence.hpp>
|
||||
#include <mcl/mp/typelist/list.hpp>
|
||||
#include <mcl/mp/typelist/lower_to_tuple.hpp>
|
||||
#include <mcl/type_traits/function_info.hpp>
|
||||
#include <mcl/type_traits/integer_of_size.hpp>
|
||||
#include "dynarmic/mcl/function_info.hpp"
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
#include "dynarmic/backend/x64/abi.h"
|
||||
@@ -41,7 +35,6 @@
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
namespace mp = mcl::mp;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -2000,6 +1993,7 @@ void EmitX64::EmitFPVectorToHalf32(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Assembly thunk; just remember not to specialise too much otherwise i-cache death!
|
||||
// template<typename FPT, size_t fbits, FP::RoundingMode rounding_mode>
|
||||
// static void EmitFPVectorToFixedThunk(VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -52,12 +52,12 @@ void EmitVectorSaturatedNative(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
}
|
||||
|
||||
enum class Op {
|
||||
enum class VectorSaturationOp {
|
||||
Add,
|
||||
Sub,
|
||||
};
|
||||
|
||||
template<Op op, size_t esize>
|
||||
template<VectorSaturationOp op, size_t esize>
|
||||
void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
static_assert(esize == 32 || esize == 64);
|
||||
constexpr u64 msb_mask = esize == 32 ? 0x8000000080000000 : 0x8000000000000000;
|
||||
@@ -72,7 +72,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
|
||||
code.movaps(xmm0, operand1);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
code.vpternlogd(xmm0, result, operand2, 0b00100100);
|
||||
} else {
|
||||
@@ -102,7 +102,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
|
||||
|
||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
} else {
|
||||
ICODE(vpsub)(result, operand1, operand2);
|
||||
@@ -112,7 +112,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
} else {
|
||||
code.movaps(xmm0, operand1);
|
||||
code.movaps(tmp, operand1);
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(padd)(result, operand2);
|
||||
} else {
|
||||
ICODE(psub)(result, operand2);
|
||||
@@ -121,7 +121,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
code.pxor(tmp, result);
|
||||
}
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
code.pandn(xmm0, tmp);
|
||||
} else {
|
||||
code.pand(xmm0, tmp);
|
||||
@@ -165,7 +165,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
}
|
||||
}
|
||||
|
||||
template<Op op, size_t esize>
|
||||
template<VectorSaturationOp op, size_t esize>
|
||||
void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
static_assert(esize == 32 || esize == 64);
|
||||
|
||||
@@ -177,7 +177,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
ICODE(vpcmpu)(k1, result, operand1, CmpInt::LessThan);
|
||||
ICODE(vpternlog)(result | k1, result, result, u8(0xFF));
|
||||
@@ -201,7 +201,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
|
||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||
code.vpxor(xmm0, operand1, operand2);
|
||||
code.vpand(tmp, operand1, operand2);
|
||||
@@ -250,7 +250,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
code.setnz(overflow);
|
||||
code.or_(code.byte[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_qc], overflow);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
code.por(result, tmp);
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
} else {
|
||||
@@ -270,11 +270,11 @@ void EmitX64::EmitVectorSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Add, 32>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Add, 64>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -286,11 +286,11 @@ void EmitX64::EmitVectorSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -302,11 +302,11 @@ void EmitX64::EmitVectorUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst)
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Add, 32>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Add, 64>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -318,11 +318,11 @@ void EmitX64::EmitVectorUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst)
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -6,17 +6,16 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "dynarmic/backend/x64/perf_map.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "dynarmic/backend/x64/perf_map.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
# include <cstdio>
|
||||
# include <cstdlib>
|
||||
# include <mutex>
|
||||
# include <fmt/format.h>
|
||||
# include <mcl/stdint.hpp>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user