Compare commits
26 commits
02c98ec5e2
...
69bc01f47d
Author | SHA1 | Date | |
---|---|---|---|
69bc01f47d | |||
ea127e719b | |||
86b554ecbe | |||
d709771d67 | |||
428249cb01 | |||
8ed05425dd | |||
26b5286250 | |||
529f78b95f | |||
434bd42a5e | |||
8407510f76 | |||
7f482d0730 | |||
9d53933a95 | |||
7950c5cca0 | |||
09e77fa146 | |||
dae0d7bec6 | |||
37375220e8 | |||
9fae048a5a | |||
eb80a30c42 | |||
22847ec78a | |||
3cb8e6111a | |||
21cd44ec04 | |||
c9a3baab5d | |||
380cfcaeed | |||
44d658bbc5 | |||
a1c5b5c911 | |||
302509d84d |
164 changed files with 1875 additions and 1484 deletions
|
@ -579,8 +579,8 @@ else()
|
||||||
find_package(lz4 REQUIRED)
|
find_package(lz4 REQUIRED)
|
||||||
find_package(RenderDoc MODULE)
|
find_package(RenderDoc MODULE)
|
||||||
find_package(stb MODULE)
|
find_package(stb MODULE)
|
||||||
find_package(enet 1.3 MODULE)
|
find_package(enet 1.3 MODULE REQUIRED)
|
||||||
find_package(Opus 1.3 MODULE)
|
find_package(Opus 1.3 MODULE REQUIRED)
|
||||||
find_package(ZLIB 1.2 REQUIRED)
|
find_package(ZLIB 1.2 REQUIRED)
|
||||||
find_package(zstd 1.5 REQUIRED)
|
find_package(zstd 1.5 REQUIRED)
|
||||||
|
|
||||||
|
|
54
dist/qt_themes/default/style.qss
vendored
54
dist/qt_themes/default/style.qss
vendored
|
@ -95,6 +95,60 @@ QPushButton#button_reset_defaults {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* QGroupBox --------------------------------------------------------------
|
||||||
|
|
||||||
|
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------- */
|
||||||
|
QGroupBox {
|
||||||
|
border: 1px solid #32414B;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
subcontrol-position: top left;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator {
|
||||||
|
margin-left: 2px;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator:unchecked {
|
||||||
|
border: none;
|
||||||
|
image: url(":/qss_icons/rc/checkbox_unchecked.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator:unchecked:hover, QGroupBox::indicator:unchecked:focus, QGroupBox::indicator:unchecked:pressed {
|
||||||
|
border: none;
|
||||||
|
image: url(":/qss_icons/rc/checkbox_unchecked_focus.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator:unchecked:disabled {
|
||||||
|
image: url(":/qss_icons/rc/checkbox_unchecked_disabled.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator:checked {
|
||||||
|
border: none;
|
||||||
|
image: url(":/qss_icons/rc/checkbox_checked.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator:checked:hover, QGroupBox::indicator:checked:focus, QGroupBox::indicator:checked:pressed {
|
||||||
|
border: none;
|
||||||
|
image: url(":/qss_icons/rc/checkbox_checked_focus.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator:checked:disabled {
|
||||||
|
image: url(":/qss_icons/rc/checkbox_checked_disabled.png");
|
||||||
|
}
|
||||||
|
|
||||||
QWidget#bottomPerGameInput,
|
QWidget#bottomPerGameInput,
|
||||||
QWidget#topControllerApplet,
|
QWidget#topControllerApplet,
|
||||||
QWidget#bottomControllerApplet,
|
QWidget#bottomControllerApplet,
|
||||||
|
|
26
dist/qt_themes/default_dark/style.qss
vendored
26
dist/qt_themes/default_dark/style.qss
vendored
|
@ -697,3 +697,29 @@ QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
|
||||||
QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled {
|
QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled {
|
||||||
image: url(:/overlay/osk_button_Y_disabled.png);
|
image: url(:/overlay/osk_button_Y_disabled.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* QGroupBox --------------------------------------------------------------
|
||||||
|
|
||||||
|
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------- */
|
||||||
|
QGroupBox {
|
||||||
|
border: 1px solid #32414B;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 22px;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
subcontrol-position: top left;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::indicator {
|
||||||
|
margin-left: 2px;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
2
dist/qt_themes/qdarkstyle/style.qss
vendored
2
dist/qt_themes/qdarkstyle/style.qss
vendored
|
@ -307,7 +307,7 @@ QAbstractItemView QLineEdit {
|
||||||
QGroupBox {
|
QGroupBox {
|
||||||
border: 1px solid #54575B;
|
border: 1px solid #54575B;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
margin-top: 12px;
|
margin-top: 20px;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -235,10 +235,9 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox
|
||||||
|
|
||||||
--------------------------------------------------------------------------- */
|
--------------------------------------------------------------------------- */
|
||||||
QGroupBox {
|
QGroupBox {
|
||||||
font-weight: bold;
|
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 12px;
|
margin-top: 20px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
* **Windows**: [Windows Building Guide](./docs/build/Windows.md)
|
* **Windows**: [Windows Building Guide](./build/Windows.md)
|
||||||
* **Linux**: [Linux Building Guide](./docs/build/Linux.md)
|
* **Linux**: [Linux Building Guide](./build/Linux.md)
|
||||||
* **Android**: [Android Building Guide](./docs/build/Android.md)
|
* **Android**: [Android Building Guide](./build/Android.md)
|
||||||
* **Solaris**: [Solaris Building Guide](./docs/build/Solaris.md)
|
* **Solaris**: [Solaris Building Guide](./build/Solaris.md)
|
||||||
* **FreeBSD**: [FreeBSD Building Guide](./docs/build/FreeBSD.md)
|
* **FreeBSD**: [FreeBSD Building Guide](./build/FreeBSD.md)
|
||||||
* **macOS**: [macOS Building Guide](./docs/build/macOS.md)
|
* **macOS**: [macOS Building Guide](./build/macOS.md)
|
||||||
|
|
||||||
# CPM
|
# CPM
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ Then type `target remote localhost:1234` and type `c` (for continue) - and then
|
||||||
|
|
||||||
### gdb cheatsheet
|
### gdb cheatsheet
|
||||||
|
|
||||||
- `mo <cmd>`: Monitor commands, `get info`, `get fastmem` and `get mappings` are available.
|
- `mo <cmd>`: Monitor commands, `get info`, `get fastmem` and `get mappings` are available. Type `mo help` for more info.
|
||||||
- `detach`: Detach from remote (i.e restarting the emulator).
|
- `detach`: Detach from remote (i.e restarting the emulator).
|
||||||
- `c`: Continue
|
- `c`: Continue
|
||||||
- `p <expr>`: Print variable, `p/x <expr>` for hexadecimal.
|
- `p <expr>`: Print variable, `p/x <expr>` for hexadecimal.
|
||||||
|
|
11
externals/CMakeLists.txt
vendored
11
externals/CMakeLists.txt
vendored
|
@ -151,6 +151,17 @@ if (ENABLE_WEB_SERVICE)
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# unordered_dense
|
||||||
|
AddPackage(
|
||||||
|
NAME unordered_dense
|
||||||
|
REPO "Lizzie841/unordered_dense"
|
||||||
|
SHA e59d30b7b1
|
||||||
|
HASH 71eff7bd9ba4b9226967bacd56a8ff000946f8813167cb5664bb01e96fb79e4e220684d824fe9c59c4d1cc98c606f13aff05b7940a1ed8ab3c95d6974ee34fa0
|
||||||
|
FIND_PACKAGE_ARGUMENTS "CONFIG"
|
||||||
|
OPTIONS
|
||||||
|
"UNORDERED_DENSE_INSTALL OFF"
|
||||||
|
)
|
||||||
|
|
||||||
# FFMpeg
|
# FFMpeg
|
||||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||||
add_subdirectory(ffmpeg)
|
add_subdirectory(ffmpeg)
|
||||||
|
|
|
@ -18,6 +18,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||||
USE_FAST_CPU_TIME("use_fast_cpu_time"),
|
USE_FAST_CPU_TIME("use_fast_cpu_time"),
|
||||||
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
|
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
|
||||||
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
|
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
|
||||||
|
CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"),
|
||||||
USE_DOCKED_MODE("use_docked_mode"),
|
USE_DOCKED_MODE("use_docked_mode"),
|
||||||
USE_AUTO_STUB("use_auto_stub"),
|
USE_AUTO_STUB("use_auto_stub"),
|
||||||
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
|
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
|
||||||
|
@ -65,7 +66,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||||
SHOW_POWER_INFO("show_power_info"),
|
SHOW_POWER_INFO("show_power_info"),
|
||||||
SHOW_SHADERS_BUILDING("show_shaders_building"),
|
SHOW_SHADERS_BUILDING("show_shaders_building"),
|
||||||
|
|
||||||
DEBUG_FLUSH_BY_LINE("flush_lines"),
|
DEBUG_FLUSH_BY_LINE("flush_line"),
|
||||||
USE_LRU_CACHE("use_lru_cache");
|
USE_LRU_CACHE("use_lru_cache");
|
||||||
|
|
||||||
external fun isRaiiEnabled(): Boolean
|
external fun isRaiiEnabled(): Boolean
|
||||||
|
|
|
@ -673,6 +673,13 @@ abstract class SettingsItem(
|
||||||
descriptionId = R.string.skip_cpu_inner_invalidation_description
|
descriptionId = R.string.skip_cpu_inner_invalidation_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.CPUOPT_UNSAFE_HOST_MMU,
|
||||||
|
titleId = R.string.cpuopt_unsafe_host_mmu,
|
||||||
|
descriptionId = R.string.cpuopt_unsafe_host_mmu_description
|
||||||
|
)
|
||||||
|
)
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.RENDERER_REACTIVE_FLUSHING,
|
BooleanSetting.RENDERER_REACTIVE_FLUSHING,
|
||||||
|
|
|
@ -466,6 +466,7 @@ class SettingsFragmentPresenter(
|
||||||
add(BooleanSetting.USE_CUSTOM_CPU_TICKS.key)
|
add(BooleanSetting.USE_CUSTOM_CPU_TICKS.key)
|
||||||
add(IntSetting.CPU_TICKS.key)
|
add(IntSetting.CPU_TICKS.key)
|
||||||
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
||||||
|
add(BooleanSetting.CPUOPT_UNSAFE_HOST_MMU.key)
|
||||||
add(BooleanSetting.USE_LRU_CACHE.key)
|
add(BooleanSetting.USE_LRU_CACHE.key)
|
||||||
add(BooleanSetting.CORE_SYNC_CORE_SPEED.key)
|
add(BooleanSetting.CORE_SYNC_CORE_SPEED.key)
|
||||||
add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key)
|
add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key)
|
||||||
|
|
|
@ -117,6 +117,8 @@
|
||||||
<string name="cpu_ticks">دورات</string>
|
<string name="cpu_ticks">دورات</string>
|
||||||
<string name="skip_cpu_inner_invalidation">تخطي إبطال ذاكرة التخزين المؤقت الداخلية للوحدة المركزية</string>
|
<string name="skip_cpu_inner_invalidation">تخطي إبطال ذاكرة التخزين المؤقت الداخلية للوحدة المركزية</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقت أثناء تحديثات الذاكرة، مما يقلل استخدام المعالج ويحسن أدائه. قد يسبب هذا أعطالاً أو تعطلًا في بعض الألعاب.</string>
|
<string name="skip_cpu_inner_invalidation_description">يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقت أثناء تحديثات الذاكرة، مما يقلل استخدام المعالج ويحسن أدائه. قد يسبب هذا أعطالاً أو تعطلًا في بعض الألعاب.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">تمكين محاكاة MMU المضيف</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية.</string>
|
||||||
<string name="dma_accuracy">مستوى DMA</string>
|
<string name="dma_accuracy">مستوى DMA</string>
|
||||||
<string name="dma_accuracy_description">يتحكم في دقة تحديد مستوى DMA. الدقة الأعلى يمكنها إصلاح بعض المشاكل في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا كنت غير متأكد، اتركه على الوضع الافتراضي.</string>
|
<string name="dma_accuracy_description">يتحكم في دقة تحديد مستوى DMA. الدقة الأعلى يمكنها إصلاح بعض المشاكل في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا كنت غير متأكد، اتركه على الوضع الافتراضي.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">تیک</string>
|
<string name="cpu_ticks">تیک</string>
|
||||||
<string name="skip_cpu_inner_invalidation">بازنەکردنی ناوەکی CPU</string>
|
<string name="skip_cpu_inner_invalidation">بازنەکردنی ناوەکی CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">هەندێک لە بازنەکردنەکانی هەڵگر لە کاتی نوێکردنەوەی بیرگە دەنێرێت، کەمکردنەوەی بەکارهێنانی CPU و باشترکردنی کارایی. لەوانەیە لە هەندێک یاری کێشە درووست بکات.</string>
|
<string name="skip_cpu_inner_invalidation_description">هەندێک لە بازنەکردنەکانی هەڵگر لە کاتی نوێکردنەوەی بیرگە دەنێرێت، کەمکردنەوەی بەکارهێنانی CPU و باشترکردنی کارایی. لەوانەیە لە هەندێک یاری کێشە درووست بکات.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">چالاککردنی میمیکردنی MMU میواندە</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">ئەم باشکردنە خێرایی دەستکەوتنی بیرگە لەلایەن پرۆگرامی میوانەکە زیاد دەکات. چالاککردنی وای لێدەکات کە خوێندنەوە/نووسینەکانی بیرگەی میوانەکە ڕاستەوخۆ لە بیرگە ئەنجام بدرێت و میمیکردنی MMU میواندە بەکاربهێنێت. ناچالاککردنی ئەمە هەموو دەستکەوتنەکانی بیرگە ڕەت دەکاتەوە لە بەکارهێنانی میمیکردنی MMU نەرمەکاڵا.</string>
|
||||||
<string name="dma_accuracy">ئاستی DMA</string>
|
<string name="dma_accuracy">ئاستی DMA</string>
|
||||||
<string name="dma_accuracy_description">کۆنتڕۆڵی وردی ڕێکخستنی DMA دەکات. وردی زیاتر دەتوانێ هەندێک کێشە لە هەندێک یاری چارەسەر بکات، بەڵام لە هەندێک حاڵەتدا کاریگەری لەسەر کارایی هەیە. ئەگەر دڵنیا نیت، بە ڕێکخستنی بنەڕەتی بێڵە.</string>
|
<string name="dma_accuracy_description">کۆنتڕۆڵی وردی ڕێکخستنی DMA دەکات. وردی زیاتر دەتوانێ هەندێک کێشە لە هەندێک یاری چارەسەر بکات، بەڵام لە هەندێک حاڵەتدا کاریگەری لەسەر کارایی هەیە. ئەگەر دڵنیا نیت، بە ڕێکخستنی بنەڕەتی بێڵە.</string>
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,8 @@
|
||||||
<string name="cpu_ticks">Takty</string>
|
<string name="cpu_ticks">Takty</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Přeskočit vnitřní invalidaci CPU</string>
|
<string name="skip_cpu_inner_invalidation">Přeskočit vnitřní invalidaci CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Přeskočí některé invalidace mezipaměti na straně CPU během aktualizací paměti, čímž sníží zatížení CPU a zlepší jeho výkon. Může způsobit chyby nebo pády v některých hrách.</string>
|
<string name="skip_cpu_inner_invalidation_description">Přeskočí některé invalidace mezipaměti na straně CPU během aktualizací paměti, čímž sníží zatížení CPU a zlepší jeho výkon. Může způsobit chyby nebo pády v některých hrách.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Povolit emulaci hostitelské MMU</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Tato optimalizace zrychluje přístup do paměti hostovaného programu. Její povolení způsobí, že čtení a zápisy do paměti hosta se provádějí přímo v paměti a využívají hostitelskou MMU. Zakázání této funkce vynutí použití softwarové emulace MMU pro všechny přístupy do paměti.</string>
|
||||||
<string name="dma_accuracy">Úroveň DMA</string>
|
<string name="dma_accuracy">Úroveň DMA</string>
|
||||||
<string name="dma_accuracy_description">Ovládá přesnost DMA. Vyšší přesnost může opravit problémy v některých hrách, ale může také ovlivnit výkon. Pokud si nejste jisti, ponechejte výchozí nastavení.</string>
|
<string name="dma_accuracy_description">Ovládá přesnost DMA. Vyšší přesnost může opravit problémy v některých hrách, ale může také ovlivnit výkon. Pokud si nejste jisti, ponechejte výchozí nastavení.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Ticks</string>
|
<string name="cpu_ticks">Ticks</string>
|
||||||
<string name="skip_cpu_inner_invalidation">CPU-interne Invalidierung überspringen</string>
|
<string name="skip_cpu_inner_invalidation">CPU-interne Invalidierung überspringen</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Überspringt bestimmte Cache-Invalidierungen auf CPU-Seite während Speicherupdates, reduziert die CPU-Auslastung und verbessert die Leistung. Kann in einigen Spielen zu Fehlern oder Abstürzen führen.</string>
|
<string name="skip_cpu_inner_invalidation_description">Überspringt bestimmte Cache-Invalidierungen auf CPU-Seite während Speicherupdates, reduziert die CPU-Auslastung und verbessert die Leistung. Kann in einigen Spielen zu Fehlern oder Abstürzen führen.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Host-MMU-Emulation aktivieren</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Diese Optimierung beschleunigt Speicherzugriffe durch das Gastprogramm. Wenn aktiviert, erfolgen Speicherlese- und -schreibvorgänge des Gastes direkt im Speicher und nutzen die MMU des Hosts. Das Deaktivieren erzwingt die Verwendung der Software-MMU-Emulation für alle Speicherzugriffe.</string>
|
||||||
<string name="dma_accuracy">DMA-Level</string>
|
<string name="dma_accuracy">DMA-Level</string>
|
||||||
<string name="dma_accuracy_description">Steuert die DMA-Präzisionsgenauigkeit. Eine höhere Präzision kann Probleme in einigen Spielen beheben, kann aber in einigen Fällen auch die Leistung beeinträchtigen. Im Zweifel auf „Standard“ belassen.</string>
|
<string name="dma_accuracy_description">Steuert die DMA-Präzisionsgenauigkeit. Eine höhere Präzision kann Probleme in einigen Spielen beheben, kann aber in einigen Fällen auch die Leistung beeinträchtigen. Im Zweifel auf „Standard“ belassen.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Ticks</string>
|
<string name="cpu_ticks">Ticks</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Omitir invalidación interna de la CPU</string>
|
<string name="skip_cpu_inner_invalidation">Omitir invalidación interna de la CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Omite ciertas invalidaciones de caché durante actualizaciones de memoria, reduciendo el uso de CPU y mejorando su rendimiento. Puede causar fallos en algunos juegos.</string>
|
<string name="skip_cpu_inner_invalidation_description">Omite ciertas invalidaciones de caché durante actualizaciones de memoria, reduciendo el uso de CPU y mejorando su rendimiento. Puede causar fallos en algunos juegos.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Habilitar emulación de MMU del host</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Esta optimización acelera los accesos a la memoria por parte del programa invitado. Al habilitarla, las lecturas/escrituras de memoria del invitado se realizan directamente en la memoria y utilizan la MMU del host. Deshabilitar esto obliga a que todos los accesos a la memoria utilicen la emulación de MMU por software.</string>
|
||||||
<string name="dma_accuracy">Nivel de DMA</string>
|
<string name="dma_accuracy">Nivel de DMA</string>
|
||||||
<string name="dma_accuracy_description">Controla la precisión del DMA. Una mayor precisión puede solucionar problemas en algunos juegos, pero también puede afectar el rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.</string>
|
<string name="dma_accuracy_description">Controla la precisión del DMA. Una mayor precisión puede solucionar problemas en algunos juegos, pero también puede afectar el rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">تیکها</string>
|
<string name="cpu_ticks">تیکها</string>
|
||||||
<string name="skip_cpu_inner_invalidation">رد کردن ابطال داخلی CPU</string>
|
<string name="skip_cpu_inner_invalidation">رد کردن ابطال داخلی CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">بعضی ابطالهای حافظه نهان در هنگام بهروزرسانیهای حافظه را رد میکند، استفاده از CPU را کاهش داده و عملکرد آن را بهبود میبخشد. ممکن است در برخی بازیها باعث مشکلات یا خرابی شود.</string>
|
<string name="skip_cpu_inner_invalidation_description">بعضی ابطالهای حافظه نهان در هنگام بهروزرسانیهای حافظه را رد میکند، استفاده از CPU را کاهش داده و عملکرد آن را بهبود میبخشد. ممکن است در برخی بازیها باعث مشکلات یا خرابی شود.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">فعالسازی شبیهسازی MMU میزبان</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">این بهینهسازی دسترسیهای حافظه توسط برنامه میهمان را تسریع میکند. فعالسازی آن باعث میشود خواندن/نوشتن حافظه میهمان مستقیماً در حافظه انجام شود و از MMU میزبان استفاده کند. غیرفعال کردن این قابلیت، همه دسترسیهای حافظه را مجبور به استفاده از شبیهسازی نرمافزاری MMU میکند.</string>
|
||||||
<string name="dma_accuracy">سطح DMA</string>
|
<string name="dma_accuracy">سطح DMA</string>
|
||||||
<string name="dma_accuracy_description">دقت صحت DMA را کنترل می کند. دقت بالاتر می تواند مشکلات برخی بازی ها را برطرف کند، اما در برخی موارد نیز می تواند بر عملکرد تأثیر بگذارد. اگر مطمئن نیستید، آن را روی پیش فرض بگذارید.</string>
|
<string name="dma_accuracy_description">دقت صحت DMA را کنترل می کند. دقت بالاتر می تواند مشکلات برخی بازی ها را برطرف کند، اما در برخی موارد نیز می تواند بر عملکرد تأثیر بگذارد. اگر مطمئن نیستید، آن را روی پیش فرض بگذارید.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Ticks</string>
|
<string name="cpu_ticks">Ticks</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Ignorer l\'invalidation interne du CPU</string>
|
<string name="skip_cpu_inner_invalidation">Ignorer l\'invalidation interne du CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Ignore certaines invalidations de cache côté CPU lors des mises à jour mémoire, réduisant l\'utilisation du CPU et améliorant ses performances. Peut causer des bugs ou plantages sur certains jeux.</string>
|
<string name="skip_cpu_inner_invalidation_description">Ignore certaines invalidations de cache côté CPU lors des mises à jour mémoire, réduisant l\'utilisation du CPU et améliorant ses performances. Peut causer des bugs ou plantages sur certains jeux.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Activer l\'émulation de la MMU hôte</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Cette optimisation accélère les accès mémoire par le programme invité. L\'activer entraîne que les lectures/écritures mémoire de l\'invité sont effectuées directement en mémoire et utilisent la MMU de l\'hôte. Désactiver cela force tous les accès mémoire à utiliser l\'émulation logicielle de la MMU.</string>
|
||||||
<string name="dma_accuracy">Niveau DMA</string>
|
<string name="dma_accuracy">Niveau DMA</string>
|
||||||
<string name="dma_accuracy_description">Contrôle la précision du DMA. Une précision plus élevée peut résoudre les problèmes dans certains jeux, mais peut aussi affecter les performances dans certains cas. Si vous n\'êtes pas sûr, laissez-la sur Défaut.</string>
|
<string name="dma_accuracy_description">Contrôle la précision du DMA. Une précision plus élevée peut résoudre les problèmes dans certains jeux, mais peut aussi affecter les performances dans certains cas. Si vous n\'êtes pas sûr, laissez-la sur Défaut.</string>
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,8 @@
|
||||||
<string name="cpu_ticks">טיקים</string>
|
<string name="cpu_ticks">טיקים</string>
|
||||||
<string name="skip_cpu_inner_invalidation">דלג על איפוס מטמון פנימי של המעבד</string>
|
<string name="skip_cpu_inner_invalidation">דלג על איפוס מטמון פנימי של המעבד</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">מדלג על איפוסי מטמון מסוימים במהלך עדכוני זיכרון, מפחית שימוש במעבד ומשפר ביצועים. עלול לגרום לתקלות או קריסות בחלק מהמשחקים.</string>
|
<string name="skip_cpu_inner_invalidation_description">מדלג על איפוסי מטמון מסוימים במהלך עדכוני זיכרון, מפחית שימוש במעבד ומשפר ביצועים. עלול לגרום לתקלות או קריסות בחלק מהמשחקים.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">הפעל אמולציית MMU מארח</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">אופטימיזציה זו מאיצה את גישת הזיכרון על ידי התוכנית האורחת. הפעלתה גורמת לכך שפעולות קריאה/כתיבה לזיכרון האורח מתבצעות ישירות לזיכרון ומשתמשות ב-MMU של המארח. השבתת זאת מאלצת את כל גישות הזיכרון להשתמש באמולציית MMU תוכנתית.</string>
|
||||||
<string name="dma_accuracy">רמת DMA</string>
|
<string name="dma_accuracy">רמת DMA</string>
|
||||||
<string name="dma_accuracy_description">שולטת בדיוק הדיוק של DMA. דיוק גבוה יותר יכול לתקן בעיות בחלק מהמשחקים, אך הוא עלול גם להשפיע על הביצועים במקרים מסוימים. אם אינך בטוח, השאר ברירת מחדל.</string>
|
<string name="dma_accuracy_description">שולטת בדיוק הדיוק של DMA. דיוק גבוה יותר יכול לתקן בעיות בחלק מהמשחקים, אך הוא עלול גם להשפיע על הביצועים במקרים מסוימים. אם אינך בטוח, השאר ברירת מחדל.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Tick-ek</string>
|
<string name="cpu_ticks">Tick-ek</string>
|
||||||
<string name="skip_cpu_inner_invalidation">CPU belső érvénytelenítés kihagyása</string>
|
<string name="skip_cpu_inner_invalidation">CPU belső érvénytelenítés kihagyása</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Kihagy néhány CPU-oldali gyorsítótár-érvénytelenítést memóriafrissítések közben, csökkentve a CPU használatát és javítva a teljesítményt. Néhány játékban hibákat vagy összeomlást okozhat.</string>
|
<string name="skip_cpu_inner_invalidation_description">Kihagy néhány CPU-oldali gyorsítótár-érvénytelenítést memóriafrissítések közben, csökkentve a CPU használatát és javítva a teljesítményt. Néhány játékban hibákat vagy összeomlást okozhat.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Gazda MMU emuláció engedélyezése</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Ez az optimalizáció gyorsítja a vendégprogram memória-hozzáférését. Engedélyezése esetén a vendég memóriaolvasási/írási műveletei közvetlenül a memóriában történnek, és kihasználják a gazda MMU-ját. Letiltás esetén minden memória-hozzáférés a szoftveres MMU emulációt használja.</string>
|
||||||
<string name="dma_accuracy">DMA szint</string>
|
<string name="dma_accuracy">DMA szint</string>
|
||||||
<string name="dma_accuracy_description">Szabályozza a DMA pontosságát. A magasabb pontosság megoldhat néhány játék problémáit, de bizonyos esetekben befolyásolhatja a teljesítményt. Ha bizonytalan, hagyja Alapértelmezett beállításnál.</string>
|
<string name="dma_accuracy_description">Szabályozza a DMA pontosságát. A magasabb pontosság megoldhat néhány játék problémáit, de bizonyos esetekben befolyásolhatja a teljesítményt. Ha bizonytalan, hagyja Alapértelmezett beállításnál.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Ticks</string>
|
<string name="cpu_ticks">Ticks</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Lewati Pembatalan Internal CPU</string>
|
<string name="skip_cpu_inner_invalidation">Lewati Pembatalan Internal CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Melewati beberapa pembatalan cache sisi CPU selama pembaruan memori, mengurangi penggunaan CPU dan meningkatkan kinerjanya. Mungkin menyebabkan gangguan atau crash pada beberapa game.</string>
|
<string name="skip_cpu_inner_invalidation_description">Melewati beberapa pembatalan cache sisi CPU selama pembaruan memori, mengurangi penggunaan CPU dan meningkatkan kinerjanya. Mungkin menyebabkan gangguan atau crash pada beberapa game.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Aktifkan Emulasi MMU Host</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Optimasi ini mempercepat akses memori oleh program tamu. Mengaktifkannya menyebabkan pembacaan/penulisan memori tamu dilakukan langsung ke memori dan memanfaatkan MMU Host. Menonaktifkan ini memaksa semua akses memori menggunakan Emulasi MMU Perangkat Lunak.</string>
|
||||||
<string name="dma_accuracy">Level DMA</string>
|
<string name="dma_accuracy">Level DMA</string>
|
||||||
<string name="dma_accuracy_description">Mengontrol akurasi presisi DMA. Presisi yang lebih tinggi dapat memperbaiki masalah di beberapa game, tetapi juga dapat memengaruhi performa dalam beberapa kasus. Jika tidak yakin, biarkan di Bawaan.</string>
|
<string name="dma_accuracy_description">Mengontrol akurasi presisi DMA. Presisi yang lebih tinggi dapat memperbaiki masalah di beberapa game, tetapi juga dapat memengaruhi performa dalam beberapa kasus. Jika tidak yakin, biarkan di Bawaan.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Tick</string>
|
<string name="cpu_ticks">Tick</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Salta invalidamento interno CPU</string>
|
<string name="skip_cpu_inner_invalidation">Salta invalidamento interno CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Salta alcuni invalidamenti della cache lato CPU durante gli aggiornamenti di memoria, riducendo l\'uso della CPU e migliorandone le prestazioni. Potrebbe causare glitch o crash in alcuni giochi.</string>
|
<string name="skip_cpu_inner_invalidation_description">Salta alcuni invalidamenti della cache lato CPU durante gli aggiornamenti di memoria, riducendo l\'uso della CPU e migliorandone le prestazioni. Potrebbe causare glitch o crash in alcuni giochi.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Abilita emulazione MMU host</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Questa ottimizzazione accelera gli accessi alla memoria da parte del programma guest. Abilitandola, le letture/scritture della memoria guest vengono eseguite direttamente in memoria e sfruttano la MMU host. Disabilitandola, tutti gli accessi alla memoria sono costretti a utilizzare l\'emulazione software della MMU.</string>
|
||||||
<string name="dma_accuracy">Livello DMA</string>
|
<string name="dma_accuracy">Livello DMA</string>
|
||||||
<string name="dma_accuracy_description">Controlla la precisione del DMA. Una precisione più alta può risolvere problemi in alcuni giochi, ma in alcuni casi può influire sulle prestazioni. Se non sei sicuro, lascia su Predefinito.</string>
|
<string name="dma_accuracy_description">Controlla la precisione del DMA. Una precisione più alta può risolvere problemi in alcuni giochi, ma in alcuni casi può influire sulle prestazioni. Se non sei sicuro, lascia su Predefinito.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">ティック</string>
|
<string name="cpu_ticks">ティック</string>
|
||||||
<string name="skip_cpu_inner_invalidation">CPU内部無効化をスキップ</string>
|
<string name="skip_cpu_inner_invalidation">CPU内部無効化をスキップ</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">メモリ更新時のCPU側キャッシュ無効化をスキップし、CPU使用率を減らして性能を向上させます。一部のゲームで不具合やクラッシュが発生する可能性があります。</string>
|
<string name="skip_cpu_inner_invalidation_description">メモリ更新時のCPU側キャッシュ無効化をスキップし、CPU使用率を減らして性能を向上させます。一部のゲームで不具合やクラッシュが発生する可能性があります。</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">ホストMMUエミュレーションを有効化</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">この最適化により、ゲストプログラムによるメモリアクセスが高速化されます。有効にすると、ゲストのメモリ読み書きが直接メモリ内で実行され、ホストのMMUを利用します。無効にすると、すべてのメモリアクセスでソフトウェアMMUエミュレーションが使用されます。</string>
|
||||||
<string name="dma_accuracy">DMAレベル</string>
|
<string name="dma_accuracy">DMAレベル</string>
|
||||||
<string name="dma_accuracy_description">DMAの精度を制御します。精度を高くすると一部のゲームの問題が修正される場合がありますが、場合によってはパフォーマンスに影響を与える可能性もあります。不明な場合は、デフォルトのままにしてください。</string>
|
<string name="dma_accuracy_description">DMAの精度を制御します。精度を高くすると一部のゲームの問題が修正される場合がありますが、場合によってはパフォーマンスに影響を与える可能性もあります。不明な場合は、デフォルトのままにしてください。</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">틱</string>
|
<string name="cpu_ticks">틱</string>
|
||||||
<string name="skip_cpu_inner_invalidation">CPU 내부 무효화 건너뛰기</string>
|
<string name="skip_cpu_inner_invalidation">CPU 내부 무효화 건너뛰기</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">메모리 업데이트 시 일부 CPU 측 캐시 무효화를 건너뛰어 CPU 사용량을 줄이고 성능을 향상시킵니다. 일부 게임에서 오류 또는 충돌을 일으킬 수 있습니다.</string>
|
<string name="skip_cpu_inner_invalidation_description">메모리 업데이트 시 일부 CPU 측 캐시 무효화를 건너뛰어 CPU 사용량을 줄이고 성능을 향상시킵니다. 일부 게임에서 오류 또는 충돌을 일으킬 수 있습니다.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">호스트 MMU 에뮬레이션 사용</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">이 최적화는 게스트 프로그램의 메모리 접근 속도를 높입니다. 활성화하면 게스트의 메모리 읽기/쓰기가 메모리에서 직접 수행되고 호스트의 MMU를 활용합니다. 비활성화하면 모든 메모리 접근에 소프트웨어 MMU 에뮬레이션을 사용하게 됩니다.</string>
|
||||||
<string name="dma_accuracy">DMA 수준</string>
|
<string name="dma_accuracy">DMA 수준</string>
|
||||||
<string name="dma_accuracy_description">DMA 정밀도를 제어합니다. 높은 정밀도는 일부 게임의 문제를 해결할 수 있지만 경우에 따라 성능에 영향을 미칠 수도 있습니다. 확실하지 않다면 기본값으로 두세요.</string>
|
<string name="dma_accuracy_description">DMA 정밀도를 제어합니다. 높은 정밀도는 일부 게임의 문제를 해결할 수 있지만 경우에 따라 성능에 영향을 미칠 수도 있습니다. 확실하지 않다면 기본값으로 두세요.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Takter</string>
|
<string name="cpu_ticks">Takter</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Hopp over CPU intern invalidering</string>
|
<string name="skip_cpu_inner_invalidation">Hopp over CPU intern invalidering</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Hopper over enkelte CPU-side cache-invalideringer under minneoppdateringer, reduserer CPU-bruk og forbedrer ytelsen. Kan forårsake feil eller krasj i noen spill.</string>
|
<string name="skip_cpu_inner_invalidation_description">Hopper over enkelte CPU-side cache-invalideringer under minneoppdateringer, reduserer CPU-bruk og forbedrer ytelsen. Kan forårsake feil eller krasj i noen spill.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Aktiver verts-MMU-emulering</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Denne optimaliseringen fremskynder minnetilgang av gjesteprogrammet. Hvis aktivert, utføres gjestens minnelesing/skriving direkte i minnet og bruker vertens MMU. Deaktivering tvinger alle minnetilganger til å bruke programvarebasert MMU-emulering.</string>
|
||||||
<string name="dma_accuracy">DMA-nivå</string>
|
<string name="dma_accuracy">DMA-nivå</string>
|
||||||
<string name="dma_accuracy_description">Styrer DMA-presisjonsnøyaktigheten. Høyere presisjon kan fikse problemer i noen spill, men kan også påvirke ytelsen i noen tilfeller. Hvis du er usikker, la den stå på Standard.</string>
|
<string name="dma_accuracy_description">Styrer DMA-presisjonsnøyaktigheten. Høyere presisjon kan fikse problemer i noen spill, men kan også påvirke ytelsen i noen tilfeller. Hvis du er usikker, la den stå på Standard.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Takty</string>
|
<string name="cpu_ticks">Takty</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Pomiń wewnętrzne unieważnienie CPU</string>
|
<string name="skip_cpu_inner_invalidation">Pomiń wewnętrzne unieważnienie CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Pomija niektóre unieważnienia pamięci podręcznej po stronie CPU podczas aktualizacji pamięci, zmniejszając użycie CPU i poprawiając jego wydajność. Może powodować błędy lub awarie w niektórych grach.</string>
|
<string name="skip_cpu_inner_invalidation_description">Pomija niektóre unieważnienia pamięci podręcznej po stronie CPU podczas aktualizacji pamięci, zmniejszając użycie CPU i poprawiając jego wydajność. Może powodować błędy lub awarie w niektórych grach.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Włącz emulację MMU hosta</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Ta optymalizacja przyspiesza dostęp do pamięci przez program gościa. Włączenie powoduje, że odczyty/zapisy pamięci gościa są wykonywane bezpośrednio w pamięci i wykorzystują MMU hosta. Wyłączenie wymusza użycie programowej emulacji MMU dla wszystkich dostępów do pamięci.</string>
|
||||||
<string name="dma_accuracy">Poziom DMA</string>
|
<string name="dma_accuracy">Poziom DMA</string>
|
||||||
<string name="dma_accuracy_description">Kontroluje dokładność precyzji DMA. Wyższy poziom może naprawić problemy w niektórych grach, ale może również wpłynąć na wydajność. Jeśli nie jesteś pewien, pozostaw wartość «Domyślny».</string>
|
<string name="dma_accuracy_description">Kontroluje dokładność precyzji DMA. Wyższy poziom może naprawić problemy w niektórych grach, ale może również wpłynąć na wydajność. Jeśli nie jesteś pewien, pozostaw wartość «Domyślny».</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Ticks</string>
|
<string name="cpu_ticks">Ticks</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Pular invalidação interna da CPU</string>
|
<string name="skip_cpu_inner_invalidation">Pular invalidação interna da CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Ignora algumas invalidações de cache do lado da CPU durante atualizações de memória, reduzindo o uso da CPU e melhorando seu desempenho. Pode causar falhas ou travamentos em alguns jogos.</string>
|
<string name="skip_cpu_inner_invalidation_description">Ignora algumas invalidações de cache do lado da CPU durante atualizações de memória, reduzindo o uso da CPU e melhorando seu desempenho. Pode causar falhas ou travamentos em alguns jogos.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Ativar Emulação de MMU do Host</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Esta otimização acelera os acessos à memória pelo programa convidado. Ativar isso faz com que as leituras/gravações de memória do convidado sejam feitas diretamente na memória e utilizem a MMU do Host. Desativar isso força todos os acessos à memória a usarem a Emulação de MMU por Software.</string>
|
||||||
<string name="dma_accuracy">Nível DMA</string>
|
<string name="dma_accuracy">Nível DMA</string>
|
||||||
<string name="dma_accuracy_description">Controla a precisão do DMA. Maior precisão pode corrigir problemas em alguns jogos, mas também pode impactar o desempenho em alguns casos. Se não tiver certeza, deixe em Padrão.</string>
|
<string name="dma_accuracy_description">Controla a precisão do DMA. Maior precisão pode corrigir problemas em alguns jogos, mas também pode impactar o desempenho em alguns casos. Se não tiver certeza, deixe em Padrão.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Ticks</string>
|
<string name="cpu_ticks">Ticks</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Ignorar invalidação interna da CPU</string>
|
<string name="skip_cpu_inner_invalidation">Ignorar invalidação interna da CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Ignora algumas invalidações de cache do lado da CPU durante atualizações de memória, reduzindo a utilização da CPU e melhorando o desempenho. Pode causar falhas ou crashes em alguns jogos.</string>
|
<string name="skip_cpu_inner_invalidation_description">Ignora algumas invalidações de cache do lado da CPU durante atualizações de memória, reduzindo a utilização da CPU e melhorando o desempenho. Pode causar falhas ou crashes em alguns jogos.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Ativar Emulação de MMU do Anfitrião</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Esta otimização acelera os acessos à memória pelo programa convidado. Ativar faz com que as leituras/escritas de memória do convidado sejam efetuadas diretamente na memória e utilizem a MMU do Anfitrião. Desativar força todos os acessos à memória a usar a Emulação de MMU por Software.</string>
|
||||||
<string name="dma_accuracy">Nível DMA</string>
|
<string name="dma_accuracy">Nível DMA</string>
|
||||||
<string name="dma_accuracy_description">Controla a precisão do DMA. Maior precisão pode corrigir problemas em alguns jogos, mas também pode afetar o desempenho nalguns casos. Se não tiver a certeza, deixe em Predefinido.</string>
|
<string name="dma_accuracy_description">Controla a precisão do DMA. Maior precisão pode corrigir problemas em alguns jogos, mas também pode afetar o desempenho nalguns casos. Se não tiver a certeza, deixe em Predefinido.</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Такты</string>
|
<string name="cpu_ticks">Такты</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string>
|
<string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string>
|
<string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Включить эмуляцию MMU хоста</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Эта оптимизация ускоряет доступ к памяти гостевой программой. При включении операции чтения/записи памяти гостя выполняются напрямую в памяти с использованием MMU хоста. Отключение заставляет все обращения к памяти использовать программную эмуляцию MMU.</string>
|
||||||
<string name="dma_accuracy">Уровень DMA</string>
|
<string name="dma_accuracy">Уровень DMA</string>
|
||||||
<string name="dma_accuracy_description">Управляет точностью DMA. Более высокий уровень может исправить проблемы в некоторых играх, но также может повлиять на производительность. Если не уверены, оставьте значение «По умолчанию».</string>
|
<string name="dma_accuracy_description">Управляет точностью DMA. Более высокий уровень может исправить проблемы в некоторых играх, но также может повлиять на производительность. Если не уверены, оставьте значение «По умолчанию».</string>
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,8 @@
|
||||||
<string name="cpu_ticks">Тактови</string>
|
<string name="cpu_ticks">Тактови</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Preskoči unutrašnje poništavanje CPU-a</string>
|
<string name="skip_cpu_inner_invalidation">Preskoči unutrašnje poništavanje CPU-a</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Preskače određena poništavanja keša na strani CPU-a tokom ažuriranja memorije, smanjujući opterećenje procesora i poboljšavajući performanse. Može izazvati greške u nekim igrama.</string>
|
<string name="skip_cpu_inner_invalidation_description">Preskače određena poništavanja keša na strani CPU-a tokom ažuriranja memorije, smanjujući opterećenje procesora i poboljšavajući performanse. Može izazvati greške u nekim igrama.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Омогући емулацију MMU домаћина</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Ова оптимизација убрзава приступ меморији од стране гостујућег програма. Укључивање изазива да се читања/уписа меморије госта обављају директно у меморији и користе MMU домаћина. Искључивање присиљава све приступе меморији да користе софтверску емулацију MMU.</string>
|
||||||
<string name="dma_accuracy">DMA ниво</string>
|
<string name="dma_accuracy">DMA ниво</string>
|
||||||
<string name="dma_accuracy_description">Контролише тачност DMA прецизности. Виши ниво може да поправи проблеме у неким играма, али може и да утиче на перформансе. Ако нисте сигурни, оставите на «Подразумевано».</string>
|
<string name="dma_accuracy_description">Контролише тачност DMA прецизности. Виши ниво може да поправи проблеме у неким играма, али може и да утиче на перформансе. Ако нисте сигурни, оставите на «Подразумевано».</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Такти</string>
|
<string name="cpu_ticks">Такти</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Пропустити внутрішнє інвалідування CPU</string>
|
<string name="skip_cpu_inner_invalidation">Пропустити внутрішнє інвалідування CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Пропускає деякі інвалідації кешу на стороні CPU під час оновлення пам\'яті, зменшуючи навантаження на процесор і покращуючи продуктивність. Може спричинити збої в деяких іграх.</string>
|
<string name="skip_cpu_inner_invalidation_description">Пропускає деякі інвалідації кешу на стороні CPU під час оновлення пам\'яті, зменшуючи навантаження на процесор і покращуючи продуктивність. Може спричинити збої в деяких іграх.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Увімкнути емуляцію MMU хоста</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Ця оптимізація пришвидшує доступ до пам\'яті гостьовою програмою. Увімкнення призводить до того, що читання/запис пам\'яті гостя виконуються безпосередньо в пам\'яті та використовують MMU хоста. Вимкнення змушує всі звернення до пам\'яті використовувати програмну емуляцію MMU.</string>
|
||||||
<string name="dma_accuracy">Рівень DMA</string>
|
<string name="dma_accuracy">Рівень DMA</string>
|
||||||
<string name="dma_accuracy_description">Керує точністю DMA. Вищий рівень може виправити проблеми в деяких іграх, але також може вплинути на продуктивність. Якщо не впевнені, залиште значення «Типово».</string>
|
<string name="dma_accuracy_description">Керує точністю DMA. Вищий рівень може виправити проблеми в деяких іграх, але також може вплинути на продуктивність. Якщо не впевнені, залиште значення «Типово».</string>
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
<string name="cpu_ticks">Tích</string>
|
<string name="cpu_ticks">Tích</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Bỏ qua vô hiệu hóa bên trong CPU</string>
|
<string name="skip_cpu_inner_invalidation">Bỏ qua vô hiệu hóa bên trong CPU</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Bỏ qua một số lần vô hiệu hóa bộ nhớ đệm phía CPU trong khi cập nhật bộ nhớ, giảm mức sử dụng CPU và cải thiện hiệu suất. Có thể gây ra lỗi hoặc treo máy trong một số trò chơi.</string>
|
<string name="skip_cpu_inner_invalidation_description">Bỏ qua một số lần vô hiệu hóa bộ nhớ đệm phía CPU trong khi cập nhật bộ nhớ, giảm mức sử dụng CPU và cải thiện hiệu suất. Có thể gây ra lỗi hoặc treo máy trong một số trò chơi.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Bật giả lập MMU Máy chủ</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">Tối ưu hóa này tăng tốc độ truy cập bộ nhớ của chương trình khách. Bật nó lên khiến các thao tác đọc/ghi bộ nhớ khách được thực hiện trực tiếp vào bộ nhớ và sử dụng MMU của Máy chủ. Tắt tính năng này buộc tất cả quyền truy cập bộ nhớ phải sử dụng Giả lập MMU Phần mềm.</string>
|
||||||
<string name="dma_accuracy">Cấp độ DMA</string>
|
<string name="dma_accuracy">Cấp độ DMA</string>
|
||||||
<string name="dma_accuracy_description">Điều khiển độ chính xác của DMA. Độ chính xác cao hơn có thể sửa lỗi trong một số trò chơi, nhưng cũng có thể ảnh hưởng đến hiệu suất trong một số trường hợp. Nếu không chắc chắn, hãy để ở Mặc định.</string>
|
<string name="dma_accuracy_description">Điều khiển độ chính xác của DMA. Độ chính xác cao hơn có thể sửa lỗi trong một số trò chơi, nhưng cũng có thể ảnh hưởng đến hiệu suất trong một số trường hợp. Nếu không chắc chắn, hãy để ở Mặc định.</string>
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,8 @@
|
||||||
<string name="cpu_ticks">时钟</string>
|
<string name="cpu_ticks">时钟</string>
|
||||||
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
|
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string>
|
<string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">启用主机 MMU 模拟</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">此优化可加速来宾程序的内存访问。启用后,来宾内存读取/写入将直接在内存中执行并利用主机的 MMU。禁用此功能将强制所有内存访问使用软件 MMU 模拟。</string>
|
||||||
<string name="dma_accuracy">DMA 级别</string>
|
<string name="dma_accuracy">DMA 级别</string>
|
||||||
<string name="dma_accuracy_description">控制 DMA 精度。更高的精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。</string>
|
<string name="dma_accuracy_description">控制 DMA 精度。更高的精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。</string>
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,8 @@
|
||||||
<string name="cpu_ticks">時脈</string>
|
<string name="cpu_ticks">時脈</string>
|
||||||
<string name="skip_cpu_inner_invalidation">跳過CPU內部無效化</string>
|
<string name="skip_cpu_inner_invalidation">跳過CPU內部無效化</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">在記憶體更新期間跳過某些CPU端快取無效化,減少CPU使用率並提高其性能。可能會導致某些遊戲出現故障或崩潰。</string>
|
<string name="skip_cpu_inner_invalidation_description">在記憶體更新期間跳過某些CPU端快取無效化,減少CPU使用率並提高其性能。可能會導致某些遊戲出現故障或崩潰。</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">啟用主機 MMU 模擬</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">此最佳化可加速來賓程式的記憶體存取。啟用後,來賓記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬。</string>
|
||||||
<string name="dma_accuracy">DMA 級別</string>
|
<string name="dma_accuracy">DMA 級別</string>
|
||||||
<string name="dma_accuracy_description">控制 DMA 精確度。更高的精確度可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為「預設」。</string>
|
<string name="dma_accuracy_description">控制 DMA 精確度。更高的精確度可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為「預設」。</string>
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@
|
||||||
<string name="cpu_ticks">Ticks</string>
|
<string name="cpu_ticks">Ticks</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
|
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu">Enable Host MMU Emulation</string>
|
||||||
|
<string name="cpuopt_unsafe_host_mmu_description">This optimization speeds up memory accesses by the guest program. Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host\'s MMU. Disabling this forces all memory accesses to use Software MMU Emulation.</string>
|
||||||
<string name="fast_cpu_time">CPU Clock</string>
|
<string name="fast_cpu_time">CPU Clock</string>
|
||||||
<string name="fast_cpu_time_description">Use Boost (1700MHz) to run at the Switch\'s highest native clock, or Fast (2000MHz) to run at 2x clock.</string>
|
<string name="fast_cpu_time_description">Use Boost (1700MHz) to run at the Switch\'s highest native clock, or Fast (2000MHz) to run at 2x clock.</string>
|
||||||
<string name="memory_layout">Memory Layout</string>
|
<string name="memory_layout">Memory Layout</string>
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -148,7 +151,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out
|
||||||
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
|
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
|
||||||
OpusPacketHeader header{ReverseHeader(*header_p)};
|
OpusPacketHeader header{ReverseHeader(*header_p)};
|
||||||
|
|
||||||
LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
|
LOG_TRACE(Service_Audio, "header size {:#X} input data size 0x{:X} in_data size 0x{:X}",
|
||||||
header.size, input_data.size_bytes(), in_data.size_bytes());
|
header.size, input_data.size_bytes(), in_data.size_bytes());
|
||||||
|
|
||||||
R_UNLESS(in_data.size_bytes() >= header.size &&
|
R_UNLESS(in_data.size_bytes() >= header.size &&
|
||||||
|
|
|
@ -262,13 +262,13 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
|
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(common PUBLIC Boost::headers)
|
target_link_libraries(common PUBLIC Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (lz4_ADDED)
|
if (lz4_ADDED)
|
||||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
|
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
|
||||||
|
@ -280,6 +280,11 @@ else()
|
||||||
target_link_libraries(common PRIVATE zstd)
|
target_link_libraries(common PRIVATE zstd)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (TARGET unordered_dense::unordered_dense)
|
||||||
|
# weird quirk of system installs
|
||||||
|
target_link_libraries(common PUBLIC unordered_dense::unordered_dense)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
# For ASharedMemory_create
|
# For ASharedMemory_create
|
||||||
target_link_libraries(common PRIVATE android)
|
target_link_libraries(common PRIVATE android)
|
||||||
|
|
|
@ -517,24 +517,4 @@ std::string_view GetPathWithoutTop(std::string_view path) {
|
||||||
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
|
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view GetFilename(std::string_view path) {
|
|
||||||
const auto name_index = path.find_last_of("\\/");
|
|
||||||
|
|
||||||
if (name_index == std::string_view::npos) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.substr(name_index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view GetExtensionFromFilename(std::string_view name) {
|
|
||||||
const std::size_t index = name.rfind('.');
|
|
||||||
|
|
||||||
if (index == std::string_view::npos) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return name.substr(index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common::FS
|
} // namespace Common::FS
|
||||||
|
|
|
@ -352,9 +352,17 @@ enum class DirectorySeparator {
|
||||||
[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
|
[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
|
||||||
|
|
||||||
// Gets the filename of the path
|
// Gets the filename of the path
|
||||||
[[nodiscard]] std::string_view GetFilename(std::string_view path);
|
[[nodiscard]] inline std::string_view GetFilename(const std::string_view path) noexcept {
|
||||||
|
if (auto const name_index = path.find_last_of("\\/"); name_index != std::string_view::npos)
|
||||||
|
return path.substr(name_index + 1);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Gets the extension of the filename
|
// Gets the extension of the filename
|
||||||
[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
|
[[nodiscard]] inline std::string_view GetExtensionFromFilename(const std::string_view name) noexcept {
|
||||||
|
if (auto const index = name.rfind('.'); index != std::string_view::npos)
|
||||||
|
return name.substr(index + 1);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common::FS
|
} // namespace Common::FS
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/heap_tracker.h"
|
#include "common/heap_tracker.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
@ -34,68 +35,60 @@ HeapTracker::~HeapTracker() = default;
|
||||||
|
|
||||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||||
MemoryPermission perm, bool is_separate_heap) {
|
MemoryPermission perm, bool is_separate_heap) {
|
||||||
|
bool rebuild_required = false;
|
||||||
// When mapping other memory, map pages immediately.
|
// When mapping other memory, map pages immediately.
|
||||||
if (!is_separate_heap) {
|
if (!is_separate_heap) {
|
||||||
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
|
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// We are mapping part of a separate heap.
|
// We are mapping part of a separate heap and insert into mappings.
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
|
m_map_count++;
|
||||||
auto* const map = new SeparateHeapMap{
|
const auto it = m_mappings.insert_or_assign(virtual_offset, SeparateHeapMap{
|
||||||
.vaddr = virtual_offset,
|
|
||||||
.paddr = host_offset,
|
.paddr = host_offset,
|
||||||
.size = length,
|
.size = length,
|
||||||
.tick = m_tick++,
|
.tick = m_tick++,
|
||||||
.perm = perm,
|
.perm = perm,
|
||||||
.is_resident = false,
|
.is_resident = false,
|
||||||
};
|
});
|
||||||
|
// Update tick before possible rebuild.
|
||||||
// Insert into mappings.
|
it.first->second.tick = m_tick++;
|
||||||
m_map_count++;
|
// Check if we need to rebuild.
|
||||||
m_mappings.insert(*map);
|
if (m_resident_map_count >= m_max_resident_map_count)
|
||||||
|
rebuild_required = true;
|
||||||
|
// Map the area.
|
||||||
|
m_buffer.Map(it.first->first, it.first->second.paddr, it.first->second.size, it.first->second.perm, false);
|
||||||
|
// This map is now resident.
|
||||||
|
it.first->second.is_resident = true;
|
||||||
|
m_resident_map_count++;
|
||||||
|
m_resident_mappings.insert(*it.first);
|
||||||
}
|
}
|
||||||
|
// A rebuild was required, so perform it now.
|
||||||
// Finally, map.
|
if (rebuild_required)
|
||||||
this->DeferredMapSeparateHeap(virtual_offset);
|
this->RebuildSeparateHeapAddressSpace();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
||||||
// If this is a separate heap...
|
// If this is a separate heap...
|
||||||
if (is_separate_heap) {
|
if (is_separate_heap) {
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
const SeparateHeapMap key{
|
|
||||||
.vaddr = virtual_offset,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Split at the boundaries of the region we are removing.
|
// Split at the boundaries of the region we are removing.
|
||||||
this->SplitHeapMapLocked(virtual_offset);
|
this->SplitHeapMapLocked(virtual_offset);
|
||||||
this->SplitHeapMapLocked(virtual_offset + size);
|
this->SplitHeapMapLocked(virtual_offset + size);
|
||||||
|
|
||||||
// Erase all mappings in range.
|
// Erase all mappings in range.
|
||||||
auto it = m_mappings.find(key);
|
auto it = m_mappings.find(virtual_offset);
|
||||||
while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
|
while (it != m_mappings.end() && it->first < virtual_offset + size) {
|
||||||
// Get underlying item.
|
|
||||||
auto* const item = std::addressof(*it);
|
|
||||||
|
|
||||||
// If resident, erase from resident map.
|
// If resident, erase from resident map.
|
||||||
if (item->is_resident) {
|
if (it->second.is_resident) {
|
||||||
ASSERT(--m_resident_map_count >= 0);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
m_resident_mappings.erase(m_resident_mappings.find(it->first));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erase from map.
|
// Erase from map.
|
||||||
ASSERT(--m_map_count >= 0);
|
ASSERT(--m_map_count >= 0);
|
||||||
it = m_mappings.erase(it);
|
it = m_mappings.erase(it);
|
||||||
|
|
||||||
// Free the item.
|
|
||||||
delete item;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmap pages.
|
// Unmap pages.
|
||||||
m_buffer.Unmap(virtual_offset, size, false);
|
m_buffer.Unmap(virtual_offset, size, false);
|
||||||
}
|
}
|
||||||
|
@ -117,110 +110,51 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lk2{m_lock};
|
std::scoped_lock lk2{m_lock};
|
||||||
|
|
||||||
const SeparateHeapMap key{
|
|
||||||
.vaddr = next,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Try to get the next mapping corresponding to this address.
|
// Try to get the next mapping corresponding to this address.
|
||||||
const auto it = m_mappings.nfind(key);
|
const auto it = m_mappings.find(next);
|
||||||
|
|
||||||
if (it == m_mappings.end()) {
|
if (it == m_mappings.end()) {
|
||||||
// There are no separate heap mappings remaining.
|
// There are no separate heap mappings remaining.
|
||||||
next = end;
|
next = end;
|
||||||
should_protect = true;
|
should_protect = true;
|
||||||
} else if (it->vaddr == cur) {
|
} else if (it->first == cur) {
|
||||||
// We are in range.
|
// We are in range.
|
||||||
// Update permission bits.
|
// Update permission bits.
|
||||||
it->perm = perm;
|
it->second.perm = perm;
|
||||||
|
|
||||||
// Determine next address and whether we should protect.
|
// Determine next address and whether we should protect.
|
||||||
next = cur + it->size;
|
next = cur + it->second.size;
|
||||||
should_protect = it->is_resident;
|
should_protect = it->second.is_resident;
|
||||||
} else /* if (it->vaddr > cur) */ {
|
} else /* if (it->vaddr > cur) */ {
|
||||||
// We weren't in range, but there is a block coming up that will be.
|
// We weren't in range, but there is a block coming up that will be.
|
||||||
next = it->vaddr;
|
next = it->first;
|
||||||
should_protect = true;
|
should_protect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp to end.
|
// Clamp to end.
|
||||||
next = std::min(next, end);
|
next = std::min(next, end);
|
||||||
|
|
||||||
// Reprotect, if we need to.
|
// Reprotect, if we need to.
|
||||||
if (should_protect) {
|
if (should_protect)
|
||||||
m_buffer.Protect(cur, next - cur, perm);
|
m_buffer.Protect(cur, next - cur, perm);
|
||||||
}
|
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
cur = next;
|
cur = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
|
|
||||||
if (m_buffer.IsInVirtualRange(fault_address)) {
|
|
||||||
return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
|
||||||
bool rebuild_required = false;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock lk{m_lock};
|
|
||||||
|
|
||||||
// Check to ensure this was a non-resident separate heap mapping.
|
|
||||||
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
|
|
||||||
if (it == m_mappings.end() || it->is_resident) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update tick before possible rebuild.
|
|
||||||
it->tick = m_tick++;
|
|
||||||
|
|
||||||
// Check if we need to rebuild.
|
|
||||||
if (m_resident_map_count > m_max_resident_map_count) {
|
|
||||||
rebuild_required = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map the area.
|
|
||||||
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
|
|
||||||
|
|
||||||
// This map is now resident.
|
|
||||||
it->is_resident = true;
|
|
||||||
m_resident_map_count++;
|
|
||||||
m_resident_mappings.insert(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rebuild_required) {
|
|
||||||
// A rebuild was required, so perform it now.
|
|
||||||
this->RebuildSeparateHeapAddressSpace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||||
|
|
||||||
ASSERT(!m_resident_mappings.empty());
|
ASSERT(!m_resident_mappings.empty());
|
||||||
|
|
||||||
// Dump half of the mappings.
|
// Dump half of the mappings.
|
||||||
//
|
|
||||||
// Despite being worse in theory, this has proven to be better in practice than more
|
// Despite being worse in theory, this has proven to be better in practice than more
|
||||||
// regularly dumping a smaller amount, because it significantly reduces average case
|
// regularly dumping a smaller amount, because it significantly reduces average case
|
||||||
// lock contention.
|
// lock contention.
|
||||||
const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
|
std::size_t const desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||||
const size_t evict_count = m_resident_map_count - desired_count;
|
std::size_t const evict_count = m_resident_map_count - desired_count;
|
||||||
auto it = m_resident_mappings.begin();
|
auto it = m_resident_mappings.begin();
|
||||||
|
for (std::size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
||||||
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
|
||||||
// Unmark and unmap.
|
// Unmark and unmap.
|
||||||
it->is_resident = false;
|
it->second.is_resident = false;
|
||||||
m_buffer.Unmap(it->vaddr, it->size, false);
|
m_buffer.Unmap(it->first, it->second.size, false);
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
ASSERT(--m_resident_map_count >= 0);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
it = m_resident_mappings.erase(it);
|
it = m_resident_mappings.erase(it);
|
||||||
|
@ -229,53 +163,32 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||||
|
|
||||||
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
|
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
this->SplitHeapMapLocked(offset);
|
this->SplitHeapMapLocked(offset);
|
||||||
this->SplitHeapMapLocked(offset + size);
|
this->SplitHeapMapLocked(offset + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||||
const auto it = this->GetNearestHeapMapLocked(offset);
|
auto it = this->GetNearestHeapMapLocked(offset);
|
||||||
if (it == m_mappings.end() || it->vaddr == offset) {
|
if (it != m_mappings.end() && it->first != offset) {
|
||||||
// Not contained or no split required.
|
// Adjust left iterator
|
||||||
return;
|
auto const orig_size = it->second.size;
|
||||||
|
auto const left_size = offset - it->first;
|
||||||
|
it->second.size = left_size;
|
||||||
|
// Insert the new right map.
|
||||||
|
auto const right = SeparateHeapMap{
|
||||||
|
.paddr = it->second.paddr + left_size,
|
||||||
|
.size = orig_size - left_size,
|
||||||
|
.tick = it->second.tick,
|
||||||
|
.perm = it->second.perm,
|
||||||
|
.is_resident = it->second.is_resident,
|
||||||
|
};
|
||||||
|
m_map_count++;
|
||||||
|
auto rit = m_mappings.insert_or_assign(it->first + left_size, right);
|
||||||
|
if (rit.first->second.is_resident) {
|
||||||
|
m_resident_map_count++;
|
||||||
|
m_resident_mappings.insert(*rit.first);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the original values.
|
|
||||||
auto* const left = std::addressof(*it);
|
|
||||||
const size_t orig_size = left->size;
|
|
||||||
|
|
||||||
// Adjust the left map.
|
|
||||||
const size_t left_size = offset - left->vaddr;
|
|
||||||
left->size = left_size;
|
|
||||||
|
|
||||||
// Create the new right map.
|
|
||||||
auto* const right = new SeparateHeapMap{
|
|
||||||
.vaddr = left->vaddr + left_size,
|
|
||||||
.paddr = left->paddr + left_size,
|
|
||||||
.size = orig_size - left_size,
|
|
||||||
.tick = left->tick,
|
|
||||||
.perm = left->perm,
|
|
||||||
.is_resident = left->is_resident,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Insert the new right map.
|
|
||||||
m_map_count++;
|
|
||||||
m_mappings.insert(*right);
|
|
||||||
|
|
||||||
// If resident, also insert into resident map.
|
|
||||||
if (right->is_resident) {
|
|
||||||
m_resident_map_count++;
|
|
||||||
m_resident_mappings.insert(*right);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
|
||||||
const SeparateHeapMap key{
|
|
||||||
.vaddr = offset,
|
|
||||||
};
|
|
||||||
|
|
||||||
return m_mappings.find(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -1,93 +1,55 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <set>
|
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
#include <ankerl/unordered_dense.h>
|
||||||
#include "common/host_memory.h"
|
#include "common/host_memory.h"
|
||||||
#include "common/intrusive_red_black_tree.h"
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
struct SeparateHeapMap {
|
struct SeparateHeapMap {
|
||||||
Common::IntrusiveRedBlackTreeNode addr_node{};
|
PAddr paddr{}; //8
|
||||||
Common::IntrusiveRedBlackTreeNode tick_node{};
|
std::size_t size{}; //8 (16)
|
||||||
VAddr vaddr{};
|
std::size_t tick{}; //8 (24)
|
||||||
PAddr paddr{};
|
// 4 bits needed, sync with host_memory.h if needed
|
||||||
size_t size{};
|
MemoryPermission perm : 4 = MemoryPermission::Read;
|
||||||
size_t tick{};
|
bool is_resident : 1 = false;
|
||||||
MemoryPermission perm{};
|
|
||||||
bool is_resident{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SeparateHeapMapAddrComparator {
|
|
||||||
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
|
|
||||||
if (lhs.vaddr < rhs.vaddr) {
|
|
||||||
return -1;
|
|
||||||
} else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SeparateHeapMapTickComparator {
|
|
||||||
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
|
|
||||||
if (lhs.tick < rhs.tick) {
|
|
||||||
return -1;
|
|
||||||
} else if (lhs.tick > rhs.tick) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(SeparateHeapMap) == 32); //half a cache line! good for coherency
|
||||||
|
|
||||||
class HeapTracker {
|
class HeapTracker {
|
||||||
public:
|
public:
|
||||||
explicit HeapTracker(Common::HostMemory& buffer);
|
explicit HeapTracker(Common::HostMemory& buffer);
|
||||||
~HeapTracker();
|
~HeapTracker();
|
||||||
|
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm, bool is_separate_heap);
|
||||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
|
|
||||||
bool is_separate_heap);
|
|
||||||
void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
|
void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
|
||||||
void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
|
void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
|
||||||
u8* VirtualBasePointer() {
|
inline u8* VirtualBasePointer() noexcept {
|
||||||
return m_buffer.VirtualBasePointer();
|
return m_buffer.VirtualBasePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeferredMapSeparateHeap(u8* fault_address);
|
|
||||||
bool DeferredMapSeparateHeap(size_t virtual_offset);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using AddrTreeTraits =
|
// TODO: You may want to "fake-map" the first 2GB of 64-bit address space
|
||||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
|
// and dedicate it entirely to a recursive PTE mapping :)
|
||||||
using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
|
// However Ankerl is way better than using an RB tree, in all senses
|
||||||
|
using AddrTree = ankerl::unordered_dense::map<VAddr, SeparateHeapMap>;
|
||||||
using TickTreeTraits =
|
AddrTree m_mappings;
|
||||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
|
using TicksTree = ankerl::unordered_dense::map<VAddr, SeparateHeapMap>;
|
||||||
using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
|
TicksTree m_resident_mappings;
|
||||||
|
|
||||||
AddrTree m_mappings{};
|
|
||||||
TickTree m_resident_mappings{};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SplitHeapMap(VAddr offset, size_t size);
|
void SplitHeapMap(VAddr offset, size_t size);
|
||||||
void SplitHeapMapLocked(VAddr offset);
|
void SplitHeapMapLocked(VAddr offset);
|
||||||
|
|
||||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
|
||||||
|
|
||||||
void RebuildSeparateHeapAddressSpace();
|
void RebuildSeparateHeapAddressSpace();
|
||||||
|
inline HeapTracker::AddrTree::iterator GetNearestHeapMapLocked(VAddr offset) noexcept {
|
||||||
|
return m_mappings.find(offset);
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
Common::HostMemory& m_buffer;
|
Common::HostMemory& m_buffer;
|
||||||
const s64 m_max_resident_map_count;
|
const s64 m_max_resident_map_count;
|
||||||
|
|
||||||
std::shared_mutex m_rebuild_lock{};
|
std::shared_mutex m_rebuild_lock{};
|
||||||
std::mutex m_lock{};
|
std::mutex m_lock{};
|
||||||
s64 m_map_count{};
|
s64 m_map_count{};
|
||||||
|
|
|
@ -477,19 +477,9 @@ class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size{backing_size_}, virtual_size{virtual_size_} {
|
: backing_size{backing_size_}, virtual_size{virtual_size_} {
|
||||||
bool good = false;
|
|
||||||
SCOPE_EXIT {
|
|
||||||
if (!good) {
|
|
||||||
Release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
long page_size = sysconf(_SC_PAGESIZE);
|
long page_size = sysconf(_SC_PAGESIZE);
|
||||||
if (page_size != 0x1000) {
|
ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging",
|
||||||
LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
|
page_size);
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backing memory initialization
|
// Backing memory initialization
|
||||||
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
|
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
|
||||||
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||||
|
@ -501,38 +491,23 @@ public:
|
||||||
#else
|
#else
|
||||||
fd = memfd_create("HostMemory", 0);
|
fd = memfd_create("HostMemory", 0);
|
||||||
#endif
|
#endif
|
||||||
if (fd < 0) {
|
ASSERT_MSG(fd >= 0, "memfd_create failed: {}", strerror(errno));
|
||||||
LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
|
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defined to extend the file with zeros
|
// Defined to extend the file with zeros
|
||||||
int ret = ftruncate(fd, backing_size);
|
int ret = ftruncate(fd, backing_size);
|
||||||
if (ret != 0) {
|
ASSERT_MSG(ret == 0, "ftruncate failed with {}, are you out-of-memory?", strerror(errno));
|
||||||
LOG_CRITICAL(HW_Memory, "ftruncate failed with {}, are you out-of-memory?",
|
|
||||||
strerror(errno));
|
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
|
|
||||||
backing_base = static_cast<u8*>(
|
backing_base = static_cast<u8*>(
|
||||||
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||||
if (backing_base == MAP_FAILED) {
|
ASSERT_MSG(backing_base != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Virtual memory initialization
|
// Virtual memory initialization
|
||||||
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
||||||
if (virtual_base == MAP_FAILED) {
|
ASSERT_MSG(virtual_base != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
|
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
free_manager.SetAddressSpace(virtual_base, virtual_size);
|
free_manager.SetAddressSpace(virtual_base, virtual_size);
|
||||||
good = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~Impl() {
|
~Impl() {
|
||||||
|
@ -673,10 +648,9 @@ private:
|
||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
|
explicit Impl([[maybe_unused]] size_t backing_size, [[maybe_unused]] size_t virtual_size) {
|
||||||
// This is just a place holder.
|
// This is just a place holder.
|
||||||
// Please implement fastmem in a proper way on your platform.
|
ASSERT_MSG(false, "Please implement fastmem in a proper way on your platform.");
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {}
|
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -121,7 +124,7 @@ public:
|
||||||
bytes_written += file->WriteString(message);
|
bytes_written += file->WriteString(message);
|
||||||
|
|
||||||
// Option to log each line rather than 4k buffers
|
// Option to log each line rather than 4k buffers
|
||||||
if (Settings::values.log_flush_lines.GetValue()) {
|
if (Settings::values.log_flush_line.GetValue()) {
|
||||||
file->Flush();
|
file->Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -37,10 +39,10 @@ public:
|
||||||
/// @param slot_count Number of slots to push
|
/// @param slot_count Number of slots to push
|
||||||
/// @returns The number of slots actually pushed
|
/// @returns The number of slots actually pushed
|
||||||
std::size_t Push(const void* new_slots, std::size_t slot_count) {
|
std::size_t Push(const void* new_slots, std::size_t slot_count) {
|
||||||
const std::size_t write_index = m_write_index.load();
|
std::lock_guard lock(rb_mutex);
|
||||||
const std::size_t slots_free = capacity + m_read_index.load() - write_index;
|
|
||||||
const std::size_t push_count = std::min(slot_count, slots_free);
|
|
||||||
|
|
||||||
|
const std::size_t slots_free = capacity + read_index - write_index;
|
||||||
|
const std::size_t push_count = std::min(slot_count, slots_free);
|
||||||
const std::size_t pos = write_index % capacity;
|
const std::size_t pos = write_index % capacity;
|
||||||
const std::size_t first_copy = std::min(capacity - pos, push_count);
|
const std::size_t first_copy = std::min(capacity - pos, push_count);
|
||||||
const std::size_t second_copy = push_count - first_copy;
|
const std::size_t second_copy = push_count - first_copy;
|
||||||
|
@ -50,8 +52,7 @@ public:
|
||||||
in += first_copy * slot_size;
|
in += first_copy * slot_size;
|
||||||
std::memcpy(m_data.data(), in, second_copy * slot_size);
|
std::memcpy(m_data.data(), in, second_copy * slot_size);
|
||||||
|
|
||||||
m_write_index.store(write_index + push_count);
|
write_index = write_index + push_count;
|
||||||
|
|
||||||
return push_count;
|
return push_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +65,10 @@ public:
|
||||||
/// @param max_slots Maximum number of slots to pop
|
/// @param max_slots Maximum number of slots to pop
|
||||||
/// @returns The number of slots actually popped
|
/// @returns The number of slots actually popped
|
||||||
std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
|
std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
|
||||||
const std::size_t read_index = m_read_index.load();
|
std::lock_guard lock(rb_mutex);
|
||||||
const std::size_t slots_filled = m_write_index.load() - read_index;
|
|
||||||
const std::size_t pop_count = std::min(slots_filled, max_slots);
|
|
||||||
|
|
||||||
|
const std::size_t slots_filled = write_index - read_index;
|
||||||
|
const std::size_t pop_count = std::min(slots_filled, max_slots);
|
||||||
const std::size_t pos = read_index % capacity;
|
const std::size_t pos = read_index % capacity;
|
||||||
const std::size_t first_copy = std::min(capacity - pos, pop_count);
|
const std::size_t first_copy = std::min(capacity - pos, pop_count);
|
||||||
const std::size_t second_copy = pop_count - first_copy;
|
const std::size_t second_copy = pop_count - first_copy;
|
||||||
|
@ -77,8 +78,7 @@ public:
|
||||||
out += first_copy * slot_size;
|
out += first_copy * slot_size;
|
||||||
std::memcpy(out, m_data.data(), second_copy * slot_size);
|
std::memcpy(out, m_data.data(), second_copy * slot_size);
|
||||||
|
|
||||||
m_read_index.store(read_index + pop_count);
|
read_index = read_index + pop_count;
|
||||||
|
|
||||||
return pop_count;
|
return pop_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,29 +90,21 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns Number of slots used
|
/// @returns Number of slots used
|
||||||
[[nodiscard]] std::size_t Size() const {
|
[[nodiscard]] inline std::size_t Size() const {
|
||||||
return m_write_index.load() - m_read_index.load();
|
return write_index - read_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns Maximum size of ring buffer
|
/// @returns Maximum size of ring buffer
|
||||||
[[nodiscard]] constexpr std::size_t Capacity() const {
|
[[nodiscard]] consteval std::size_t Capacity() const {
|
||||||
return capacity;
|
return capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// It is important to align the below variables for performance reasons:
|
|
||||||
// Having them on the same cache-line would result in false-sharing between them.
|
|
||||||
// TODO: Remove this ifdef whenever clang and GCC support
|
|
||||||
// std::hardware_destructive_interference_size.
|
|
||||||
#ifdef __cpp_lib_hardware_interference_size
|
|
||||||
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
|
|
||||||
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
|
|
||||||
#else
|
|
||||||
alignas(128) std::atomic_size_t m_read_index{0};
|
|
||||||
alignas(128) std::atomic_size_t m_write_index{0};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::array<T, capacity> m_data;
|
std::array<T, capacity> m_data;
|
||||||
|
// This is wrong, a thread-safe ringbuffer is impossible.
|
||||||
|
std::size_t read_index{0};
|
||||||
|
std::size_t write_index{0};
|
||||||
|
std::mutex rb_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -158,6 +158,12 @@ bool IsFastmemEnabled() {
|
||||||
if (values.cpu_debug_mode) {
|
if (values.cpu_debug_mode) {
|
||||||
return static_cast<bool>(values.cpuopt_fastmem);
|
return static_cast<bool>(values.cpuopt_fastmem);
|
||||||
}
|
}
|
||||||
|
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
|
||||||
|
return static_cast<bool>(values.cpuopt_unsafe_host_mmu);
|
||||||
|
}
|
||||||
|
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,6 +299,15 @@ struct Values {
|
||||||
Category::CpuDebug};
|
Category::CpuDebug};
|
||||||
Setting<bool> cpuopt_ignore_memory_aborts{linkage, true, "cpuopt_ignore_memory_aborts",
|
Setting<bool> cpuopt_ignore_memory_aborts{linkage, true, "cpuopt_ignore_memory_aborts",
|
||||||
Category::CpuDebug};
|
Category::CpuDebug};
|
||||||
|
|
||||||
|
SwitchableSetting<bool> cpuopt_unsafe_host_mmu{linkage,
|
||||||
|
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__)
|
||||||
|
false,
|
||||||
|
#else
|
||||||
|
true,
|
||||||
|
#endif
|
||||||
|
"cpuopt_unsafe_host_mmu",
|
||||||
|
Category::CpuUnsafe};
|
||||||
SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{linkage, true, "cpuopt_unsafe_unfuse_fma",
|
SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{linkage, true, "cpuopt_unsafe_unfuse_fma",
|
||||||
Category::CpuUnsafe};
|
Category::CpuUnsafe};
|
||||||
SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{
|
SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{
|
||||||
|
@ -574,8 +583,11 @@ struct Values {
|
||||||
linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
|
linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
|
||||||
Setting<bool> enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey",
|
Setting<bool> enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey",
|
||||||
Category::RendererDebug};
|
Category::RendererDebug};
|
||||||
Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
|
SwitchableSetting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
|
||||||
Category::RendererDebug};
|
Category::RendererDebug,
|
||||||
|
Specialization::Default,
|
||||||
|
true,
|
||||||
|
true};
|
||||||
|
|
||||||
// System
|
// System
|
||||||
SwitchableSetting<Language, true> language_index{linkage,
|
SwitchableSetting<Language, true> language_index{linkage,
|
||||||
|
@ -741,7 +753,7 @@ struct Values {
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
|
Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
|
||||||
Setting<bool> log_flush_lines{linkage, true, "flush_lines", Category::Miscellaneous, Specialization::Default, true, true};
|
Setting<bool> log_flush_line{linkage, false, "flush_line", Category::Miscellaneous, Specialization::Default, true, true};
|
||||||
Setting<bool> censor_username{linkage, true, "censor_username", Category::Miscellaneous};
|
Setting<bool> censor_username{linkage, true, "censor_username", Category::Miscellaneous};
|
||||||
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
|
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
|
||||||
Setting<bool> first_launch{linkage, true, "first_launch", Category::Miscellaneous};
|
Setting<bool> first_launch{linkage, true, "first_launch", Category::Miscellaneous};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
@ -7,6 +10,7 @@
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
@ -21,51 +25,21 @@
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
/// Make a string lowercase
|
||||||
std::string ToLower(std::string str) {
|
std::string ToLower(const std::string_view sv) {
|
||||||
|
std::string str{sv};
|
||||||
std::transform(str.begin(), str.end(), str.begin(),
|
std::transform(str.begin(), str.end(), str.begin(),
|
||||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
[](auto const c) { return char(std::tolower(c)); });
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make a string uppercase
|
/// Make a string uppercase
|
||||||
std::string ToUpper(std::string str) {
|
std::string ToUpper(const std::string_view sv) {
|
||||||
|
std::string str{sv};
|
||||||
std::transform(str.begin(), str.end(), str.begin(),
|
std::transform(str.begin(), str.end(), str.begin(),
|
||||||
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
|
[](auto const c) { return char(std::toupper(c)); });
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StringFromBuffer(std::span<const u8> data) {
|
|
||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string StringFromBuffer(std::span<const char> data) {
|
|
||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turns " hej " into "hej". Also handles tabs.
|
|
||||||
std::string StripSpaces(const std::string& str) {
|
|
||||||
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
|
||||||
|
|
||||||
if (str.npos != s)
|
|
||||||
return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
|
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// "\"hello\"" is turned to "hello"
|
|
||||||
// This one assumes that the string has already been space stripped in both
|
|
||||||
// ends, as done by StripSpaces above, for example.
|
|
||||||
std::string StripQuotes(const std::string& s) {
|
|
||||||
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
|
|
||||||
return s.substr(1, s.size() - 2);
|
|
||||||
else
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string StringFromBool(bool value) {
|
|
||||||
return value ? "True" : "False";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
||||||
std::string* _pExtension) {
|
std::string* _pExtension) {
|
||||||
if (full_path.empty())
|
if (full_path.empty())
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
@ -13,18 +16,38 @@
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
/// Make a string lowercase
|
||||||
[[nodiscard]] std::string ToLower(std::string str);
|
[[nodiscard]] std::string ToLower(const std::string_view sv);
|
||||||
|
|
||||||
/// Make a string uppercase
|
/// Make a string uppercase
|
||||||
[[nodiscard]] std::string ToUpper(std::string str);
|
[[nodiscard]] std::string ToUpper(const std::string_view sv);
|
||||||
|
|
||||||
[[nodiscard]] std::string StringFromBuffer(std::span<const u8> data);
|
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
||||||
[[nodiscard]] std::string StringFromBuffer(std::span<const char> data);
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
|
}
|
||||||
|
[[nodiscard]] inline std::string StringFromBuffer(std::span<const char> data) noexcept {
|
||||||
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::string StripSpaces(const std::string& s);
|
/// Turns " hej " into "hej". Also handles tabs.
|
||||||
[[nodiscard]] std::string StripQuotes(const std::string& s);
|
[[nodiscard]] inline std::string StripSpaces(const std::string_view str) noexcept {
|
||||||
|
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
||||||
|
if (str.npos != s)
|
||||||
|
return std::string{str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1)};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::string StringFromBool(bool value);
|
/// "\"hello\"" is turned to "hello"
|
||||||
|
/// This one assumes that the string has already been space stripped in both
|
||||||
|
/// ends, as done by StripSpaces above, for example.
|
||||||
|
[[nodiscard]] inline std::string StripQuotes(const std::string_view s) noexcept {
|
||||||
|
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
|
||||||
|
return std::string{s.substr(1, s.size() - 2)};
|
||||||
|
return std::string{s};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::string StringFromBool(bool value) noexcept {
|
||||||
|
return value ? "True" : "False";
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
|
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
|
||||||
|
|
||||||
|
@ -54,7 +77,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||||
* `other` for equality.
|
* `other` for equality.
|
||||||
*/
|
*/
|
||||||
template <typename InIt>
|
template <typename InIt>
|
||||||
[[nodiscard]] bool ComparePartialString(InIt begin, InIt end, const char* other) {
|
[[nodiscard]] inline bool ComparePartialString(InIt begin, InIt end, const char* other) noexcept {
|
||||||
for (; begin != end && *other != '\0'; ++begin, ++other) {
|
for (; begin != end && *other != '\0'; ++begin, ++other) {
|
||||||
if (*begin != *other) {
|
if (*begin != *other) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -64,18 +87,14 @@ template <typename InIt>
|
||||||
return (begin == end) == (*other == '\0');
|
return (begin == end) == (*other == '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
* Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// NUL-terminated then the string ends at max_len characters.
|
||||||
* NUL-terminated then the string ends at max_len characters.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
|
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
|
||||||
std::size_t max_len);
|
std::size_t max_len);
|
||||||
|
|
||||||
/**
|
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||||
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
/// max_len_bytes.
|
||||||
* max_len_bytes.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||||
std::size_t max_len);
|
std::size_t max_len);
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ constexpr std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
|
||||||
constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
|
constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
|
||||||
constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
|
constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
|
||||||
|
|
||||||
|
constexpr inline Xbyak::Reg ABI_JIT_REG = Xbyak::util::rbx;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
// Microsoft x64 ABI
|
// Microsoft x64 ABI
|
||||||
|
|
|
@ -3,47 +3,9 @@
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
#include "common/signal_chain.h"
|
//#include "common/signal_chain.h"
|
||||||
|
|
||||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
//#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/memory.h"
|
//#include "core/memory.h"
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
thread_local Core::Memory::Memory* g_current_memory{};
|
|
||||||
std::once_flag g_registered{};
|
|
||||||
struct sigaction g_old_segv {};
|
|
||||||
|
|
||||||
void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
|
|
||||||
if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_old_segv.sa_sigaction(sig, info, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
|
|
||||||
g_current_memory = std::addressof(process->GetMemory());
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedJitExecution::~ScopedJitExecution() {
|
|
||||||
g_current_memory = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScopedJitExecution::RegisterHandler() {
|
|
||||||
std::call_once(g_registered, [] {
|
|
||||||
struct sigaction sa {};
|
|
||||||
sa.sa_sigaction = &HandleSigSegv;
|
|
||||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
|
||||||
Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Core
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,24 +26,4 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||||
return static_cast<HaltReason>(hr);
|
return static_cast<HaltReason>(hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
|
|
||||||
class ScopedJitExecution {
|
|
||||||
public:
|
|
||||||
explicit ScopedJitExecution(Kernel::KProcess* process);
|
|
||||||
~ScopedJitExecution();
|
|
||||||
static void RegisterHandler();
|
|
||||||
};
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
class ScopedJitExecution {
|
|
||||||
public:
|
|
||||||
explicit ScopedJitExecution(Kernel::KProcess* process) {}
|
|
||||||
~ScopedJitExecution() {}
|
|
||||||
static void RegisterHandler() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
@ -88,7 +88,7 @@ public:
|
||||||
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
||||||
m_parent.LogBacktrace(m_process);
|
m_parent.LogBacktrace(m_process);
|
||||||
LOG_ERROR(Core_ARM,
|
LOG_ERROR(Core_ARM,
|
||||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
"Unimplemented instruction @ {:#X} for {} instructions (instr = {:08X})", pc,
|
||||||
num_instructions, m_memory.Read32(pc));
|
num_instructions, m_memory.Read32(pc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +175,6 @@ public:
|
||||||
Kernel::KProcess* m_process{};
|
Kernel::KProcess* m_process{};
|
||||||
const bool m_debugger_enabled{};
|
const bool m_debugger_enabled{};
|
||||||
const bool m_check_memory_access{};
|
const bool m_check_memory_access{};
|
||||||
static constexpr u64 MinimumRunCycles = 10000U;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* page_table) const {
|
std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* page_table) const {
|
||||||
|
@ -272,6 +271,10 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
|
||||||
// Unsafe optimizations
|
// Unsafe optimizations
|
||||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
|
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
|
||||||
config.unsafe_optimizations = true;
|
config.unsafe_optimizations = true;
|
||||||
|
if (!Settings::values.cpuopt_unsafe_host_mmu) {
|
||||||
|
config.fastmem_pointer = std::nullopt;
|
||||||
|
config.fastmem_exclusive_access = false;
|
||||||
|
}
|
||||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||||
}
|
}
|
||||||
|
@ -292,13 +295,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
|
||||||
// Curated optimizations
|
// Curated optimizations
|
||||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
|
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
|
||||||
config.unsafe_optimizations = true;
|
config.unsafe_optimizations = true;
|
||||||
|
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__)
|
||||||
|
config.fastmem_pointer = std::nullopt;
|
||||||
|
config.fastmem_exclusive_access = false;
|
||||||
|
#endif
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paranoia mode for debugging optimizations
|
// Paranoid mode for debugging optimizations
|
||||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Paranoid) {
|
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Paranoid) {
|
||||||
config.unsafe_optimizations = false;
|
config.unsafe_optimizations = false;
|
||||||
config.optimizations = Dynarmic::no_optimizations;
|
config.optimizations = Dynarmic::no_optimizations;
|
||||||
|
@ -336,15 +343,11 @@ bool ArmDynarmic32::IsInThumbMode() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
||||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Run());
|
return TranslateHaltReason(m_jit->Run());
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
||||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Step());
|
return TranslateHaltReason(m_jit->Step());
|
||||||
}
|
}
|
||||||
|
@ -386,7 +389,6 @@ ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProc
|
||||||
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
||||||
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
||||||
m_jit = MakeJit(&page_table_impl);
|
m_jit = MakeJit(&page_table_impl);
|
||||||
ScopedJitExecution::RegisterHandler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArmDynarmic32::~ArmDynarmic32() = default;
|
ArmDynarmic32::~ArmDynarmic32() = default;
|
||||||
|
|
|
@ -102,7 +102,7 @@ public:
|
||||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||||
m_parent.LogBacktrace(m_process);
|
m_parent.LogBacktrace(m_process);
|
||||||
LOG_ERROR(Core_ARM,
|
LOG_ERROR(Core_ARM,
|
||||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
"Unimplemented instruction @ {:#X} for {} instructions (instr = {:08X})", pc,
|
||||||
num_instructions, m_memory.Read32(pc));
|
num_instructions, m_memory.Read32(pc));
|
||||||
ReturnException(pc, PrefetchAbort);
|
ReturnException(pc, PrefetchAbort);
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,7 @@ public:
|
||||||
case Dynarmic::A64::Exception::SendEvent:
|
case Dynarmic::A64::Exception::SendEvent:
|
||||||
case Dynarmic::A64::Exception::SendEventLocal:
|
case Dynarmic::A64::Exception::SendEventLocal:
|
||||||
case Dynarmic::A64::Exception::Yield:
|
case Dynarmic::A64::Exception::Yield:
|
||||||
|
LOG_TRACE(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast<std::size_t>(exception), pc, m_memory.Read32(pc));
|
||||||
return;
|
return;
|
||||||
case Dynarmic::A64::Exception::NoExecuteFault:
|
case Dynarmic::A64::Exception::NoExecuteFault:
|
||||||
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
|
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
|
||||||
|
@ -144,12 +145,10 @@ public:
|
||||||
default:
|
default:
|
||||||
if (m_debugger_enabled) {
|
if (m_debugger_enabled) {
|
||||||
ReturnException(pc, InstructionBreakpoint);
|
ReturnException(pc, InstructionBreakpoint);
|
||||||
return;
|
} else {
|
||||||
|
m_parent.LogBacktrace(m_process);
|
||||||
|
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast<std::size_t>(exception), pc, m_memory.Read32(pc));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_parent.LogBacktrace(m_process);
|
|
||||||
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
|
||||||
static_cast<std::size_t>(exception), pc, m_memory.Read32(pc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,6 +330,10 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||||
// Unsafe optimizations
|
// Unsafe optimizations
|
||||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
|
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
|
||||||
config.unsafe_optimizations = true;
|
config.unsafe_optimizations = true;
|
||||||
|
if (!Settings::values.cpuopt_unsafe_host_mmu) {
|
||||||
|
config.fastmem_pointer = std::nullopt;
|
||||||
|
config.fastmem_exclusive_access = false;
|
||||||
|
}
|
||||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||||
}
|
}
|
||||||
|
@ -351,6 +354,10 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||||
// Curated optimizations
|
// Curated optimizations
|
||||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
|
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Auto) {
|
||||||
config.unsafe_optimizations = true;
|
config.unsafe_optimizations = true;
|
||||||
|
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__)
|
||||||
|
config.fastmem_pointer = std::nullopt;
|
||||||
|
config.fastmem_exclusive_access = false;
|
||||||
|
#endif
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||||
config.fastmem_address_space_bits = 64;
|
config.fastmem_address_space_bits = 64;
|
||||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||||
|
@ -367,15 +374,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
||||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Run());
|
return TranslateHaltReason(m_jit->Run());
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
||||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Step());
|
return TranslateHaltReason(m_jit->Step());
|
||||||
}
|
}
|
||||||
|
@ -415,7 +418,6 @@ ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProc
|
||||||
auto& page_table = process->GetPageTable().GetBasePageTable();
|
auto& page_table = process->GetPageTable().GetBasePageTable();
|
||||||
auto& page_table_impl = page_table.GetImpl();
|
auto& page_table_impl = page_table.GetImpl();
|
||||||
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
||||||
ScopedJitExecution::RegisterHandler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArmDynarmic64::~ArmDynarmic64() = default;
|
ArmDynarmic64::~ArmDynarmic64() = default;
|
||||||
|
|
|
@ -554,32 +554,31 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
|
|
||||||
{"----- Free ------", Kernel::Svc::MemoryState::Free},
|
|
||||||
{"Io ", Kernel::Svc::MemoryState::Io},
|
|
||||||
{"Static ", Kernel::Svc::MemoryState::Static},
|
|
||||||
{"Code ", Kernel::Svc::MemoryState::Code},
|
|
||||||
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
|
|
||||||
{"Normal ", Kernel::Svc::MemoryState::Normal},
|
|
||||||
{"Shared ", Kernel::Svc::MemoryState::Shared},
|
|
||||||
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
|
|
||||||
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
|
|
||||||
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
|
|
||||||
{"Stack ", Kernel::Svc::MemoryState::Stack},
|
|
||||||
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
|
|
||||||
{"Transferred ", Kernel::Svc::MemoryState::Transferred},
|
|
||||||
{"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred},
|
|
||||||
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
|
|
||||||
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
|
|
||||||
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
|
|
||||||
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
|
|
||||||
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
|
|
||||||
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
|
|
||||||
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
|
|
||||||
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
|
|
||||||
}};
|
|
||||||
|
|
||||||
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
|
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
|
||||||
|
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
|
||||||
|
{"----- Free ------", Kernel::Svc::MemoryState::Free},
|
||||||
|
{"Io ", Kernel::Svc::MemoryState::Io},
|
||||||
|
{"Static ", Kernel::Svc::MemoryState::Static},
|
||||||
|
{"Code ", Kernel::Svc::MemoryState::Code},
|
||||||
|
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
|
||||||
|
{"Normal ", Kernel::Svc::MemoryState::Normal},
|
||||||
|
{"Shared ", Kernel::Svc::MemoryState::Shared},
|
||||||
|
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
|
||||||
|
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
|
||||||
|
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
|
||||||
|
{"Stack ", Kernel::Svc::MemoryState::Stack},
|
||||||
|
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
|
||||||
|
{"Transferred ", Kernel::Svc::MemoryState::Transferred},
|
||||||
|
{"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred},
|
||||||
|
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
|
||||||
|
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
|
||||||
|
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
|
||||||
|
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
|
||||||
|
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
|
||||||
|
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
|
||||||
|
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
|
||||||
|
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
|
||||||
|
}};
|
||||||
for (size_t i = 0; i < MemoryStateNames.size(); i++) {
|
for (size_t i = 0; i < MemoryStateNames.size(); i++) {
|
||||||
if (std::get<1>(MemoryStateNames[i]) == state) {
|
if (std::get<1>(MemoryStateNames[i]) == state) {
|
||||||
return std::get<0>(MemoryStateNames[i]);
|
return std::get<0>(MemoryStateNames[i]);
|
||||||
|
@ -611,13 +610,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||||
|
|
||||||
auto* process = GetProcess();
|
auto* process = GetProcess();
|
||||||
auto& page_table = process->GetPageTable();
|
auto& page_table = process->GetPageTable();
|
||||||
|
if (command_str == "fastmem" || command_str == "get fastmem") {
|
||||||
const char* commands = "Commands:\n"
|
|
||||||
" get fastmem\n"
|
|
||||||
" get info\n"
|
|
||||||
" get mappings\n";
|
|
||||||
|
|
||||||
if (command_str == "get fastmem") {
|
|
||||||
if (Settings::IsFastmemEnabled()) {
|
if (Settings::IsFastmemEnabled()) {
|
||||||
const auto& impl = page_table.GetImpl();
|
const auto& impl = page_table.GetImpl();
|
||||||
const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
|
const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
|
||||||
|
@ -630,7 +623,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||||
} else {
|
} else {
|
||||||
reply = "Fastmem is not enabled.\n";
|
reply = "Fastmem is not enabled.\n";
|
||||||
}
|
}
|
||||||
} else if (command_str == "get info") {
|
} else if (command_str == "info" || command_str == "get info") {
|
||||||
auto modules = Core::FindModules(process);
|
auto modules = Core::FindModules(process);
|
||||||
|
|
||||||
reply = fmt::format("Process: {:#x} ({})\n"
|
reply = fmt::format("Process: {:#x} ({})\n"
|
||||||
|
@ -648,8 +641,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||||
GetInteger(page_table.GetHeapRegionStart()),
|
GetInteger(page_table.GetHeapRegionStart()),
|
||||||
GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
|
GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1,
|
||||||
GetInteger(page_table.GetAliasCodeRegionStart()),
|
GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||||
GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() -
|
GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() - 1,
|
||||||
1,
|
|
||||||
GetInteger(page_table.GetStackRegionStart()),
|
GetInteger(page_table.GetStackRegionStart()),
|
||||||
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
|
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
|
||||||
|
|
||||||
|
@ -657,7 +649,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||||
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
||||||
GetInteger(Core::GetModuleEnd(process, vaddr)), name);
|
GetInteger(Core::GetModuleEnd(process, vaddr)), name);
|
||||||
}
|
}
|
||||||
} else if (command_str == "get mappings") {
|
} else if (command_str == "mappings" || command_str == "get mappings") {
|
||||||
reply = "Mappings:\n";
|
reply = "Mappings:\n";
|
||||||
VAddr cur_addr = 0;
|
VAddr cur_addr = 0;
|
||||||
|
|
||||||
|
@ -675,15 +667,11 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||||
std::numeric_limits<u64>::max()) {
|
std::numeric_limits<u64>::max()) {
|
||||||
const char* state = GetMemoryStateName(svc_mem_info.state);
|
const char* state = GetMemoryStateName(svc_mem_info.state);
|
||||||
const char* perm = GetMemoryPermissionString(svc_mem_info);
|
const char* perm = GetMemoryPermissionString(svc_mem_info);
|
||||||
|
|
||||||
const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
|
const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
|
||||||
const char i =
|
const char i = True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||||
True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
const char d = True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||||
const char d =
|
|
||||||
True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
|
||||||
const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||||
const char p =
|
const char p =True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
||||||
True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
|
||||||
|
|
||||||
reply += fmt::format(
|
reply += fmt::format(
|
||||||
" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
|
" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address,
|
||||||
|
@ -698,11 +686,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||||
|
|
||||||
cur_addr = next_address;
|
cur_addr = next_address;
|
||||||
}
|
}
|
||||||
} else if (command_str == "help") {
|
|
||||||
reply = commands;
|
|
||||||
} else {
|
} else {
|
||||||
reply = "Unknown command.\n";
|
reply += "Commands: fastmem, info, mappings\n";
|
||||||
reply += commands;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
|
std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -180,7 +183,7 @@ void ProgramMetadata::Print() const {
|
||||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
|
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
|
||||||
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
|
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
|
||||||
LOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu);
|
LOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu);
|
||||||
LOG_DEBUG(Service_FS, "Main thread stack size: 0x{:X} bytes", npdm_header.main_stack_size);
|
LOG_DEBUG(Service_FS, "Main thread stack size: {:#X} bytes", npdm_header.main_stack_size);
|
||||||
LOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category);
|
LOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category);
|
||||||
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags);
|
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags);
|
||||||
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -56,8 +59,8 @@ NAX::NAX(VirtualFile file_)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string two_dir = Common::ToUpper(match[1]);
|
const std::string two_dir = Common::ToUpper(std::string{match[1]});
|
||||||
const std::string nca_id = Common::ToLower(match[2]);
|
const std::string nca_id = Common::ToLower(std::string{match[2]});
|
||||||
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Hardware {
|
||||||
|
|
||||||
constexpr u64 BASE_CLOCK_RATE = 1'020'000'000; // Default CPU Frequency = 1020 MHz
|
constexpr u64 BASE_CLOCK_RATE = 1'020'000'000; // Default CPU Frequency = 1020 MHz
|
||||||
constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
|
constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
|
||||||
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
|
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync with dynarmic exclusive_monitor.h
|
||||||
|
|
||||||
// Virtual to Physical core map.
|
// Virtual to Physical core map.
|
||||||
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
||||||
|
|
|
@ -1266,10 +1266,6 @@ void KProcess::InitializeInterfaces() {
|
||||||
|
|
||||||
#ifdef HAS_NCE
|
#ifdef HAS_NCE
|
||||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||||
// Register the scoped JIT handler before creating any NCE instances
|
|
||||||
// so that its signal handler will appear first in the signal chain.
|
|
||||||
Core::ScopedJitExecution::RegisterHandler();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||||
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -40,7 +43,7 @@ constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
|
||||||
// Wait for an address (via Address Arbiter)
|
// Wait for an address (via Address Arbiter)
|
||||||
Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_type, s32 value,
|
Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_type, s32 value,
|
||||||
s64 timeout_ns) {
|
s64 timeout_ns) {
|
||||||
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
|
LOG_TRACE(Kernel_SVC, "called, address={:#X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
|
||||||
address, arb_type, value, timeout_ns);
|
address, arb_type, value, timeout_ns);
|
||||||
|
|
||||||
// Validate input.
|
// Validate input.
|
||||||
|
@ -71,7 +74,7 @@ Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_typ
|
||||||
// Signals to an address (via Address Arbiter)
|
// Signals to an address (via Address Arbiter)
|
||||||
Result SignalToAddress(Core::System& system, u64 address, SignalType signal_type, s32 value,
|
Result SignalToAddress(Core::System& system, u64 address, SignalType signal_type, s32 value,
|
||||||
s32 count) {
|
s32 count) {
|
||||||
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
|
LOG_TRACE(Kernel_SVC, "called, address={:#X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
|
||||||
address, signal_type, value, count);
|
address, signal_type, value, count);
|
||||||
|
|
||||||
// Validate input.
|
// Validate input.
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -30,7 +33,7 @@ constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm)
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t size) {
|
Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t size) {
|
||||||
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
|
LOG_TRACE(Kernel_SVC, "called, address={:#X}, size=0x{:X}", address, size);
|
||||||
|
|
||||||
// Get kernel instance.
|
// Get kernel instance.
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
|
@ -70,8 +73,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
|
||||||
MemoryPermission perm) {
|
MemoryPermission perm) {
|
||||||
|
|
||||||
LOG_TRACE(Kernel_SVC,
|
LOG_TRACE(Kernel_SVC,
|
||||||
"called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
|
"called, code_memory_handle={:#X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
|
||||||
"permission=0x{:X}",
|
"permission={:#X}",
|
||||||
code_memory_handle, operation, address, size, perm);
|
code_memory_handle, operation, address, size, perm);
|
||||||
|
|
||||||
// Validate the address / size.
|
// Validate the address / size.
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -45,7 +48,7 @@ Result WaitProcessWideKeyAtomic(Core::System& system, u64 address, u64 cv_key, u
|
||||||
|
|
||||||
/// Signal process wide key
|
/// Signal process wide key
|
||||||
void SignalProcessWideKey(Core::System& system, u64 cv_key, s32 count) {
|
void SignalProcessWideKey(Core::System& system, u64 cv_key, s32 count) {
|
||||||
LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
|
LOG_TRACE(Kernel_SVC, "called, cv_key={:#X}, count=0x{:08X}", cv_key, count);
|
||||||
|
|
||||||
// Signal the condition variable.
|
// Signal the condition variable.
|
||||||
return GetCurrentProcess(system.Kernel())
|
return GetCurrentProcess(system.Kernel())
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Kernel::Svc {
|
||||||
/// Gets system/memory information for the current process
|
/// Gets system/memory information for the current process
|
||||||
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
|
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
|
||||||
u64 info_sub_id) {
|
u64 info_sub_id) {
|
||||||
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}",
|
LOG_TRACE(Kernel_SVC, "called info_id={:#X}, info_sub_id=0x{:X}, handle=0x{:08X}",
|
||||||
info_id_type, info_sub_id, handle);
|
info_id_type, info_sub_id, handle);
|
||||||
|
|
||||||
u32 info_id = static_cast<u32>(info_id_type);
|
u32 info_id = static_cast<u32>(info_id_type);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ namespace Kernel::Svc {
|
||||||
|
|
||||||
/// Attempts to locks a mutex
|
/// Attempts to locks a mutex
|
||||||
Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u32 tag) {
|
Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u32 tag) {
|
||||||
LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
|
LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address={:#X}, tag=0x{:08X}",
|
||||||
thread_handle, address, tag);
|
thread_handle, address, tag);
|
||||||
|
|
||||||
// Validate the input address.
|
// Validate the input address.
|
||||||
|
@ -22,7 +25,7 @@ Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u3
|
||||||
|
|
||||||
/// Unlock a mutex
|
/// Unlock a mutex
|
||||||
Result ArbitrateUnlock(Core::System& system, u64 address) {
|
Result ArbitrateUnlock(Core::System& system, u64 address) {
|
||||||
LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
|
LOG_TRACE(Kernel_SVC, "called address={:#X}", address);
|
||||||
|
|
||||||
// Validate the input address.
|
// Validate the input address.
|
||||||
R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory);
|
R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -77,7 +80,7 @@ Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPermission perm) {
|
Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPermission perm) {
|
||||||
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X}", address, size,
|
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size={:#X}, perm=0x{:08X}", address, size,
|
||||||
perm);
|
perm);
|
||||||
|
|
||||||
// Validate address / size.
|
// Validate address / size.
|
||||||
|
@ -99,7 +102,7 @@ Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPe
|
||||||
|
|
||||||
Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, u32 attr) {
|
Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, u32 attr) {
|
||||||
LOG_DEBUG(Kernel_SVC,
|
LOG_DEBUG(Kernel_SVC,
|
||||||
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
|
"called, address=0x{:016X}, size={:#X}, mask=0x{:08X}, attribute=0x{:08X}", address,
|
||||||
size, mask, attr);
|
size, mask, attr);
|
||||||
|
|
||||||
// Validate address / size.
|
// Validate address / size.
|
||||||
|
@ -130,7 +133,7 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
|
||||||
|
|
||||||
/// Maps a memory range into a different range.
|
/// Maps a memory range into a different range.
|
||||||
Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
|
Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
|
||||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||||
src_addr, size);
|
src_addr, size);
|
||||||
|
|
||||||
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
|
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
|
||||||
|
@ -145,7 +148,7 @@ Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
|
||||||
|
|
||||||
/// Unmaps a region that was previously mapped with svcMapMemory
|
/// Unmaps a region that was previously mapped with svcMapMemory
|
||||||
Result UnmapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
|
Result UnmapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
|
||||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||||
src_addr, size);
|
src_addr, size);
|
||||||
|
|
||||||
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
|
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -9,7 +12,7 @@ namespace Kernel::Svc {
|
||||||
|
|
||||||
/// Set the process heap to a given Size. It can both extend and shrink the heap.
|
/// Set the process heap to a given Size. It can both extend and shrink the heap.
|
||||||
Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
|
Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
|
||||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
|
LOG_TRACE(Kernel_SVC, "called, heap_size={:#X}", size);
|
||||||
|
|
||||||
// Validate size.
|
// Validate size.
|
||||||
R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
|
R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
|
||||||
|
@ -28,7 +31,7 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
|
||||||
|
|
||||||
/// Maps memory at a desired address
|
/// Maps memory at a desired address
|
||||||
Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
|
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
|
||||||
|
|
||||||
if (!Common::Is4KBAligned(addr)) {
|
if (!Common::Is4KBAligned(addr)) {
|
||||||
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
||||||
|
@ -36,7 +39,7 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Common::Is4KBAligned(size)) {
|
if (!Common::Is4KBAligned(size)) {
|
||||||
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
|
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size);
|
||||||
R_THROW(ResultInvalidSize);
|
R_THROW(ResultInvalidSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +80,7 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||||
|
|
||||||
/// Unmaps memory previously mapped via MapPhysicalMemory
|
/// Unmaps memory previously mapped via MapPhysicalMemory
|
||||||
Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
|
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
|
||||||
|
|
||||||
if (!Common::Is4KBAligned(addr)) {
|
if (!Common::Is4KBAligned(addr)) {
|
||||||
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
||||||
|
@ -85,7 +88,7 @@ Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Common::Is4KBAligned(size)) {
|
if (!Common::Is4KBAligned(size)) {
|
||||||
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
|
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#X}", size);
|
||||||
R_THROW(ResultInvalidSize);
|
R_THROW(ResultInvalidSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -92,7 +95,7 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
|
||||||
|
|
||||||
Result GetProcessInfo(Core::System& system, s64* out, Handle process_handle,
|
Result GetProcessInfo(Core::System& system, s64* out, Handle process_handle,
|
||||||
ProcessInfoType info_type) {
|
ProcessInfoType info_type) {
|
||||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, info_type);
|
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type={:#X}", process_handle, info_type);
|
||||||
|
|
||||||
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
|
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
|
||||||
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
|
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -29,7 +32,7 @@ constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
|
||||||
Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u64 address,
|
Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u64 address,
|
||||||
u64 size, Svc::MemoryPermission perm) {
|
u64 size, Svc::MemoryPermission perm) {
|
||||||
LOG_TRACE(Kernel_SVC,
|
LOG_TRACE(Kernel_SVC,
|
||||||
"called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
|
"called, process_handle={:#X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
|
||||||
process_handle, address, size, perm);
|
process_handle, address, size, perm);
|
||||||
|
|
||||||
// Validate the address/size.
|
// Validate the address/size.
|
||||||
|
@ -59,7 +62,7 @@ Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u
|
||||||
Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_handle,
|
Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_handle,
|
||||||
u64 src_address, u64 size) {
|
u64 src_address, u64 size) {
|
||||||
LOG_TRACE(Kernel_SVC,
|
LOG_TRACE(Kernel_SVC,
|
||||||
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
|
"called, dst_address={:#X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
|
||||||
dst_address, process_handle, src_address, size);
|
dst_address, process_handle, src_address, size);
|
||||||
|
|
||||||
// Validate the address/size.
|
// Validate the address/size.
|
||||||
|
@ -100,7 +103,7 @@ Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_ha
|
||||||
Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_handle,
|
Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_handle,
|
||||||
u64 src_address, u64 size) {
|
u64 src_address, u64 size) {
|
||||||
LOG_TRACE(Kernel_SVC,
|
LOG_TRACE(Kernel_SVC,
|
||||||
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
|
"called, dst_address={:#X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
|
||||||
dst_address, process_handle, src_address, size);
|
dst_address, process_handle, src_address, size);
|
||||||
|
|
||||||
// Validate the address/size.
|
// Validate the address/size.
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -29,7 +32,7 @@ constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) {
|
||||||
Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u64 size,
|
Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u64 size,
|
||||||
Svc::MemoryPermission map_perm) {
|
Svc::MemoryPermission map_perm) {
|
||||||
LOG_TRACE(Kernel_SVC,
|
LOG_TRACE(Kernel_SVC,
|
||||||
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
|
"called, shared_memory_handle={:#X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
|
||||||
shmem_handle, address, size, map_perm);
|
shmem_handle, address, size, map_perm);
|
||||||
|
|
||||||
// Validate the address/size.
|
// Validate the address/size.
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -105,7 +108,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
|
||||||
|
|
||||||
/// Resumes a thread waiting on WaitSynchronization
|
/// Resumes a thread waiting on WaitSynchronization
|
||||||
Result CancelSynchronization(Core::System& system, Handle handle) {
|
Result CancelSynchronization(Core::System& system, Handle handle) {
|
||||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
|
LOG_TRACE(Kernel_SVC, "called handle={:#X}", handle);
|
||||||
|
|
||||||
// Get the thread from its handle.
|
// Get the thread from its handle.
|
||||||
KScopedAutoObject thread =
|
KScopedAutoObject thread =
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -136,7 +139,7 @@ void SleepThread(Core::System& system, s64 ns) {
|
||||||
|
|
||||||
/// Gets the thread context
|
/// Gets the thread context
|
||||||
Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_handle) {
|
Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_handle) {
|
||||||
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
|
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle={:#X}", out_context,
|
||||||
thread_handle);
|
thread_handle);
|
||||||
|
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -101,12 +104,12 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
|
||||||
|
|
||||||
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
||||||
Event m_system_event = caller_applet->lifecycle_manager.GetSystemEvent();
|
caller_applet->lifecycle_manager.GetSystemEvent().Signal();
|
||||||
m_system_event.Signal();
|
caller_applet->lifecycle_manager.RequestResumeNotification();
|
||||||
caller_applet->lifecycle_manager.RequestResumeNotification();
|
caller_applet->lifecycle_manager.GetSystemEvent().Clear();
|
||||||
m_system_event.Clear();
|
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
||||||
}
|
}
|
||||||
R_RETURN(m_broker->GetOutData().Pop(out_storage.Get()));
|
R_RETURN(m_broker->GetOutData().Pop(out_storage.Get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -68,7 +71,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
|
||||||
std::string crash_report = fmt::format(
|
std::string crash_report = fmt::format(
|
||||||
"Yuzu {}-{} crash report\n"
|
"Yuzu {}-{} crash report\n"
|
||||||
"Title ID: {:016x}\n"
|
"Title ID: {:016x}\n"
|
||||||
"Result: 0x{:X} ({:04}-{:04d})\n"
|
"Result: {:#X} ({:04}-{:04d})\n"
|
||||||
"Set flags: 0x{:16X}\n"
|
"Set flags: 0x{:16X}\n"
|
||||||
"Program entry point: 0x{:16X}\n"
|
"Program entry point: 0x{:16X}\n"
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -108,7 +111,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
|
||||||
|
|
||||||
static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type,
|
static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type,
|
||||||
const FatalInfo& info) {
|
const FatalInfo& info) {
|
||||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", fatal_type,
|
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code {:#X}", fatal_type,
|
||||||
error_code.raw);
|
error_code.raw);
|
||||||
|
|
||||||
switch (fatal_type) {
|
switch (fatal_type) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -27,7 +30,7 @@ Result IFile::Read(
|
||||||
FileSys::ReadOption option, Out<s64> out_size, s64 offset,
|
FileSys::ReadOption option, Out<s64> out_size, s64 offset,
|
||||||
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
|
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
|
||||||
s64 size) {
|
s64 size) {
|
||||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
|
LOG_DEBUG(Service_FS, "called, option={}, offset={:#X}, length={}", option.value, offset,
|
||||||
size);
|
size);
|
||||||
|
|
||||||
// Read the data from the Storage backend
|
// Read the data from the Storage backend
|
||||||
|
@ -38,7 +41,7 @@ Result IFile::Read(
|
||||||
Result IFile::Write(
|
Result IFile::Write(
|
||||||
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
|
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
|
||||||
FileSys::WriteOption option, s64 offset, s64 size) {
|
FileSys::WriteOption option, s64 offset, s64 size) {
|
||||||
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
|
LOG_DEBUG(Service_FS, "called, option={}, offset={:#X}, length={}", option.value, offset,
|
||||||
size);
|
size);
|
||||||
|
|
||||||
R_RETURN(backend->Write(offset, buffer.data(), size, option));
|
R_RETURN(backend->Write(offset, buffer.data(), size, option));
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -38,7 +41,7 @@ IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGe
|
||||||
|
|
||||||
Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||||
s32 option, s64 size) {
|
s32 option, s64 size) {
|
||||||
LOG_DEBUG(Service_FS, "called. file={}, option=0x{:X}, size=0x{:08X}", path->str, option, size);
|
LOG_DEBUG(Service_FS, "called. file={}, option={:#X}, size=0x{:08X}", path->str, option, size);
|
||||||
|
|
||||||
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
|
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -23,7 +26,7 @@ IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_)
|
||||||
Result IStorage::Read(
|
Result IStorage::Read(
|
||||||
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_bytes,
|
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_bytes,
|
||||||
s64 offset, s64 length) {
|
s64 offset, s64 length) {
|
||||||
LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
|
LOG_DEBUG(Service_FS, "called, offset={:#X}, length={}", offset, length);
|
||||||
|
|
||||||
R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
|
R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
|
||||||
R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);
|
R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -27,7 +30,7 @@ Result IAppletResource::GetSharedMemoryHandle(
|
||||||
OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_handle) {
|
OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_handle) {
|
||||||
const auto result = resource_manager->GetSharedMemoryHandle(out_shared_memory_handle, aruid);
|
const auto result = resource_manager->GetSharedMemoryHandle(out_shared_memory_handle, aruid);
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}", aruid, result.raw);
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result={:#X}", aruid, result.raw);
|
||||||
R_RETURN(result);
|
R_RETURN(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ Result IHidServer::CreateAppletResource(OutInterface<IAppletResource> out_applet
|
||||||
ClientAppletResourceUserId aruid) {
|
ClientAppletResourceUserId aruid) {
|
||||||
const auto result = GetResourceManager()->CreateAppletResource(aruid.pid);
|
const auto result = GetResourceManager()->CreateAppletResource(aruid.pid);
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}", aruid.pid,
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result={:#X}", aruid.pid,
|
||||||
result.raw);
|
result.raw);
|
||||||
|
|
||||||
*out_applet_resource = std::make_shared<IAppletResource>(system, resource_manager, aruid.pid);
|
*out_applet_resource = std::make_shared<IAppletResource>(system, resource_manager, aruid.pid);
|
||||||
|
|
|
@ -93,7 +93,7 @@ void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
|
||||||
void nvhost_as_gpu::OnClose(DeviceFD fd) {}
|
void nvhost_as_gpu::OnClose(DeviceFD fd) {}
|
||||||
|
|
||||||
NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
|
NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
|
||||||
LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
|
LOG_DEBUG(Service_NVDRV, "called, big_page_size={:#X}", params.big_page_size);
|
||||||
|
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
|
|
||||||
|
@ -104,12 +104,12 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
|
||||||
|
|
||||||
if (params.big_page_size) {
|
if (params.big_page_size) {
|
||||||
if (!std::has_single_bit(params.big_page_size)) {
|
if (!std::has_single_bit(params.big_page_size)) {
|
||||||
LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size);
|
LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: {:#X}!", params.big_page_size);
|
||||||
return NvResult::BadValue;
|
return NvResult::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) {
|
if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) {
|
||||||
LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size);
|
LOG_ERROR(Service_NVDRV, "Unsupported big page size: {:#X}!", params.big_page_size);
|
||||||
return NvResult::BadValue;
|
return NvResult::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
|
NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
|
||||||
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", entries.size());
|
LOG_DEBUG(Service_NVDRV, "called, num_entries={:#X}", entries.size());
|
||||||
|
|
||||||
if (!vm.initialised) {
|
if (!vm.initialised) {
|
||||||
return NvResult::BadValue;
|
return NvResult::BadValue;
|
||||||
|
@ -315,7 +315,7 @@ NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
|
||||||
NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
|
NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
|
||||||
LOG_DEBUG(Service_NVDRV,
|
LOG_DEBUG(Service_NVDRV,
|
||||||
"called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
|
"called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
|
||||||
", offset=0x{:X}",
|
", offset={:#X}",
|
||||||
params.flags, params.handle, params.buffer_offset, params.mapping_size,
|
params.flags, params.handle, params.buffer_offset, params.mapping_size,
|
||||||
params.offset);
|
params.offset);
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
|
||||||
|
|
||||||
if (mapping->size < params.mapping_size) {
|
if (mapping->size < params.mapping_size) {
|
||||||
LOG_WARNING(Service_NVDRV,
|
LOG_WARNING(Service_NVDRV,
|
||||||
"Cannot remap a partially mapped GPU address space region: 0x{:X}",
|
"Cannot remap a partially mapped GPU address space region: {:#X}",
|
||||||
params.offset);
|
params.offset);
|
||||||
return NvResult::BadValue;
|
return NvResult::BadValue;
|
||||||
}
|
}
|
||||||
|
@ -345,7 +345,7 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
|
||||||
|
|
||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
} catch (const std::out_of_range&) {
|
} catch (const std::out_of_range&) {
|
||||||
LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}",
|
LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: {:#X}",
|
||||||
params.offset);
|
params.offset);
|
||||||
return NvResult::BadValue;
|
return NvResult::BadValue;
|
||||||
}
|
}
|
||||||
|
@ -416,7 +416,7 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
|
||||||
|
|
||||||
NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
|
NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
|
||||||
if (map_buffer_offsets.find(params.offset) != map_buffer_offsets.end()) {
|
if (map_buffer_offsets.find(params.offset) != map_buffer_offsets.end()) {
|
||||||
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
|
LOG_DEBUG(Service_NVDRV, "called, offset={:#X}", params.offset);
|
||||||
|
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock(mutex);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -205,7 +208,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics3(
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
|
NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
|
||||||
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
|
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size={:#X}", params.mask_buffer_size);
|
||||||
if (params.mask_buffer_size != 0) {
|
if (params.mask_buffer_size != 0) {
|
||||||
params.tcp_mask = 3;
|
params.tcp_mask = 3;
|
||||||
}
|
}
|
||||||
|
@ -213,7 +216,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) {
|
NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) {
|
||||||
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
|
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size={:#X}", params.mask_buffer_size);
|
||||||
if (params.mask_buffer_size != 0) {
|
if (params.mask_buffer_size != 0) {
|
||||||
params.tcp_mask = 3;
|
params.tcp_mask = 3;
|
||||||
}
|
}
|
||||||
|
@ -312,7 +315,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) {
|
NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) {
|
||||||
LOG_DEBUG(Service_NVDRV, "called 0x{:X}", params.flush);
|
LOG_DEBUG(Service_NVDRV, "called {:#X}", params.flush);
|
||||||
// if ((params.flush & 0x01) != 0) //l2 flush
|
// if ((params.flush & 0x01) != 0) //l2 flush
|
||||||
// /* we dont emulate l2 */;
|
// /* we dont emulate l2 */;
|
||||||
// if ((params.flush & 0x04) != 0) //fb flush
|
// if ((params.flush & 0x04) != 0) //fb flush
|
||||||
|
|
|
@ -438,20 +438,20 @@ NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) {
|
NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) {
|
||||||
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
LOG_INFO(Service_NVDRV, "called, unknown={:#X}", params.unknown);
|
||||||
|
|
||||||
params.value = 0; // Seems to be hard coded at 0
|
params.value = 0; // Seems to be hard coded at 0
|
||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) {
|
NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) {
|
||||||
LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
|
LOG_INFO(Service_NVDRV, "called, timeout={:#X}", params.timeout);
|
||||||
|
|
||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) {
|
NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) {
|
||||||
LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
|
LOG_INFO(Service_NVDRV, "called, timeslice={:#X}", params.timeslice);
|
||||||
|
|
||||||
channel_timeslice = params.timeslice;
|
channel_timeslice = params.timeslice;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -89,7 +92,7 @@ NvResult nvmap::IocCreate(IocCreateParams& params) {
|
||||||
}
|
}
|
||||||
handle_description->orig_size = params.size; // Orig size is the unaligned size
|
handle_description->orig_size = params.size; // Orig size is the unaligned size
|
||||||
params.handle = handle_description->id;
|
params.handle = handle_description->id;
|
||||||
LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
|
LOG_DEBUG(Service_NVDRV, "handle: {}, size: {:#X}", handle_description->id, params.size);
|
||||||
|
|
||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
@ -209,7 +212,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
|
||||||
void NVDRV::SetAruid(HLERequestContext& ctx) {
|
void NVDRV::SetAruid(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
pid = rp.Pop<u64>();
|
pid = rp.Pop<u64>();
|
||||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
|
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#X}", pid);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -87,7 +90,7 @@ Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) {
|
||||||
|
|
||||||
Result SteadyClock::GetSetupResultValue(Out<Result> out_result) {
|
Result SteadyClock::GetSetupResultValue(Out<Result> out_result) {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
LOG_DEBUG(Service_Time, "called. out_result=0x{:X}", out_result->raw);
|
LOG_DEBUG(Service_Time, "called. out_result={:#X}", out_result->raw);
|
||||||
};
|
};
|
||||||
|
|
||||||
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
|
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -27,7 +30,7 @@ namespace Service {
|
||||||
|
|
||||||
std::string function_string = fmt::format("function '{}': port={}", name, port_name);
|
std::string function_string = fmt::format("function '{}': port={}", name, port_name);
|
||||||
for (int i = 1; i <= num_params; ++i) {
|
for (int i = 1; i <= num_params; ++i) {
|
||||||
function_string += fmt::format(", cmd_buff[{}]=0x{:X}", i, cmd_buff[i]);
|
function_string += fmt::format(", cmd_buff[{}]={:#X}", i, cmd_buff[i]);
|
||||||
}
|
}
|
||||||
return function_string;
|
return function_string;
|
||||||
}
|
}
|
||||||
|
@ -66,10 +69,10 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
||||||
std::string function_name = info == nullptr ? "<unknown>" : info->name;
|
std::string function_name = info == nullptr ? "<unknown>" : info->name;
|
||||||
|
|
||||||
fmt::memory_buffer buf;
|
fmt::memory_buffer buf;
|
||||||
fmt::format_to(std::back_inserter(buf), "function '{}({})': port='{}' cmd_buf={{[0]=0x{:X}",
|
fmt::format_to(std::back_inserter(buf), "function '{}({})': port='{}' cmd_buf={{[0]={:#X}",
|
||||||
ctx.GetCommand(), function_name, service_name, cmd_buf[0]);
|
ctx.GetCommand(), function_name, service_name, cmd_buf[0]);
|
||||||
for (int i = 1; i <= 8; ++i) {
|
for (int i = 1; i <= 8; ++i) {
|
||||||
fmt::format_to(std::back_inserter(buf), ", [{}]=0x{:X}", i, cmd_buf[i]);
|
fmt::format_to(std::back_inserter(buf), ", [{}]={:#X}", i, cmd_buf[i]);
|
||||||
}
|
}
|
||||||
buf.push_back('}');
|
buf.push_back('}');
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -101,7 +104,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||||
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||||
process.LoadModule(std::move(codeset), base_address);
|
process.LoadModule(std::move(codeset), base_address);
|
||||||
|
|
||||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
|
LOG_DEBUG(Loader, "loaded module {} @ {:#X}", kip->GetName(), base_address);
|
||||||
|
|
||||||
is_loaded = true;
|
is_loaded = true;
|
||||||
return {ResultStatus::Success,
|
return {ResultStatus::Success,
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -61,25 +64,23 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
|
||||||
FileType GuessFromFilename(const std::string& name) {
|
FileType GuessFromFilename(const std::string& name) {
|
||||||
if (name == "main")
|
if (name == "main")
|
||||||
return FileType::DeconstructedRomDirectory;
|
return FileType::DeconstructedRomDirectory;
|
||||||
if (name == "00")
|
else if (name == "00")
|
||||||
return FileType::NCA;
|
return FileType::NCA;
|
||||||
|
|
||||||
const std::string extension =
|
auto const extension =
|
||||||
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||||
|
|
||||||
if (extension == "nro")
|
if (extension == "nro")
|
||||||
return FileType::NRO;
|
return FileType::NRO;
|
||||||
if (extension == "nso")
|
else if (extension == "nso")
|
||||||
return FileType::NSO;
|
return FileType::NSO;
|
||||||
if (extension == "nca")
|
else if (extension == "nca")
|
||||||
return FileType::NCA;
|
return FileType::NCA;
|
||||||
if (extension == "xci")
|
else if (extension == "xci")
|
||||||
return FileType::XCI;
|
return FileType::XCI;
|
||||||
if (extension == "nsp")
|
else if (extension == "nsp")
|
||||||
return FileType::NSP;
|
return FileType::NSP;
|
||||||
if (extension == "kip")
|
else if (extension == "kip")
|
||||||
return FileType::KIP;
|
return FileType::KIP;
|
||||||
|
|
||||||
return FileType::Unknown;
|
return FileType::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -223,7 +226,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S
|
||||||
}
|
}
|
||||||
|
|
||||||
modules.insert_or_assign(base_address, file->GetName());
|
modules.insert_or_assign(base_address, file->GetName());
|
||||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
|
LOG_DEBUG(Loader, "loaded module {} @ {:#X}", file->GetName(), base_address);
|
||||||
|
|
||||||
is_loaded = true;
|
is_loaded = true;
|
||||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
||||||
|
|
|
@ -61,8 +61,7 @@ struct Memory::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
heap_tracker.emplace(system.DeviceMemory().buffer);
|
buffer.emplace(system.DeviceMemory().buffer);
|
||||||
buffer = std::addressof(*heap_tracker);
|
|
||||||
#else
|
#else
|
||||||
buffer = std::addressof(system.DeviceMemory().buffer);
|
buffer = std::addressof(system.DeviceMemory().buffer);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1024,9 +1023,8 @@ struct Memory::Impl {
|
||||||
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
||||||
std::mutex sys_core_guard;
|
std::mutex sys_core_guard;
|
||||||
|
|
||||||
std::optional<Common::HeapTracker> heap_tracker;
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
Common::HeapTracker* buffer{};
|
std::optional<Common::HeapTracker> buffer;
|
||||||
#else
|
#else
|
||||||
Common::HostMemory* buffer{};
|
Common::HostMemory* buffer{};
|
||||||
#endif
|
#endif
|
||||||
|
@ -1230,22 +1228,7 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
impl->InvalidateGPUMemory(ptr, size);
|
impl->InvalidateGPUMemory(ptr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
if (!rasterizer && mapped) {
|
|
||||||
impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return mapped && ptr != nullptr;
|
return mapped && ptr != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
|
||||||
#ifdef __linux__
|
|
||||||
return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Core::Memory
|
} // namespace Core::Memory
|
||||||
|
|
|
@ -487,13 +487,8 @@ public:
|
||||||
* marked as debug or non-debug.
|
* marked as debug or non-debug.
|
||||||
*/
|
*/
|
||||||
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
|
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
|
||||||
|
|
||||||
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
||||||
|
|
||||||
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
||||||
|
|
||||||
bool InvalidateSeparateHeap(void* fault_address);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ contain a prediction with the same `UniqueHash`.
|
||||||
? u64(unique_hash_to_code_ptr[imm64])
|
? u64(unique_hash_to_code_ptr[imm64])
|
||||||
: u64(code->GetReturnFromRunCodeAddress());
|
: u64(code->GetReturnFromRunCodeAddress());
|
||||||
|
|
||||||
code->mov(index_reg, dword[r15 + offsetof(JitState, rsb_ptr)]);
|
code->mov(index_reg, dword[code.ABI_JIT_PTR + offsetof(JitState, rsb_ptr)]);
|
||||||
code->add(index_reg, 1);
|
code->add(index_reg, 1);
|
||||||
code->and_(index_reg, u32(JitState::RSBSize - 1));
|
code->and_(index_reg, u32(JitState::RSBSize - 1));
|
||||||
|
|
||||||
|
@ -91,13 +91,13 @@ contain a prediction with the same `UniqueHash`.
|
||||||
|
|
||||||
Xbyak::Label label;
|
Xbyak::Label label;
|
||||||
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
||||||
code->cmp(loc_desc_reg, qword[r15 + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
code->cmp(loc_desc_reg, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
||||||
code->je(label, code->T_SHORT);
|
code->je(label, code->T_SHORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
code->mov(dword[r15 + offsetof(JitState, rsb_ptr)], index_reg);
|
code->mov(dword[code.ABI_JIT_PTR + offsetof(JitState, rsb_ptr)], index_reg);
|
||||||
code->mov(qword[r15 + index_reg.cvt64() * 8 + offsetof(JitState, rsb_location_descriptors)], loc_desc_reg);
|
code->mov(qword[code.ABI_JIT_PTR + index_reg.cvt64() * 8 + offsetof(JitState, rsb_location_descriptors)], loc_desc_reg);
|
||||||
code->mov(qword[r15 + index_reg.cvt64() * 8 + offsetof(JitState, rsb_codeptrs)], code_ptr_reg);
|
code->mov(qword[code.ABI_JIT_PTR + index_reg.cvt64() * 8 + offsetof(JitState, rsb_codeptrs)], code_ptr_reg);
|
||||||
code->L(label);
|
code->L(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,14 +122,14 @@ To check if a predicition is in the RSB, we linearly scan the RSB.
|
||||||
// This calculation has to match up with IREmitter::PushRSB
|
// This calculation has to match up with IREmitter::PushRSB
|
||||||
code->mov(ecx, MJitStateReg(Arm::Reg::PC));
|
code->mov(ecx, MJitStateReg(Arm::Reg::PC));
|
||||||
code->shl(rcx, 32);
|
code->shl(rcx, 32);
|
||||||
code->mov(ebx, dword[r15 + offsetof(JitState, FPSCR_mode)]);
|
code->mov(ebx, dword[code.ABI_JIT_PTR + offsetof(JitState, FPSCR_mode)]);
|
||||||
code->or_(ebx, dword[r15 + offsetof(JitState, CPSR_et)]);
|
code->or_(ebx, dword[code.ABI_JIT_PTR + offsetof(JitState, CPSR_et)]);
|
||||||
code->or_(rbx, rcx);
|
code->or_(rbx, rcx);
|
||||||
|
|
||||||
code->mov(rax, u64(code->GetReturnFromRunCodeAddress()));
|
code->mov(rax, u64(code->GetReturnFromRunCodeAddress()));
|
||||||
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
for (size_t i = 0; i < JitState::RSBSize; ++i) {
|
||||||
code->cmp(rbx, qword[r15 + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
code->cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
|
||||||
code->cmove(rax, qword[r15 + offsetof(JitState, rsb_codeptrs) + i * sizeof(u64)]);
|
code->cmove(rax, qword[code.ABI_JIT_PTR + offsetof(JitState, rsb_codeptrs) + i * sizeof(u64)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
code->jmp(rax);
|
code->jmp(rax);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
|
||||||
ExclusiveMonitor::ExclusiveMonitor(size_t processor_count)
|
ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count)
|
||||||
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {}
|
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {}
|
||||||
|
|
||||||
size_t ExclusiveMonitor::GetProcessorCount() const {
|
size_t ExclusiveMonitor::GetProcessorCount() const {
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct Label;
|
||||||
} // namespace oaknut
|
} // namespace oaknut
|
||||||
|
|
||||||
namespace Dynarmic::IR {
|
namespace Dynarmic::IR {
|
||||||
enum class Type;
|
enum class Type : u16;
|
||||||
} // namespace Dynarmic::IR
|
} // namespace Dynarmic::IR
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
|
@ -44,21 +44,21 @@ namespace Dynarmic::Backend::X64 {
|
||||||
using namespace Xbyak::util;
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
static Xbyak::Address MJitStateReg(A32::Reg reg) {
|
static Xbyak::Address MJitStateReg(A32::Reg reg) {
|
||||||
return dword[r15 + offsetof(A32JitState, Reg) + sizeof(u32) * static_cast<size_t>(reg)];
|
return dword[BlockOfCode::ABI_JIT_PTR + offsetof(A32JitState, Reg) + sizeof(u32) * static_cast<size_t>(reg)];
|
||||||
}
|
}
|
||||||
|
|
||||||
static Xbyak::Address MJitStateExtReg(A32::ExtReg reg) {
|
static Xbyak::Address MJitStateExtReg(A32::ExtReg reg) {
|
||||||
if (A32::IsSingleExtReg(reg)) {
|
if (A32::IsSingleExtReg(reg)) {
|
||||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::S0);
|
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::S0);
|
||||||
return dword[r15 + offsetof(A32JitState, ExtReg) + sizeof(u32) * index];
|
return dword[BlockOfCode::ABI_JIT_PTR + offsetof(A32JitState, ExtReg) + sizeof(u32) * index];
|
||||||
}
|
}
|
||||||
if (A32::IsDoubleExtReg(reg)) {
|
if (A32::IsDoubleExtReg(reg)) {
|
||||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::D0);
|
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::D0);
|
||||||
return qword[r15 + offsetof(A32JitState, ExtReg) + sizeof(u64) * index];
|
return qword[BlockOfCode::ABI_JIT_PTR + offsetof(A32JitState, ExtReg) + sizeof(u64) * index];
|
||||||
}
|
}
|
||||||
if (A32::IsQuadExtReg(reg)) {
|
if (A32::IsQuadExtReg(reg)) {
|
||||||
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::Q0);
|
const size_t index = static_cast<size_t>(reg) - static_cast<size_t>(A32::ExtReg::Q0);
|
||||||
return xword[r15 + offsetof(A32JitState, ExtReg) + 2 * sizeof(u64) * index];
|
return xword[BlockOfCode::ABI_JIT_PTR + offsetof(A32JitState, ExtReg) + 2 * sizeof(u64) * index];
|
||||||
}
|
}
|
||||||
ASSERT_FALSE("Should never happen.");
|
ASSERT_FALSE("Should never happen.");
|
||||||
}
|
}
|
||||||
|
@ -109,12 +109,12 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||||
|
|
||||||
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
||||||
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
||||||
if (conf.page_table) {
|
|
||||||
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R14));
|
|
||||||
}
|
|
||||||
if (conf.fastmem_pointer) {
|
if (conf.fastmem_pointer) {
|
||||||
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R13));
|
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R13));
|
||||||
}
|
}
|
||||||
|
if (conf.page_table) {
|
||||||
|
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R14));
|
||||||
|
}
|
||||||
return gprs;
|
return gprs;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ void A32EmitX64::GenTerminalHandlers() {
|
||||||
// PC ends up in ebp, location_descriptor ends up in rbx
|
// PC ends up in ebp, location_descriptor ends up in rbx
|
||||||
const auto calculate_location_descriptor = [this] {
|
const auto calculate_location_descriptor = [this] {
|
||||||
// This calculation has to match up with IREmitter::PushRSB
|
// This calculation has to match up with IREmitter::PushRSB
|
||||||
code.mov(ebx, dword[r15 + offsetof(A32JitState, upper_location_descriptor)]);
|
code.mov(ebx, dword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)]);
|
||||||
code.shl(rbx, 32);
|
code.shl(rbx, 32);
|
||||||
code.mov(ecx, MJitStateReg(A32::Reg::PC));
|
code.mov(ecx, MJitStateReg(A32::Reg::PC));
|
||||||
code.mov(ebp, ecx);
|
code.mov(ebp, ecx);
|
||||||
|
@ -232,17 +232,17 @@ void A32EmitX64::GenTerminalHandlers() {
|
||||||
code.align();
|
code.align();
|
||||||
terminal_handler_pop_rsb_hint = code.getCurr<const void*>();
|
terminal_handler_pop_rsb_hint = code.getCurr<const void*>();
|
||||||
calculate_location_descriptor();
|
calculate_location_descriptor();
|
||||||
code.mov(eax, dword[r15 + offsetof(A32JitState, rsb_ptr)]);
|
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)]);
|
||||||
code.dec(eax);
|
code.sub(eax, 1);
|
||||||
code.and_(eax, u32(A32JitState::RSBPtrMask));
|
code.and_(eax, u32(A32JitState::RSBPtrMask));
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, rsb_ptr)], eax);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)], eax);
|
||||||
code.cmp(rbx, qword[r15 + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
||||||
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
||||||
code.jne(rsb_cache_miss);
|
code.jne(rsb_cache_miss);
|
||||||
} else {
|
} else {
|
||||||
code.jne(code.GetReturnFromRunCodeAddress());
|
code.jne(code.GetReturnFromRunCodeAddress());
|
||||||
}
|
}
|
||||||
code.mov(rax, qword[r15 + offsetof(A32JitState, rsb_codeptrs) + rax * sizeof(u64)]);
|
code.mov(rax, qword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_codeptrs) + rax * sizeof(u64)]);
|
||||||
code.jmp(rax);
|
code.jmp(rax);
|
||||||
PerfMapRegister(terminal_handler_pop_rsb_hint, code.getCurr(), "a32_terminal_handler_pop_rsb_hint");
|
PerfMapRegister(terminal_handler_pop_rsb_hint, code.getCurr(), "a32_terminal_handler_pop_rsb_hint");
|
||||||
|
|
||||||
|
@ -392,17 +392,17 @@ void A32EmitX64::EmitA32GetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
// so we load them both at the same time with one 64-bit read. This allows us to
|
// so we load them both at the same time with one 64-bit read. This allows us to
|
||||||
// extract all of their bits together at once with one pext.
|
// extract all of their bits together at once with one pext.
|
||||||
static_assert(offsetof(A32JitState, upper_location_descriptor) + 4 == offsetof(A32JitState, cpsr_ge));
|
static_assert(offsetof(A32JitState, upper_location_descriptor) + 4 == offsetof(A32JitState, cpsr_ge));
|
||||||
code.mov(result.cvt64(), qword[r15 + offsetof(A32JitState, upper_location_descriptor)]);
|
code.mov(result.cvt64(), qword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)]);
|
||||||
code.mov(tmp.cvt64(), 0x80808080'00000003ull);
|
code.mov(tmp.cvt64(), 0x80808080'00000003ull);
|
||||||
code.pext(result.cvt64(), result.cvt64(), tmp.cvt64());
|
code.pext(result.cvt64(), result.cvt64(), tmp.cvt64());
|
||||||
code.mov(tmp, 0x000f0220);
|
code.mov(tmp, 0x000f0220);
|
||||||
code.pdep(result, result, tmp);
|
code.pdep(result, result, tmp);
|
||||||
} else {
|
} else {
|
||||||
code.mov(result, dword[r15 + offsetof(A32JitState, upper_location_descriptor)]);
|
code.mov(result, dword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)]);
|
||||||
code.imul(result, result, 0x120);
|
code.imul(result, result, 0x120);
|
||||||
code.and_(result, 0x00000220);
|
code.and_(result, 0x00000220);
|
||||||
|
|
||||||
code.mov(tmp, dword[r15 + offsetof(A32JitState, cpsr_ge)]);
|
code.mov(tmp, dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)]);
|
||||||
code.and_(tmp, 0x80808080);
|
code.and_(tmp, 0x80808080);
|
||||||
code.imul(tmp, tmp, 0x00204081);
|
code.imul(tmp, tmp, 0x00204081);
|
||||||
code.shr(tmp, 12);
|
code.shr(tmp, 12);
|
||||||
|
@ -410,11 +410,11 @@ void A32EmitX64::EmitA32GetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.or_(result, tmp);
|
code.or_(result, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.mov(tmp, dword[r15 + offsetof(A32JitState, cpsr_q)]);
|
code.mov(tmp, dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_q)]);
|
||||||
code.shl(tmp, 27);
|
code.shl(tmp, 27);
|
||||||
code.or_(result, tmp);
|
code.or_(result, tmp);
|
||||||
|
|
||||||
code.mov(tmp2, dword[r15 + offsetof(A32JitState, cpsr_nzcv)]);
|
code.mov(tmp2, dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)]);
|
||||||
if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
||||||
code.mov(tmp, NZCV::x64_mask);
|
code.mov(tmp, NZCV::x64_mask);
|
||||||
code.pext(tmp2, tmp2, tmp);
|
code.pext(tmp2, tmp2, tmp);
|
||||||
|
@ -426,7 +426,7 @@ void A32EmitX64::EmitA32GetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
code.or_(result, tmp2);
|
code.or_(result, tmp2);
|
||||||
|
|
||||||
code.or_(result, dword[r15 + offsetof(A32JitState, cpsr_jaifm)]);
|
code.or_(result, dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_jaifm)]);
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,7 @@ void A32EmitX64::EmitA32SetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
// cpsr_q
|
// cpsr_q
|
||||||
code.bt(cpsr, 27);
|
code.bt(cpsr, 27);
|
||||||
code.setc(code.byte[r15 + offsetof(A32JitState, cpsr_q)]);
|
code.setc(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_q)]);
|
||||||
|
|
||||||
// cpsr_nzcv
|
// cpsr_nzcv
|
||||||
code.mov(tmp, cpsr);
|
code.mov(tmp, cpsr);
|
||||||
|
@ -456,12 +456,12 @@ void A32EmitX64::EmitA32SetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.imul(tmp, tmp, NZCV::to_x64_multiplier);
|
code.imul(tmp, tmp, NZCV::to_x64_multiplier);
|
||||||
code.and_(tmp, NZCV::x64_mask);
|
code.and_(tmp, NZCV::x64_mask);
|
||||||
}
|
}
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], tmp);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], tmp);
|
||||||
|
|
||||||
// cpsr_jaifm
|
// cpsr_jaifm
|
||||||
code.mov(tmp, cpsr);
|
code.mov(tmp, cpsr);
|
||||||
code.and_(tmp, 0x010001DF);
|
code.and_(tmp, 0x010001DF);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_jaifm)], tmp);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_jaifm)], tmp);
|
||||||
|
|
||||||
if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
||||||
// cpsr_et and cpsr_ge
|
// cpsr_et and cpsr_ge
|
||||||
|
@ -469,7 +469,7 @@ void A32EmitX64::EmitA32SetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
// This mask is 0x7FFF0000, because we do not want the MSB to be sign extended to the upper dword.
|
// This mask is 0x7FFF0000, because we do not want the MSB to be sign extended to the upper dword.
|
||||||
static_assert((A32::LocationDescriptor::FPSCR_MODE_MASK & ~0x7FFF0000) == 0);
|
static_assert((A32::LocationDescriptor::FPSCR_MODE_MASK & ~0x7FFF0000) == 0);
|
||||||
|
|
||||||
code.and_(qword[r15 + offsetof(A32JitState, upper_location_descriptor)], u32(0x7FFF0000));
|
code.and_(qword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)], u32(0x7FFF0000));
|
||||||
code.mov(tmp, 0x000f0220);
|
code.mov(tmp, 0x000f0220);
|
||||||
code.pext(cpsr, cpsr, tmp);
|
code.pext(cpsr, cpsr, tmp);
|
||||||
code.mov(tmp.cvt64(), 0x01010101'00000003ull);
|
code.mov(tmp.cvt64(), 0x01010101'00000003ull);
|
||||||
|
@ -479,14 +479,14 @@ void A32EmitX64::EmitA32SetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.mov(tmp2.cvt64(), tmp.cvt64());
|
code.mov(tmp2.cvt64(), tmp.cvt64());
|
||||||
code.sub(tmp.cvt64(), cpsr.cvt64());
|
code.sub(tmp.cvt64(), cpsr.cvt64());
|
||||||
code.xor_(tmp.cvt64(), tmp2.cvt64());
|
code.xor_(tmp.cvt64(), tmp2.cvt64());
|
||||||
code.or_(qword[r15 + offsetof(A32JitState, upper_location_descriptor)], tmp.cvt64());
|
code.or_(qword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)], tmp.cvt64());
|
||||||
} else {
|
} else {
|
||||||
code.and_(dword[r15 + offsetof(A32JitState, upper_location_descriptor)], u32(0xFFFF0000));
|
code.and_(dword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)], u32(0xFFFF0000));
|
||||||
code.mov(tmp, cpsr);
|
code.mov(tmp, cpsr);
|
||||||
code.and_(tmp, 0x00000220);
|
code.and_(tmp, 0x00000220);
|
||||||
code.imul(tmp, tmp, 0x00900000);
|
code.imul(tmp, tmp, 0x00900000);
|
||||||
code.shr(tmp, 28);
|
code.shr(tmp, 28);
|
||||||
code.or_(dword[r15 + offsetof(A32JitState, upper_location_descriptor)], tmp);
|
code.or_(dword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)], tmp);
|
||||||
|
|
||||||
code.and_(cpsr, 0x000f0000);
|
code.and_(cpsr, 0x000f0000);
|
||||||
code.shr(cpsr, 16);
|
code.shr(cpsr, 16);
|
||||||
|
@ -495,14 +495,14 @@ void A32EmitX64::EmitA32SetCpsr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.mov(tmp, 0x80808080);
|
code.mov(tmp, 0x80808080);
|
||||||
code.sub(tmp, cpsr);
|
code.sub(tmp, cpsr);
|
||||||
code.xor_(tmp, 0x80808080);
|
code.xor_(tmp, 0x80808080);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_ge)], tmp);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)], tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32SetCpsrNZCV(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32SetCpsrNZCV(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const Xbyak::Reg32 to_store = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 to_store = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], to_store);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], to_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32SetCpsrNZCVRaw(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32SetCpsrNZCVRaw(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
@ -510,7 +510,7 @@ void A32EmitX64::EmitA32SetCpsrNZCVRaw(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (args[0].IsImmediate()) {
|
if (args[0].IsImmediate()) {
|
||||||
const u32 imm = args[0].GetImmediateU32();
|
const u32 imm = args[0].GetImmediateU32();
|
||||||
|
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], NZCV::ToX64(imm));
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], NZCV::ToX64(imm));
|
||||||
} else if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
} else if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
||||||
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
const Xbyak::Reg32 b = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 b = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
@ -518,14 +518,14 @@ void A32EmitX64::EmitA32SetCpsrNZCVRaw(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.shr(a, 28);
|
code.shr(a, 28);
|
||||||
code.mov(b, NZCV::x64_mask);
|
code.mov(b, NZCV::x64_mask);
|
||||||
code.pdep(a, a, b);
|
code.pdep(a, a, b);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], a);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], a);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
|
||||||
code.shr(a, 28);
|
code.shr(a, 28);
|
||||||
code.imul(a, a, NZCV::to_x64_multiplier);
|
code.imul(a, a, NZCV::to_x64_multiplier);
|
||||||
code.and_(a, NZCV::x64_mask);
|
code.and_(a, NZCV::x64_mask);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], a);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,25 +534,25 @@ void A32EmitX64::EmitA32SetCpsrNZCVQ(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (args[0].IsImmediate()) {
|
if (args[0].IsImmediate()) {
|
||||||
const u32 imm = args[0].GetImmediateU32();
|
const u32 imm = args[0].GetImmediateU32();
|
||||||
|
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], NZCV::ToX64(imm));
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], NZCV::ToX64(imm));
|
||||||
code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_q)], u8((imm & 0x08000000) != 0 ? 1 : 0));
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_q)], u8((imm & 0x08000000) != 0 ? 1 : 0));
|
||||||
} else if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
} else if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
||||||
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
const Xbyak::Reg32 b = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 b = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
code.shr(a, 28);
|
code.shr(a, 28);
|
||||||
code.setc(code.byte[r15 + offsetof(A32JitState, cpsr_q)]);
|
code.setc(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_q)]);
|
||||||
code.mov(b, NZCV::x64_mask);
|
code.mov(b, NZCV::x64_mask);
|
||||||
code.pdep(a, a, b);
|
code.pdep(a, a, b);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], a);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], a);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
|
||||||
code.shr(a, 28);
|
code.shr(a, 28);
|
||||||
code.setc(code.byte[r15 + offsetof(A32JitState, cpsr_q)]);
|
code.setc(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_q)]);
|
||||||
code.imul(a, a, NZCV::to_x64_multiplier);
|
code.imul(a, a, NZCV::to_x64_multiplier);
|
||||||
code.and_(a, NZCV::x64_mask);
|
code.and_(a, NZCV::x64_mask);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_nzcv)], a);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)], a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,10 +562,10 @@ void A32EmitX64::EmitA32SetCpsrNZ(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Reg32 nz = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
const Xbyak::Reg32 nz = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||||
const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
code.movzx(tmp, code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1]);
|
code.movzx(tmp, code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv) + 1]);
|
||||||
code.and_(tmp, 1);
|
code.and_(tmp, 1);
|
||||||
code.or_(tmp, nz);
|
code.or_(tmp, nz);
|
||||||
code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], tmp.cvt8());
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv) + 1], tmp.cvt8());
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32SetCpsrNZC(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32SetCpsrNZC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
@ -575,11 +575,11 @@ void A32EmitX64::EmitA32SetCpsrNZC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (args[1].IsImmediate()) {
|
if (args[1].IsImmediate()) {
|
||||||
const bool c = args[1].GetImmediateU1();
|
const bool c = args[1].GetImmediateU1();
|
||||||
|
|
||||||
code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], c);
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv) + 1], c);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg8 c = ctx.reg_alloc.UseGpr(args[1]).cvt8();
|
const Xbyak::Reg8 c = ctx.reg_alloc.UseGpr(args[1]).cvt8();
|
||||||
|
|
||||||
code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], c);
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv) + 1], c);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg32 nz = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 nz = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
@ -588,19 +588,19 @@ void A32EmitX64::EmitA32SetCpsrNZC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
const bool c = args[1].GetImmediateU1();
|
const bool c = args[1].GetImmediateU1();
|
||||||
|
|
||||||
code.or_(nz, c);
|
code.or_(nz, c);
|
||||||
code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], nz.cvt8());
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv) + 1], nz.cvt8());
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg32 c = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
const Xbyak::Reg32 c = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
||||||
|
|
||||||
code.or_(nz, c);
|
code.or_(nz, c);
|
||||||
code.mov(code.byte[r15 + offsetof(A32JitState, cpsr_nzcv) + 1], nz.cvt8());
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv) + 1], nz.cvt8());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EmitGetFlag(BlockOfCode& code, A32EmitContext& ctx, IR::Inst* inst, size_t flag_bit) {
|
static void EmitGetFlag(BlockOfCode& code, A32EmitContext& ctx, IR::Inst* inst, size_t flag_bit) {
|
||||||
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
code.mov(result, dword[r15 + offsetof(A32JitState, cpsr_nzcv)]);
|
code.mov(result, dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_nzcv)]);
|
||||||
if (flag_bit != 0) {
|
if (flag_bit != 0) {
|
||||||
code.shr(result, static_cast<int>(flag_bit));
|
code.shr(result, static_cast<int>(flag_bit));
|
||||||
}
|
}
|
||||||
|
@ -616,18 +616,18 @@ void A32EmitX64::EmitA32OrQFlag(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
if (args[0].IsImmediate()) {
|
if (args[0].IsImmediate()) {
|
||||||
if (args[0].GetImmediateU1()) {
|
if (args[0].GetImmediateU1()) {
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_q)], 1);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_q)], 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg8 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt8();
|
const Xbyak::Reg8 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt8();
|
||||||
|
|
||||||
code.or_(code.byte[r15 + offsetof(A32JitState, cpsr_q)], to_store);
|
code.or_(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_q)], to_store);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32GetGEFlags(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32GetGEFlags(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||||
code.movd(result, dword[r15 + offsetof(A32JitState, cpsr_ge)]);
|
code.movd(result, dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)]);
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,10 +637,10 @@ void A32EmitX64::EmitA32SetGEFlags(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
if (args[0].IsInXmm()) {
|
if (args[0].IsInXmm()) {
|
||||||
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[0]);
|
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[0]);
|
||||||
code.movd(dword[r15 + offsetof(A32JitState, cpsr_ge)], to_store);
|
code.movd(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)], to_store);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg32 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
const Xbyak::Reg32 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_ge)], to_store);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)], to_store);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,7 +654,7 @@ void A32EmitX64::EmitA32SetGEFlagsCompressed(A32EmitContext& ctx, IR::Inst* inst
|
||||||
ge |= mcl::bit::get_bit<17>(imm) ? 0x0000FF00 : 0;
|
ge |= mcl::bit::get_bit<17>(imm) ? 0x0000FF00 : 0;
|
||||||
ge |= mcl::bit::get_bit<16>(imm) ? 0x000000FF : 0;
|
ge |= mcl::bit::get_bit<16>(imm) ? 0x000000FF : 0;
|
||||||
|
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_ge)], ge);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)], ge);
|
||||||
} else if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
} else if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
||||||
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
const Xbyak::Reg32 b = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 b = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
@ -663,7 +663,7 @@ void A32EmitX64::EmitA32SetGEFlagsCompressed(A32EmitContext& ctx, IR::Inst* inst
|
||||||
code.shr(a, 16);
|
code.shr(a, 16);
|
||||||
code.pdep(a, a, b);
|
code.pdep(a, a, b);
|
||||||
code.imul(a, a, 0xFF);
|
code.imul(a, a, 0xFF);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_ge)], a);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)], a);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
|
||||||
|
@ -672,7 +672,7 @@ void A32EmitX64::EmitA32SetGEFlagsCompressed(A32EmitContext& ctx, IR::Inst* inst
|
||||||
code.imul(a, a, 0x00204081);
|
code.imul(a, a, 0x00204081);
|
||||||
code.and_(a, 0x01010101);
|
code.and_(a, 0x01010101);
|
||||||
code.imul(a, a, 0xFF);
|
code.imul(a, a, 0xFF);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, cpsr_ge)], a);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, cpsr_ge)], a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +716,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
const u32 new_upper = upper_without_t | (mcl::bit::get_bit<0>(new_pc) ? 1 : 0);
|
const u32 new_upper = upper_without_t | (mcl::bit::get_bit<0>(new_pc) ? 1 : 0);
|
||||||
|
|
||||||
code.mov(MJitStateReg(A32::Reg::PC), new_pc & mask);
|
code.mov(MJitStateReg(A32::Reg::PC), new_pc & mask);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, upper_location_descriptor)], new_upper);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)], new_upper);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Reg32 new_pc = ctx.reg_alloc.UseScratchGpr(arg).cvt32();
|
const Xbyak::Reg32 new_pc = ctx.reg_alloc.UseScratchGpr(arg).cvt32();
|
||||||
const Xbyak::Reg32 mask = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 mask = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
@ -728,7 +728,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.lea(mask, ptr[mask.cvt64() + mask.cvt64() * 1 - 4]); // mask = pc & 1 ? 0xFFFFFFFE : 0xFFFFFFFC
|
code.lea(mask, ptr[mask.cvt64() + mask.cvt64() * 1 - 4]); // mask = pc & 1 ? 0xFFFFFFFE : 0xFFFFFFFC
|
||||||
code.and_(new_pc, mask);
|
code.and_(new_pc, mask);
|
||||||
code.mov(MJitStateReg(A32::Reg::PC), new_pc);
|
code.mov(MJitStateReg(A32::Reg::PC), new_pc);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, upper_location_descriptor)], new_upper);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)], new_upper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,9 +798,9 @@ static u32 GetFpscrImpl(A32JitState* jit_state) {
|
||||||
|
|
||||||
void A32EmitX64::EmitA32GetFpscr(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32GetFpscr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
ctx.reg_alloc.HostCall(inst);
|
ctx.reg_alloc.HostCall(inst);
|
||||||
code.mov(code.ABI_PARAM1, code.r15);
|
code.mov(code.ABI_PARAM1, code.ABI_JIT_PTR);
|
||||||
|
|
||||||
code.stmxcsr(code.dword[code.r15 + offsetof(A32JitState, guest_MXCSR)]);
|
code.stmxcsr(code.dword[code.ABI_JIT_PTR + offsetof(A32JitState, guest_MXCSR)]);
|
||||||
code.CallFunction(&GetFpscrImpl);
|
code.CallFunction(&GetFpscrImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,15 +811,15 @@ static void SetFpscrImpl(u32 value, A32JitState* jit_state) {
|
||||||
void A32EmitX64::EmitA32SetFpscr(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32SetFpscr(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
ctx.reg_alloc.HostCall(nullptr, args[0]);
|
ctx.reg_alloc.HostCall(nullptr, args[0]);
|
||||||
code.mov(code.ABI_PARAM2, code.r15);
|
code.mov(code.ABI_PARAM2, code.ABI_JIT_PTR);
|
||||||
|
|
||||||
code.CallFunction(&SetFpscrImpl);
|
code.CallFunction(&SetFpscrImpl);
|
||||||
code.ldmxcsr(code.dword[code.r15 + offsetof(A32JitState, guest_MXCSR)]);
|
code.ldmxcsr(code.dword[code.ABI_JIT_PTR + offsetof(A32JitState, guest_MXCSR)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32GetFpscrNZCV(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32GetFpscrNZCV(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
code.mov(result, dword[r15 + offsetof(A32JitState, fpsr_nzcv)]);
|
code.mov(result, dword[code.ABI_JIT_PTR + offsetof(A32JitState, fpsr_nzcv)]);
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,7 +833,7 @@ void A32EmitX64::EmitA32SetFpscrNZCV(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.mov(tmp, NZCV::x64_mask);
|
code.mov(tmp, NZCV::x64_mask);
|
||||||
code.pext(tmp, value, tmp);
|
code.pext(tmp, value, tmp);
|
||||||
code.shl(tmp, 28);
|
code.shl(tmp, 28);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, fpsr_nzcv)], tmp);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, fpsr_nzcv)], tmp);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -843,7 +843,7 @@ void A32EmitX64::EmitA32SetFpscrNZCV(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.and_(value, NZCV::x64_mask);
|
code.and_(value, NZCV::x64_mask);
|
||||||
code.imul(value, value, NZCV::from_x64_multiplier);
|
code.imul(value, value, NZCV::from_x64_multiplier);
|
||||||
code.and_(value, NZCV::arm_mask);
|
code.and_(value, NZCV::arm_mask);
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, fpsr_nzcv)], value);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, fpsr_nzcv)], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EmitCoprocessorException() {
|
static void EmitCoprocessorException() {
|
||||||
|
@ -1155,7 +1155,7 @@ void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_locat
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (old_upper != new_upper) {
|
if (old_upper != new_upper) {
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, upper_location_descriptor)], new_upper);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, upper_location_descriptor)], new_upper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,32 +1165,28 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc
|
||||||
if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||||
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||||
code.ReturnFromRunCode();
|
code.ReturnFromRunCode();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conf.enable_cycle_counting) {
|
|
||||||
code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
|
||||||
|
|
||||||
patch_information[terminal.next].jg.push_back(code.getCurr());
|
|
||||||
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
|
||||||
EmitPatchJg(terminal.next, next_bb->entrypoint);
|
|
||||||
} else {
|
|
||||||
EmitPatchJg(terminal.next);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
code.cmp(dword[r15 + offsetof(A32JitState, halt_reason)], 0);
|
if (conf.enable_cycle_counting) {
|
||||||
|
code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
||||||
patch_information[terminal.next].jz.push_back(code.getCurr());
|
patch_information[terminal.next].jg.push_back(code.getCurr());
|
||||||
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
EmitPatchJz(terminal.next, next_bb->entrypoint);
|
EmitPatchJg(terminal.next, next_bb->entrypoint);
|
||||||
|
} else {
|
||||||
|
EmitPatchJg(terminal.next);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
EmitPatchJz(terminal.next);
|
code.cmp(dword[code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0);
|
||||||
|
patch_information[terminal.next].jz.push_back(code.getCurr());
|
||||||
|
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
|
EmitPatchJz(terminal.next, next_bb->entrypoint);
|
||||||
|
} else {
|
||||||
|
EmitPatchJz(terminal.next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||||
|
PushRSBHelper(rax, rbx, terminal.next);
|
||||||
|
code.ForceReturnFromRunCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
|
||||||
PushRSBHelper(rax, rbx, terminal.next);
|
|
||||||
code.ForceReturnFromRunCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
|
@ -1199,14 +1195,13 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::Location
|
||||||
if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||||
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||||
code.ReturnFromRunCode();
|
code.ReturnFromRunCode();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
patch_information[terminal.next].jmp.push_back(code.getCurr());
|
|
||||||
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
|
||||||
EmitPatchJmp(terminal.next, next_bb->entrypoint);
|
|
||||||
} else {
|
} else {
|
||||||
EmitPatchJmp(terminal.next);
|
patch_information[terminal.next].jmp.push_back(code.getCurr());
|
||||||
|
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
|
EmitPatchJmp(terminal.next, next_bb->entrypoint);
|
||||||
|
} else {
|
||||||
|
EmitPatchJmp(terminal.next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1245,7 +1240,7 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescr
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
code.cmp(dword[r15 + offsetof(A32JitState, halt_reason)], 0);
|
code.cmp(dword[code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0);
|
||||||
code.jne(code.GetForceReturnFromRunCodeAddress());
|
code.jne(code.GetForceReturnFromRunCodeAddress());
|
||||||
EmitTerminal(terminal.else_, initial_location, is_single_step);
|
EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
|
void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
|
||||||
code.mov(code.byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0));
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A32JitState, exclusive_state)], u8(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
@ -244,14 +244,14 @@ void A32EmitX64::EmitCheckMemoryAbort(A32EmitContext& ctx, IR::Inst* inst, Xbyak
|
||||||
|
|
||||||
const A32::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}};
|
const A32::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}};
|
||||||
|
|
||||||
code.test(dword[r15 + offsetof(A32JitState, halt_reason)], static_cast<u32>(HaltReason::MemoryAbort));
|
code.test(dword[code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], static_cast<u32>(HaltReason::MemoryAbort));
|
||||||
if (end) {
|
if (end) {
|
||||||
code.jz(*end, code.T_NEAR);
|
code.jz(*end, code.T_NEAR);
|
||||||
} else {
|
} else {
|
||||||
code.jz(skip, code.T_NEAR);
|
code.jz(skip, code.T_NEAR);
|
||||||
}
|
}
|
||||||
EmitSetUpperLocationDescriptor(current_location, ctx.Location());
|
EmitSetUpperLocationDescriptor(current_location, ctx.Location());
|
||||||
code.mov(dword[r15 + offsetof(A32JitState, Reg) + sizeof(u32) * 15], current_location.PC());
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, Reg) + sizeof(u32) * 15], current_location.PC());
|
||||||
code.ForceReturnFromRunCode();
|
code.ForceReturnFromRunCode();
|
||||||
code.L(skip);
|
code.L(skip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,12 +80,12 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) noexcept {
|
||||||
|
|
||||||
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
||||||
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
||||||
if (conf.page_table) {
|
|
||||||
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R14));
|
|
||||||
}
|
|
||||||
if (conf.fastmem_pointer) {
|
if (conf.fastmem_pointer) {
|
||||||
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R13));
|
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R13));
|
||||||
}
|
}
|
||||||
|
if (conf.page_table) {
|
||||||
|
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R14));
|
||||||
|
}
|
||||||
return gprs;
|
return gprs;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
@ -192,10 +192,10 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||||
const auto calculate_location_descriptor = [this] {
|
const auto calculate_location_descriptor = [this] {
|
||||||
// This calculation has to match up with A64::LocationDescriptor::UniqueHash
|
// This calculation has to match up with A64::LocationDescriptor::UniqueHash
|
||||||
// TODO: Optimization is available here based on known state of fpcr.
|
// TODO: Optimization is available here based on known state of fpcr.
|
||||||
code.mov(rbp, qword[r15 + offsetof(A64JitState, pc)]);
|
code.mov(rbp, qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)]);
|
||||||
code.mov(rcx, A64::LocationDescriptor::pc_mask);
|
code.mov(rcx, A64::LocationDescriptor::pc_mask);
|
||||||
code.and_(rcx, rbp);
|
code.and_(rcx, rbp);
|
||||||
code.mov(ebx, dword[r15 + offsetof(A64JitState, fpcr)]);
|
code.mov(ebx, dword[code.ABI_JIT_PTR + offsetof(A64JitState, fpcr)]);
|
||||||
code.and_(ebx, A64::LocationDescriptor::fpcr_mask);
|
code.and_(ebx, A64::LocationDescriptor::fpcr_mask);
|
||||||
code.shl(rbx, A64::LocationDescriptor::fpcr_shift);
|
code.shl(rbx, A64::LocationDescriptor::fpcr_shift);
|
||||||
code.or_(rbx, rcx);
|
code.or_(rbx, rcx);
|
||||||
|
@ -207,17 +207,17 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||||
code.align();
|
code.align();
|
||||||
terminal_handler_pop_rsb_hint = code.getCurr<const void*>();
|
terminal_handler_pop_rsb_hint = code.getCurr<const void*>();
|
||||||
calculate_location_descriptor();
|
calculate_location_descriptor();
|
||||||
code.mov(eax, dword[r15 + offsetof(A64JitState, rsb_ptr)]);
|
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)]);
|
||||||
code.dec(eax);
|
code.sub(eax, 1);
|
||||||
code.and_(eax, u32(A64JitState::RSBPtrMask));
|
code.and_(eax, u32(A64JitState::RSBPtrMask));
|
||||||
code.mov(dword[r15 + offsetof(A64JitState, rsb_ptr)], eax);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)], eax);
|
||||||
code.cmp(rbx, qword[r15 + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
||||||
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
||||||
code.jne(rsb_cache_miss, code.T_NEAR);
|
code.jne(rsb_cache_miss, code.T_NEAR);
|
||||||
} else {
|
} else {
|
||||||
code.jne(code.GetReturnFromRunCodeAddress());
|
code.jne(code.GetReturnFromRunCodeAddress());
|
||||||
}
|
}
|
||||||
code.mov(rax, qword[r15 + offsetof(A64JitState, rsb_codeptrs) + rax * sizeof(u64)]);
|
code.mov(rax, qword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_codeptrs) + rax * sizeof(u64)]);
|
||||||
code.jmp(rax);
|
code.jmp(rax);
|
||||||
PerfMapRegister(terminal_handler_pop_rsb_hint, code.getCurr(), "a64_terminal_handler_pop_rsb_hint");
|
PerfMapRegister(terminal_handler_pop_rsb_hint, code.getCurr(), "a64_terminal_handler_pop_rsb_hint");
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ void A64EmitX64::EmitA64SetCheckBit(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetCFlag(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetCFlag(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
code.mov(result, dword[r15 + offsetof(A64JitState, cpsr_nzcv)]);
|
code.mov(result, dword[code.ABI_JIT_PTR + offsetof(A64JitState, cpsr_nzcv)]);
|
||||||
code.shr(result, NZCV::x64_c_flag_bit);
|
code.shr(result, NZCV::x64_c_flag_bit);
|
||||||
code.and_(result, 1);
|
code.and_(result, 1);
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
@ -281,7 +281,7 @@ void A64EmitX64::EmitA64GetCFlag(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
void A64EmitX64::EmitA64GetNZCVRaw(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetNZCVRaw(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Reg32 nzcv_raw = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 nzcv_raw = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
code.mov(nzcv_raw, dword[r15 + offsetof(A64JitState, cpsr_nzcv)]);
|
code.mov(nzcv_raw, dword[code.ABI_JIT_PTR + offsetof(A64JitState, cpsr_nzcv)]);
|
||||||
|
|
||||||
if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
if (code.HasHostFeature(HostFeature::FastBMI2)) {
|
||||||
const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
@ -310,20 +310,20 @@ void A64EmitX64::EmitA64SetNZCVRaw(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.imul(nzcv_raw, nzcv_raw, NZCV::to_x64_multiplier);
|
code.imul(nzcv_raw, nzcv_raw, NZCV::to_x64_multiplier);
|
||||||
code.and_(nzcv_raw, NZCV::x64_mask);
|
code.and_(nzcv_raw, NZCV::x64_mask);
|
||||||
}
|
}
|
||||||
code.mov(dword[r15 + offsetof(A64JitState, cpsr_nzcv)], nzcv_raw);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A64JitState, cpsr_nzcv)], nzcv_raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64SetNZCV(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetNZCV(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const Xbyak::Reg32 to_store = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
const Xbyak::Reg32 to_store = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
code.mov(dword[r15 + offsetof(A64JitState, cpsr_nzcv)], to_store);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A64JitState, cpsr_nzcv)], to_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetW(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetW(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
code.mov(result, dword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)]);
|
code.mov(result, dword[code.ABI_JIT_PTR + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)]);
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,13 +331,13 @@ void A64EmitX64::EmitA64GetX(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
const Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr();
|
const Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
code.mov(result, qword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)]);
|
code.mov(result, qword[code.ABI_JIT_PTR + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)]);
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetS(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetS(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
const auto addr = qword[r15 + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
const auto addr = qword[code.ABI_JIT_PTR + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
||||||
|
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||||
code.movd(result, addr);
|
code.movd(result, addr);
|
||||||
|
@ -346,7 +346,7 @@ void A64EmitX64::EmitA64GetS(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetD(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetD(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
const auto addr = qword[r15 + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
const auto addr = qword[code.ABI_JIT_PTR + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
||||||
|
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||||
code.movq(result, addr);
|
code.movq(result, addr);
|
||||||
|
@ -355,7 +355,7 @@ void A64EmitX64::EmitA64GetD(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetQ(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetQ(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
const auto addr = xword[r15 + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
const auto addr = xword[code.ABI_JIT_PTR + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
||||||
|
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||||
code.movaps(result, addr);
|
code.movaps(result, addr);
|
||||||
|
@ -364,13 +364,13 @@ void A64EmitX64::EmitA64GetQ(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetSP(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetSP(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr();
|
const Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr();
|
||||||
code.mov(result, qword[r15 + offsetof(A64JitState, sp)]);
|
code.mov(result, qword[code.ABI_JIT_PTR + offsetof(A64JitState, sp)]);
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetFPCR(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetFPCR(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
code.mov(result, dword[r15 + offsetof(A64JitState, fpcr)]);
|
code.mov(result, dword[code.ABI_JIT_PTR + offsetof(A64JitState, fpcr)]);
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,15 +380,15 @@ static u32 GetFPSRImpl(A64JitState* jit_state) {
|
||||||
|
|
||||||
void A64EmitX64::EmitA64GetFPSR(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64GetFPSR(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
ctx.reg_alloc.HostCall(inst);
|
ctx.reg_alloc.HostCall(inst);
|
||||||
code.mov(code.ABI_PARAM1, code.r15);
|
code.mov(code.ABI_PARAM1, code.ABI_JIT_PTR);
|
||||||
code.stmxcsr(code.dword[code.r15 + offsetof(A64JitState, guest_MXCSR)]);
|
code.stmxcsr(code.dword[code.ABI_JIT_PTR + offsetof(A64JitState, guest_MXCSR)]);
|
||||||
code.CallFunction(GetFPSRImpl);
|
code.CallFunction(GetFPSRImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64SetW(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetW(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
const auto addr = qword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)];
|
const auto addr = qword[code.ABI_JIT_PTR + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)];
|
||||||
if (args[1].FitsInImmediateS32()) {
|
if (args[1].FitsInImmediateS32()) {
|
||||||
code.mov(addr, args[1].GetImmediateS32());
|
code.mov(addr, args[1].GetImmediateS32());
|
||||||
} else {
|
} else {
|
||||||
|
@ -402,7 +402,7 @@ void A64EmitX64::EmitA64SetW(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
void A64EmitX64::EmitA64SetX(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetX(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
const auto addr = qword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)];
|
const auto addr = qword[code.ABI_JIT_PTR + offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg)];
|
||||||
if (args[1].FitsInImmediateS32()) {
|
if (args[1].FitsInImmediateS32()) {
|
||||||
code.mov(addr, args[1].GetImmediateS32());
|
code.mov(addr, args[1].GetImmediateS32());
|
||||||
} else if (args[1].IsInXmm()) {
|
} else if (args[1].IsInXmm()) {
|
||||||
|
@ -417,7 +417,7 @@ void A64EmitX64::EmitA64SetX(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
void A64EmitX64::EmitA64SetS(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetS(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
const auto addr = xword[r15 + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
const auto addr = xword[code.ABI_JIT_PTR + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
||||||
|
|
||||||
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]);
|
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
@ -430,7 +430,7 @@ void A64EmitX64::EmitA64SetS(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
void A64EmitX64::EmitA64SetD(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetD(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
const auto addr = xword[r15 + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
const auto addr = xword[code.ABI_JIT_PTR + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
||||||
|
|
||||||
const Xbyak::Xmm to_store = ctx.reg_alloc.UseScratchXmm(args[1]);
|
const Xbyak::Xmm to_store = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
code.movq(to_store, to_store); // TODO: Remove when able
|
code.movq(to_store, to_store); // TODO: Remove when able
|
||||||
|
@ -440,7 +440,7 @@ void A64EmitX64::EmitA64SetD(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
void A64EmitX64::EmitA64SetQ(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetQ(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
const auto addr = xword[r15 + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
const auto addr = xword[code.ABI_JIT_PTR + offsetof(A64JitState, vec) + sizeof(u64) * 2 * static_cast<size_t>(vec)];
|
||||||
|
|
||||||
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]);
|
const Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
code.movaps(addr, to_store);
|
code.movaps(addr, to_store);
|
||||||
|
@ -448,7 +448,7 @@ void A64EmitX64::EmitA64SetQ(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
|
||||||
void A64EmitX64::EmitA64SetSP(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetSP(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const auto addr = qword[r15 + offsetof(A64JitState, sp)];
|
const auto addr = qword[code.ABI_JIT_PTR + offsetof(A64JitState, sp)];
|
||||||
if (args[0].FitsInImmediateS32()) {
|
if (args[0].FitsInImmediateS32()) {
|
||||||
code.mov(addr, args[0].GetImmediateS32());
|
code.mov(addr, args[0].GetImmediateS32());
|
||||||
} else if (args[0].IsInXmm()) {
|
} else if (args[0].IsInXmm()) {
|
||||||
|
@ -467,9 +467,9 @@ static void SetFPCRImpl(A64JitState* jit_state, u32 value) {
|
||||||
void A64EmitX64::EmitA64SetFPCR(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetFPCR(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
|
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
|
||||||
code.mov(code.ABI_PARAM1, code.r15);
|
code.mov(code.ABI_PARAM1, code.ABI_JIT_PTR);
|
||||||
code.CallFunction(SetFPCRImpl);
|
code.CallFunction(SetFPCRImpl);
|
||||||
code.ldmxcsr(code.dword[code.r15 + offsetof(A64JitState, guest_MXCSR)]);
|
code.ldmxcsr(code.dword[code.ABI_JIT_PTR + offsetof(A64JitState, guest_MXCSR)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetFPSRImpl(A64JitState* jit_state, u32 value) {
|
static void SetFPSRImpl(A64JitState* jit_state, u32 value) {
|
||||||
|
@ -479,14 +479,14 @@ static void SetFPSRImpl(A64JitState* jit_state, u32 value) {
|
||||||
void A64EmitX64::EmitA64SetFPSR(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetFPSR(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
|
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
|
||||||
code.mov(code.ABI_PARAM1, code.r15);
|
code.mov(code.ABI_PARAM1, code.ABI_JIT_PTR);
|
||||||
code.CallFunction(SetFPSRImpl);
|
code.CallFunction(SetFPSRImpl);
|
||||||
code.ldmxcsr(code.dword[code.r15 + offsetof(A64JitState, guest_MXCSR)]);
|
code.ldmxcsr(code.dword[code.ABI_JIT_PTR + offsetof(A64JitState, guest_MXCSR)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64SetPC(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64SetPC(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const auto addr = qword[r15 + offsetof(A64JitState, pc)];
|
const auto addr = qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)];
|
||||||
if (args[0].FitsInImmediateS32()) {
|
if (args[0].FitsInImmediateS32()) {
|
||||||
code.mov(addr, args[0].GetImmediateS32());
|
code.mov(addr, args[0].GetImmediateS32());
|
||||||
} else if (args[0].IsInXmm()) {
|
} else if (args[0].IsInXmm()) {
|
||||||
|
@ -507,7 +507,7 @@ void A64EmitX64::EmitA64CallSupervisor(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
code.mov(param[0], imm);
|
code.mov(param[0], imm);
|
||||||
});
|
});
|
||||||
// The kernel would have to execute ERET to get here, which would clear exclusive state.
|
// The kernel would have to execute ERET to get here, which would clear exclusive state.
|
||||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(0));
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A64JitState, exclusive_state)], u8(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
@ -621,7 +621,7 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDesc
|
||||||
code.SwitchMxcsrOnExit();
|
code.SwitchMxcsrOnExit();
|
||||||
Devirtualize<&A64::UserCallbacks::InterpreterFallback>(conf.callbacks).EmitCall(code, [&](RegList param) {
|
Devirtualize<&A64::UserCallbacks::InterpreterFallback>(conf.callbacks).EmitCall(code, [&](RegList param) {
|
||||||
code.mov(param[0], A64::LocationDescriptor{terminal.next}.PC());
|
code.mov(param[0], A64::LocationDescriptor{terminal.next}.PC());
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], param[0]);
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], param[0]);
|
||||||
code.mov(param[1].cvt32(), terminal.num_instructions);
|
code.mov(param[1].cvt32(), terminal.num_instructions);
|
||||||
});
|
});
|
||||||
code.ReturnFromRunCode(true); // TODO: Check cycles
|
code.ReturnFromRunCode(true); // TODO: Check cycles
|
||||||
|
@ -632,61 +632,56 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::ReturnToDispatch, IR::LocationDescri
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
// Used for patches and linking
|
||||||
|
if (conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||||
|
if (conf.enable_cycle_counting) {
|
||||||
|
code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
||||||
|
patch_information[terminal.next].jg.push_back(code.getCurr());
|
||||||
|
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
|
EmitPatchJg(terminal.next, next_bb->entrypoint);
|
||||||
|
} else {
|
||||||
|
EmitPatchJg(terminal.next);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
code.cmp(dword[code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0);
|
||||||
|
patch_information[terminal.next].jz.push_back(code.getCurr());
|
||||||
|
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
|
EmitPatchJz(terminal.next, next_bb->entrypoint);
|
||||||
|
} else {
|
||||||
|
EmitPatchJz(terminal.next);
|
||||||
|
}
|
||||||
|
}
|
||||||
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
|
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
code.ReturnFromRunCode();
|
code.ForceReturnFromRunCode();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conf.enable_cycle_counting) {
|
|
||||||
code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
|
||||||
|
|
||||||
patch_information[terminal.next].jg.push_back(code.getCurr());
|
|
||||||
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
|
||||||
EmitPatchJg(terminal.next, next_bb->entrypoint);
|
|
||||||
} else {
|
|
||||||
EmitPatchJg(terminal.next);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
code.cmp(dword[r15 + offsetof(A64JitState, halt_reason)], 0);
|
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
|
||||||
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
patch_information[terminal.next].jz.push_back(code.getCurr());
|
code.ReturnFromRunCode();
|
||||||
if (const auto next_bb = GetBasicBlock(terminal.next)) {
|
|
||||||
EmitPatchJz(terminal.next, next_bb->entrypoint);
|
|
||||||
} else {
|
|
||||||
EmitPatchJz(terminal.next);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
|
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
|
||||||
code.ForceReturnFromRunCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
if (conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||||
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
|
patch_information[terminal.next].jmp.push_back(code.getCurr());
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
if (auto next_bb = GetBasicBlock(terminal.next)) {
|
||||||
code.ReturnFromRunCode();
|
EmitPatchJmp(terminal.next, next_bb->entrypoint);
|
||||||
return;
|
} else {
|
||||||
}
|
EmitPatchJmp(terminal.next);
|
||||||
|
}
|
||||||
patch_information[terminal.next].jmp.push_back(code.getCurr());
|
|
||||||
if (auto next_bb = GetBasicBlock(terminal.next)) {
|
|
||||||
EmitPatchJmp(terminal.next, next_bb->entrypoint);
|
|
||||||
} else {
|
} else {
|
||||||
EmitPatchJmp(terminal.next);
|
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
|
||||||
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
|
code.ReturnFromRunCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (!conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) {
|
if (conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) {
|
||||||
|
code.jmp(terminal_handler_pop_rsb_hint);
|
||||||
|
} else {
|
||||||
code.ReturnFromRunCode();
|
code.ReturnFromRunCode();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code.jmp(terminal_handler_pop_rsb_hint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
void A64EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||||
|
@ -723,7 +718,7 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescr
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
code.cmp(dword[r15 + offsetof(A64JitState, halt_reason)], 0);
|
code.cmp(dword[code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0);
|
||||||
code.jne(code.GetForceReturnFromRunCodeAddress());
|
code.jne(code.GetForceReturnFromRunCodeAddress());
|
||||||
EmitTerminal(terminal.else_, initial_location, is_single_step);
|
EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
@ -734,7 +729,7 @@ void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr
|
||||||
code.jg(target_code_ptr);
|
code.jg(target_code_ptr);
|
||||||
} else {
|
} else {
|
||||||
code.mov(rax, A64::LocationDescriptor{target_desc}.PC());
|
code.mov(rax, A64::LocationDescriptor{target_desc}.PC());
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
code.jg(code.GetReturnFromRunCodeAddress());
|
code.jg(code.GetReturnFromRunCodeAddress());
|
||||||
}
|
}
|
||||||
code.EnsurePatchLocationSize(patch_location, 23);
|
code.EnsurePatchLocationSize(patch_location, 23);
|
||||||
|
@ -746,7 +741,7 @@ void A64EmitX64::EmitPatchJz(const IR::LocationDescriptor& target_desc, CodePtr
|
||||||
code.jz(target_code_ptr);
|
code.jz(target_code_ptr);
|
||||||
} else {
|
} else {
|
||||||
code.mov(rax, A64::LocationDescriptor{target_desc}.PC());
|
code.mov(rax, A64::LocationDescriptor{target_desc}.PC());
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
code.jz(code.GetReturnFromRunCodeAddress());
|
code.jz(code.GetReturnFromRunCodeAddress());
|
||||||
}
|
}
|
||||||
code.EnsurePatchLocationSize(patch_location, 23);
|
code.EnsurePatchLocationSize(patch_location, 23);
|
||||||
|
@ -758,7 +753,7 @@ void A64EmitX64::EmitPatchJmp(const IR::LocationDescriptor& target_desc, CodePtr
|
||||||
code.jmp(target_code_ptr);
|
code.jmp(target_code_ptr);
|
||||||
} else {
|
} else {
|
||||||
code.mov(rax, A64::LocationDescriptor{target_desc}.PC());
|
code.mov(rax, A64::LocationDescriptor{target_desc}.PC());
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
code.jmp(code.GetReturnFromRunCodeAddress());
|
code.jmp(code.GetReturnFromRunCodeAddress());
|
||||||
}
|
}
|
||||||
code.EnsurePatchLocationSize(patch_location, 22);
|
code.EnsurePatchLocationSize(patch_location, 22);
|
||||||
|
|
|
@ -127,10 +127,10 @@ protected:
|
||||||
BlockRangeInformation<u64> block_ranges;
|
BlockRangeInformation<u64> block_ranges;
|
||||||
std::array<FastDispatchEntry, fast_dispatch_table_size> fast_dispatch_table;
|
std::array<FastDispatchEntry, fast_dispatch_table_size> fast_dispatch_table;
|
||||||
ankerl::unordered_dense::map<u64, FastmemPatchInfo> fastmem_patch_info;
|
ankerl::unordered_dense::map<u64, FastmemPatchInfo> fastmem_patch_info;
|
||||||
std::map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks;
|
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks;
|
||||||
std::map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
|
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks;
|
||||||
std::map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
|
ankerl::unordered_dense::map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks;
|
||||||
std::set<DoNotFastmemMarker> do_not_fastmem;
|
ankerl::unordered_dense::set<DoNotFastmemMarker> do_not_fastmem;
|
||||||
const void* terminal_handler_pop_rsb_hint = nullptr;
|
const void* terminal_handler_pop_rsb_hint = nullptr;
|
||||||
const void* terminal_handler_fast_dispatch_hint = nullptr;
|
const void* terminal_handler_fast_dispatch_hint = nullptr;
|
||||||
FastDispatchEntry& (*fast_dispatch_table_lookup)(u64) = nullptr;
|
FastDispatchEntry& (*fast_dispatch_table_lookup)(u64) = nullptr;
|
||||||
|
|
|
@ -324,7 +324,7 @@ void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
|
void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
|
||||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(0));
|
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(A64JitState, exclusive_state)], u8(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
@ -416,14 +416,14 @@ void A64EmitX64::EmitCheckMemoryAbort(A64EmitContext&, IR::Inst* inst, Xbyak::La
|
||||||
|
|
||||||
const A64::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}};
|
const A64::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}};
|
||||||
|
|
||||||
code.test(dword[r15 + offsetof(A64JitState, halt_reason)], static_cast<u32>(HaltReason::MemoryAbort));
|
code.test(dword[code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], static_cast<u32>(HaltReason::MemoryAbort));
|
||||||
if (end) {
|
if (end) {
|
||||||
code.jz(*end, code.T_NEAR);
|
code.jz(*end, code.T_NEAR);
|
||||||
} else {
|
} else {
|
||||||
code.jz(skip, code.T_NEAR);
|
code.jz(skip, code.T_NEAR);
|
||||||
}
|
}
|
||||||
code.mov(rax, current_location.PC());
|
code.mov(rax, current_location.PC());
|
||||||
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
code.ForceReturnFromRunCode();
|
code.ForceReturnFromRunCode();
|
||||||
code.L(skip);
|
code.L(skip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,16 +49,11 @@ void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size,
|
||||||
const size_t num_xmms = std::count_if(regs.begin(), regs.end(), HostLocIsXMM);
|
const size_t num_xmms = std::count_if(regs.begin(), regs.end(), HostLocIsXMM);
|
||||||
const FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
const FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
||||||
|
|
||||||
for (auto const gpr : regs) {
|
for (auto const gpr : regs)
|
||||||
if (HostLocIsGPR(gpr)) {
|
if (HostLocIsGPR(gpr))
|
||||||
code.push(HostLocToReg64(gpr));
|
code.push(HostLocToReg64(gpr));
|
||||||
}
|
if (frame_info.stack_subtraction != 0)
|
||||||
}
|
|
||||||
|
|
||||||
if (frame_info.stack_subtraction != 0) {
|
|
||||||
code.sub(rsp, u32(frame_info.stack_subtraction));
|
code.sub(rsp, u32(frame_info.stack_subtraction));
|
||||||
}
|
|
||||||
|
|
||||||
size_t xmm_offset = frame_info.xmm_offset;
|
size_t xmm_offset = frame_info.xmm_offset;
|
||||||
for (auto const xmm : regs) {
|
for (auto const xmm : regs) {
|
||||||
if (HostLocIsXMM(xmm)) {
|
if (HostLocIsXMM(xmm)) {
|
||||||
|
@ -80,27 +75,22 @@ void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size,
|
||||||
const size_t num_xmms = std::count_if(regs.begin(), regs.end(), HostLocIsXMM);
|
const size_t num_xmms = std::count_if(regs.begin(), regs.end(), HostLocIsXMM);
|
||||||
const FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
const FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
||||||
|
|
||||||
size_t xmm_offset = frame_info.xmm_offset;
|
size_t xmm_offset = frame_info.xmm_offset + (num_xmms * XMM_SIZE);
|
||||||
for (auto const xmm : regs) {
|
for (auto const xmm : mcl::iterator::reverse(regs)) {
|
||||||
if (HostLocIsXMM(xmm)) {
|
if (HostLocIsXMM(xmm)) {
|
||||||
|
xmm_offset -= XMM_SIZE;
|
||||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||||
code.vmovaps(HostLocToXmm(xmm), code.xword[rsp + xmm_offset]);
|
code.vmovaps(HostLocToXmm(xmm), code.xword[rsp + xmm_offset]);
|
||||||
} else {
|
} else {
|
||||||
code.movaps(HostLocToXmm(xmm), code.xword[rsp + xmm_offset]);
|
code.movaps(HostLocToXmm(xmm), code.xword[rsp + xmm_offset]);
|
||||||
}
|
}
|
||||||
xmm_offset += XMM_SIZE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (frame_info.stack_subtraction != 0)
|
||||||
if (frame_info.stack_subtraction != 0) {
|
|
||||||
code.add(rsp, u32(frame_info.stack_subtraction));
|
code.add(rsp, u32(frame_info.stack_subtraction));
|
||||||
}
|
for (auto const gpr : mcl::iterator::reverse(regs))
|
||||||
|
if (HostLocIsGPR(gpr))
|
||||||
for (auto const gpr : mcl::iterator::reverse(regs)) {
|
|
||||||
if (HostLocIsGPR(gpr)) {
|
|
||||||
code.pop(HostLocToReg64(gpr));
|
code.pop(HostLocToReg64(gpr));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ABI_PushCalleeSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size_t frame_size) {
|
void ABI_PushCalleeSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size_t frame_size) {
|
||||||
|
@ -119,6 +109,20 @@ void ABI_PopCallerSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size
|
||||||
ABI_PopRegistersAndAdjustStack(code, frame_size, ABI_ALL_CALLER_SAVE);
|
ABI_PopRegistersAndAdjustStack(code, frame_size, ABI_ALL_CALLER_SAVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows ABI registers are not in the same allocation algorithm as unix's
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||||
|
std::vector<HostLoc> regs;
|
||||||
|
std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
|
||||||
|
ABI_PushRegistersAndAdjustStack(code, 0, regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||||
|
std::vector<HostLoc> regs;
|
||||||
|
std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
|
||||||
|
ABI_PopRegistersAndAdjustStack(code, 0, regs);
|
||||||
|
}
|
||||||
|
#else
|
||||||
static consteval size_t ABI_AllCallerSaveSize() noexcept {
|
static consteval size_t ABI_AllCallerSaveSize() noexcept {
|
||||||
return ABI_ALL_CALLER_SAVE.max_size();
|
return ABI_ALL_CALLER_SAVE.max_size();
|
||||||
}
|
}
|
||||||
|
@ -166,24 +170,14 @@ alignas(64) static constinit std::array<HostLoc, ABI_AllCallerSaveSize() - 1> AB
|
||||||
};
|
};
|
||||||
|
|
||||||
void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||||
#ifdef _MSC_VER
|
|
||||||
std::vector<HostLoc> regs;
|
|
||||||
std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
|
|
||||||
ABI_PushRegistersAndAdjustStack(code, 0, regs);
|
|
||||||
#else
|
|
||||||
ASSUME(size_t(exception) < 32);
|
ASSUME(size_t(exception) < 32);
|
||||||
ABI_PushRegistersAndAdjustStack(code, 0, ABI_CALLER_SAVED_EXCEPT_TABLE[size_t(exception)]);
|
ABI_PushRegistersAndAdjustStack(code, 0, ABI_CALLER_SAVED_EXCEPT_TABLE[size_t(exception)]);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||||
#ifdef _MSC_VER
|
|
||||||
std::vector<HostLoc> regs;
|
|
||||||
std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
|
|
||||||
ABI_PopRegistersAndAdjustStack(code, 0, regs);
|
|
||||||
#else
|
|
||||||
ASSUME(size_t(exception) < 32);
|
ASSUME(size_t(exception) < 32);
|
||||||
ABI_PopRegistersAndAdjustStack(code, 0, ABI_CALLER_SAVED_EXCEPT_TABLE[size_t(exception)]);
|
ABI_PopRegistersAndAdjustStack(code, 0, ABI_CALLER_SAVED_EXCEPT_TABLE[size_t(exception)]);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::X64
|
} // namespace Dynarmic::Backend::X64
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace Dynarmic::Backend::X64 {
|
||||||
|
|
||||||
class BlockOfCode;
|
class BlockOfCode;
|
||||||
|
|
||||||
|
constexpr HostLoc ABI_JIT_PTR = HostLoc::R15;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
constexpr HostLoc ABI_RETURN = HostLoc::RAX;
|
constexpr HostLoc ABI_RETURN = HostLoc::RAX;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
namespace Dynarmic::Backend::X64 {
|
namespace Dynarmic::Backend::X64 {
|
||||||
|
|
||||||
|
const Xbyak::Reg64 BlockOfCode::ABI_JIT_PTR = HostLocToReg64(Dynarmic::Backend::X64::ABI_JIT_PTR);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const Xbyak::Reg64 BlockOfCode::ABI_RETURN = HostLocToReg64(Dynarmic::Backend::X64::ABI_RETURN);
|
const Xbyak::Reg64 BlockOfCode::ABI_RETURN = HostLocToReg64(Dynarmic::Backend::X64::ABI_RETURN);
|
||||||
const Xbyak::Reg64 BlockOfCode::ABI_PARAM1 = HostLocToReg64(Dynarmic::Backend::X64::ABI_PARAM1);
|
const Xbyak::Reg64 BlockOfCode::ABI_PARAM1 = HostLocToReg64(Dynarmic::Backend::X64::ABI_PARAM1);
|
||||||
|
@ -322,8 +323,8 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
// that the stack is appropriately aligned for CALLs.
|
// that the stack is appropriately aligned for CALLs.
|
||||||
ABI_PushCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout));
|
ABI_PushCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout));
|
||||||
|
|
||||||
mov(r15, ABI_PARAM1);
|
mov(ABI_JIT_PTR, ABI_PARAM1);
|
||||||
mov(rbx, ABI_PARAM2); // save temporarily in non-volatile register
|
mov(rbx, ABI_PARAM2); // save temporarily in non-volatile register
|
||||||
|
|
||||||
if (cb.enable_cycle_counting) {
|
if (cb.enable_cycle_counting) {
|
||||||
cb.GetTicksRemaining->EmitCall(*this);
|
cb.GetTicksRemaining->EmitCall(*this);
|
||||||
|
@ -331,9 +332,11 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// r14 = page table
|
||||||
|
// r13 = fastmem pointer
|
||||||
rcp(*this);
|
rcp(*this);
|
||||||
|
|
||||||
cmp(dword[r15 + jsi.offsetof_halt_reason], 0);
|
cmp(dword[ABI_JIT_PTR + jsi.offsetof_halt_reason], 0);
|
||||||
jne(return_to_caller_mxcsr_already_exited, T_NEAR);
|
jne(return_to_caller_mxcsr_already_exited, T_NEAR);
|
||||||
|
|
||||||
SwitchMxcsrOnEntry();
|
SwitchMxcsrOnEntry();
|
||||||
|
@ -344,7 +347,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
|
|
||||||
ABI_PushCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout));
|
ABI_PushCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout));
|
||||||
|
|
||||||
mov(r15, ABI_PARAM1);
|
mov(ABI_JIT_PTR, ABI_PARAM1);
|
||||||
|
|
||||||
if (cb.enable_cycle_counting) {
|
if (cb.enable_cycle_counting) {
|
||||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], 1);
|
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], 1);
|
||||||
|
@ -353,10 +356,10 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
|
|
||||||
rcp(*this);
|
rcp(*this);
|
||||||
|
|
||||||
cmp(dword[r15 + jsi.offsetof_halt_reason], 0);
|
cmp(dword[ABI_JIT_PTR + jsi.offsetof_halt_reason], 0);
|
||||||
jne(return_to_caller_mxcsr_already_exited, T_NEAR);
|
jne(return_to_caller_mxcsr_already_exited, T_NEAR);
|
||||||
lock();
|
lock();
|
||||||
or_(dword[r15 + jsi.offsetof_halt_reason], static_cast<u32>(HaltReason::Step));
|
or_(dword[ABI_JIT_PTR + jsi.offsetof_halt_reason], static_cast<u32>(HaltReason::Step));
|
||||||
|
|
||||||
SwitchMxcsrOnEntry();
|
SwitchMxcsrOnEntry();
|
||||||
jmp(ABI_PARAM2);
|
jmp(ABI_PARAM2);
|
||||||
|
@ -366,7 +369,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
align();
|
align();
|
||||||
return_from_run_code[0] = getCurr<const void*>();
|
return_from_run_code[0] = getCurr<const void*>();
|
||||||
|
|
||||||
cmp(dword[r15 + jsi.offsetof_halt_reason], 0);
|
cmp(dword[ABI_JIT_PTR + jsi.offsetof_halt_reason], 0);
|
||||||
jne(return_to_caller);
|
jne(return_to_caller);
|
||||||
if (cb.enable_cycle_counting) {
|
if (cb.enable_cycle_counting) {
|
||||||
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
||||||
|
@ -378,7 +381,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
align();
|
align();
|
||||||
return_from_run_code[MXCSR_ALREADY_EXITED] = getCurr<const void*>();
|
return_from_run_code[MXCSR_ALREADY_EXITED] = getCurr<const void*>();
|
||||||
|
|
||||||
cmp(dword[r15 + jsi.offsetof_halt_reason], 0);
|
cmp(dword[ABI_JIT_PTR + jsi.offsetof_halt_reason], 0);
|
||||||
jne(return_to_caller_mxcsr_already_exited);
|
jne(return_to_caller_mxcsr_already_exited);
|
||||||
if (cb.enable_cycle_counting) {
|
if (cb.enable_cycle_counting) {
|
||||||
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
||||||
|
@ -407,7 +410,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
|
|
||||||
xor_(eax, eax);
|
xor_(eax, eax);
|
||||||
lock();
|
lock();
|
||||||
xchg(dword[r15 + jsi.offsetof_halt_reason], eax);
|
xchg(dword[ABI_JIT_PTR + jsi.offsetof_halt_reason], eax);
|
||||||
|
|
||||||
ABI_PopCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout));
|
ABI_PopCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout));
|
||||||
ret();
|
ret();
|
||||||
|
@ -417,22 +420,22 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
||||||
|
|
||||||
void BlockOfCode::SwitchMxcsrOnEntry() {
|
void BlockOfCode::SwitchMxcsrOnEntry() {
|
||||||
stmxcsr(dword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, save_host_MXCSR)]);
|
stmxcsr(dword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, save_host_MXCSR)]);
|
||||||
ldmxcsr(dword[r15 + jsi.offsetof_guest_MXCSR]);
|
ldmxcsr(dword[ABI_JIT_PTR + jsi.offsetof_guest_MXCSR]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::SwitchMxcsrOnExit() {
|
void BlockOfCode::SwitchMxcsrOnExit() {
|
||||||
stmxcsr(dword[r15 + jsi.offsetof_guest_MXCSR]);
|
stmxcsr(dword[ABI_JIT_PTR + jsi.offsetof_guest_MXCSR]);
|
||||||
ldmxcsr(dword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, save_host_MXCSR)]);
|
ldmxcsr(dword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, save_host_MXCSR)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::EnterStandardASIMD() {
|
void BlockOfCode::EnterStandardASIMD() {
|
||||||
stmxcsr(dword[r15 + jsi.offsetof_guest_MXCSR]);
|
stmxcsr(dword[ABI_JIT_PTR + jsi.offsetof_guest_MXCSR]);
|
||||||
ldmxcsr(dword[r15 + jsi.offsetof_asimd_MXCSR]);
|
ldmxcsr(dword[ABI_JIT_PTR + jsi.offsetof_asimd_MXCSR]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::LeaveStandardASIMD() {
|
void BlockOfCode::LeaveStandardASIMD() {
|
||||||
stmxcsr(dword[r15 + jsi.offsetof_asimd_MXCSR]);
|
stmxcsr(dword[ABI_JIT_PTR + jsi.offsetof_asimd_MXCSR]);
|
||||||
ldmxcsr(dword[r15 + jsi.offsetof_guest_MXCSR]);
|
ldmxcsr(dword[ABI_JIT_PTR + jsi.offsetof_guest_MXCSR]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::UpdateTicks() {
|
void BlockOfCode::UpdateTicks() {
|
||||||
|
|
|
@ -155,6 +155,7 @@ public:
|
||||||
void SetCodePtr(CodePtr code_ptr);
|
void SetCodePtr(CodePtr code_ptr);
|
||||||
void EnsurePatchLocationSize(CodePtr begin, size_t size);
|
void EnsurePatchLocationSize(CodePtr begin, size_t size);
|
||||||
|
|
||||||
|
static const Xbyak::Reg64 ABI_JIT_PTR;
|
||||||
// ABI registers
|
// ABI registers
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static const Xbyak::Reg64 ABI_RETURN;
|
static const Xbyak::Reg64 ABI_RETURN;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue