mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-18 06:10:35 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 809a61f600 | |||
| c7b24bad1a | |||
| 4244a6418d | |||
| 26044e18e1 | |||
| 04d08f0cf1 | |||
| 2bf037c7bb | |||
| f6668ef01b | |||
| 20a775ed08 | |||
| 3379556d89 | |||
| 8e687924b8 | |||
| d3e8c5e977 | |||
| 95e67e6533 | |||
| bdd1785b04 | |||
| f9e5ac191e | |||
| f3a674c064 | |||
| 4aa35bc835 | |||
| 894f2544a0 | |||
| ceffda663c | |||
| 27e7fa10d9 | |||
| d3bd4f42cf | |||
| 5a8fb951dd | |||
| 27942142cf | |||
| db647f9a40 | |||
| 35aeb0cf55 | |||
| 3423d3c6a5 | |||
| 607bc18ad4 | |||
| 2000fdfb7b | |||
| dc95cd09ee |
@@ -19,7 +19,6 @@ log.txt
|
||||
# Generated source files
|
||||
src/common/scm_rev.cpp
|
||||
dist/english_plurals/generated_en.ts
|
||||
*-toolchain.cmake
|
||||
|
||||
# Project/editor files
|
||||
*.swp
|
||||
|
||||
+6
-2
@@ -81,6 +81,9 @@ cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSV
|
||||
option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF)
|
||||
option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
|
||||
|
||||
# Lossless Scaling frame generation. Only Android.
|
||||
cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF)
|
||||
|
||||
# non-linux bundled qt are static
|
||||
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
|
||||
set(YUZU_STATIC_BUILD ON)
|
||||
@@ -339,11 +342,12 @@ if (CXX_GCC OR CXX_CLANG)
|
||||
endif()
|
||||
elseif(ARCHITECTURE_arm64)
|
||||
# See https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
|
||||
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, armv9, native")
|
||||
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, optimized, armv9, native")
|
||||
set(mtune generic)
|
||||
|
||||
if (${YUZU_BUILD_PRESET} STREQUAL "generic")
|
||||
set(march armv8-a)
|
||||
elseif (${YUZU_BUILD_PRESET} STREQUAL "optimized")
|
||||
set(march armv8.2-a+lse+rcpc)
|
||||
elseif (${YUZU_BUILD_PRESET} STREQUAL "armv9")
|
||||
set(march armv9-a)
|
||||
endif()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set(CROSS_TARGET "powerpc64le" CACHE STRING "Cross-compilation target (aarch64, powerpc64le, riscv64, etc)")
|
||||
|
||||
set(CMAKE_SYSROOT /usr/${CROSS_TARGET}-unknown-linux-gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-g++)
|
||||
|
||||
# search programs in the host environment
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
# search headers and libraries in the target environment
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/${CROSS_TARGET}-unknown-linux-gnu)
|
||||
+1
-137
@@ -2,122 +2,7 @@
|
||||
|
||||
General guide for cross compiling.
|
||||
|
||||
## Gentoo
|
||||
|
||||
Gentoo's cross-compilation setup is relatively easy, provided you're already familiar with portage.
|
||||
|
||||
### Crossdev
|
||||
|
||||
First, emerge crossdev via `sudo emerge -a sys-devel/crossdev`.
|
||||
|
||||
Now, set up the environment depending on the target architecture; e.g.
|
||||
|
||||
```sh
|
||||
sudo crossdev powerpc64le
|
||||
sudo crossdev aarch64
|
||||
```
|
||||
|
||||
### QEMU
|
||||
|
||||
Installing a qemu user setup is recommended for testing. To do so, you will need the relevant USE flags:
|
||||
|
||||
```sh
|
||||
app-emulation/qemu static-user qemu_user_targets_ppc64le qemu_user_targets_aarch64
|
||||
```
|
||||
|
||||
Note that to use cross-emerged libraries, you will need to tell qemu where the sysroot is. You can do this with an alias:
|
||||
|
||||
```sh
|
||||
alias qemu-ppc64le="qemu-ppc64le -L /usr/powerpc64le-unknown-linux-gnu"
|
||||
alias qemu-aarch64="qemu-aarch64 -L /usr/aarch64-unknown-linux-gnu"
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
Some packages have broken USE flags on other architectures; you'll also need to set up python targets. In `/usr/<target>-unknown-linux-gnu/etc/portage/package.use`:
|
||||
|
||||
```sh
|
||||
>=net-misc/curl-8.16.0-r1 ssl
|
||||
|
||||
*/* PYTHON_TARGETS: python3_13 PYTHON_SINGLE_TARGET: python3_13
|
||||
*/* pam
|
||||
|
||||
sys-apps/util-linux pam su
|
||||
app-shells/bash -readline
|
||||
>=dev-libs/libpcre2-10.47 unicode
|
||||
>=x11-libs/libxkbcommon-1.12.3 X
|
||||
>=sys-libs/zlib-1.3.1-r1 minizip
|
||||
>=app-alternatives/gpg-1-r3 ssl
|
||||
>=app-crypt/gnupg-2.5.13-r2 ssl
|
||||
|
||||
dev-libs/* -introspection
|
||||
media-libs/harfbuzz -introspection
|
||||
dev-libs/quazip -qt5 qt6
|
||||
```
|
||||
|
||||
Dependencies should be about the same [as normal Gentoo](./Deps.md), but removing gamemode and renderdoc is recommended. Keep in mind that when emerging, you want to use `emerge-<target>-unknown-linux-gnu`, e.g. `emerge-powerpc64le-unknown-linux-gnu`.
|
||||
|
||||
Enable GURU in the cross environment (as root):
|
||||
|
||||
```sh
|
||||
mkdir -p /usr/powerpc64le-unknown-linux-gnu/etc/portage/repos.conf
|
||||
cat << EOF > /usr/powerpc64le-unknown-linux-gnu/etc/portage/repos.conf/guru.conf
|
||||
[guru]
|
||||
location = /var/db/repos/guru
|
||||
auto-sync = no
|
||||
priority = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
Now emerge your dependencies:
|
||||
|
||||
```sh
|
||||
sudo emerge-powerpc64le-unknown-linux-gnu -aU app-arch/lz4 app-arch/zstd app-arch/unzip \
|
||||
dev-libs/libfmt dev-libs/libusb dev-libs/mcl dev-libs/sirit dev-libs/oaknut \
|
||||
dev-libs/unordered_dense dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
|
||||
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
||||
dev-util/vulkan-utility-libraries dev-util/glslang \
|
||||
media-libs/libva media-libs/opus media-video/ffmpeg \
|
||||
media-libs/VulkanMemoryAllocator media-libs/libsdl2 media-libs/cubeb \
|
||||
net-libs/enet net-libs/mbedtls \
|
||||
sys-libs/zlib \
|
||||
dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt dev-cpp/catch \
|
||||
net-wireless/wireless-tools \
|
||||
dev-qt/qtbase:6 dev-libs/quazip \
|
||||
virtual/pkgconfig
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
A toolchain is provided in `CMakeModules/GentooCross.cmake`. To use it:
|
||||
|
||||
```sh
|
||||
cmake -S . -B build/ppc64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/GentooCross.cmake -G Ninja -DCROSS_TARGET=powerpc64le -DENABLE_OPENGL=OFF
|
||||
```
|
||||
|
||||
Now build as normal:
|
||||
|
||||
```sh
|
||||
cmake --build build/ppc64 -j$(nproc)
|
||||
```
|
||||
|
||||
### Alternatively
|
||||
|
||||
Only emerge the absolute necessities:
|
||||
|
||||
```sh
|
||||
sudo emerge-powerpc64le-unknown-linux-gnu -aU media-video/ffmpeg media-libs/libsdl2 dev-qt/qtbase:6
|
||||
```
|
||||
|
||||
Then set `YUZU_USE_CPM=ON`:
|
||||
|
||||
```sh
|
||||
cmake -S . -B build/ppc64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/GentooCross.cmake -G Ninja -DCROSS_TARGET=powerpc64le -DENABLE_OPENGL=OFF -DYUZU_USE_CPM=ON
|
||||
```
|
||||
|
||||
## ARM64
|
||||
|
||||
### Debian ARM64
|
||||
## Debian ARM64
|
||||
|
||||
A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main.
|
||||
|
||||
@@ -203,24 +88,3 @@ And finally, run the compiled executable with QEMU!
|
||||
```sh
|
||||
qemu-aarch64 build/aarch64/bin/eden
|
||||
```
|
||||
|
||||
## PowerPC
|
||||
|
||||
This is a guide for FreeBSD users mainly.
|
||||
|
||||
Now you got a PowerPC sysroot - quickly decompress it somewhere, say `/home/user/opt/powerpc64le`. Create a toolchain file, for example `powerpc64le-toolchain.cmake`; always [consult the manual](https://man.freebsd.org/cgi/man.cgi?query=cmake-toolchains&sektion=7&manpath=FreeBSD+13.2-RELEASE+and+Ports).
|
||||
|
||||
There is a script to automatically do all of this under `./tools/setup-cross-sysroot.sh`.
|
||||
|
||||
Remember to add `-mabi=elfv1` to `CFLAGS`/`CXXFLAGS` otherwise the program will crash.
|
||||
|
||||
Specify:
|
||||
|
||||
- `YUZU_USE_CPM`: Set this to `ON` so packages can be found and built if your sysroot doesn't have them.
|
||||
- `YUZU_USE_EXTERNAL_FFMPEG`: Set this to `ON` as well.
|
||||
|
||||
Then run using a program such as QEMU to emulate userland syscalls:
|
||||
|
||||
```sh
|
||||
cmake --build build-ppc64-pc-freebsd -t dynarmic_tests -- -j8 && qemu-ppc64-static -L $HOME/opt/ppc64-freebsd/sysroot ./build-ppc64-pc-freebsd/bin/dynarmic_tests
|
||||
```
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# PowerPC 64 backend
|
||||
|
||||
The ppc64 backend currently only supports the little endian variant, with big endian support being experimental for the time being. Additionally only A32 is supported (for now).
|
||||
|
||||
- Flag handling: Flags are emulated via software, while there may be some funny tricks with the CTR, I'd rather not bother - plus it's widely known that those instructions are not nice on real metal - so I would rather take the i-cache cost.
|
||||
- 128-bit atomics: No 128-bit atomic support is provided, this may cause wrong or erroneous execution in some contexts.
|
||||
|
||||
To handle endianess differences all 16/32/64-bit loads and stores to the "emulated memory" are byteswapped beforehand.
|
||||
@@ -35,7 +35,6 @@ There are no plans to support v1 or v2.
|
||||
|
||||
* x86-64
|
||||
* AArch64
|
||||
* PowerPC 64 (POWER 4 and up)
|
||||
|
||||
There are no plans to support any 32-bit architecture.
|
||||
|
||||
@@ -51,7 +50,6 @@ Documentation
|
||||
-------------
|
||||
|
||||
Design documentation can be found at [./Design.md](./Design.md).
|
||||
PowerPC design documentation can be found at [./PowerPC](./PowerPC.md).
|
||||
|
||||
|
||||
Usage Example
|
||||
|
||||
Vendored
-5
@@ -254,11 +254,6 @@ endif()
|
||||
# TZDB (Time Zone Database)
|
||||
add_subdirectory(nx_tzdb)
|
||||
|
||||
# PowerPC emitter
|
||||
if (ARCHITECTURE_powerpc64)
|
||||
add_subdirectory(powah)
|
||||
endif()
|
||||
|
||||
add_library(tz tz/tz/tz.cpp)
|
||||
target_include_directories(tz PUBLIC ./tz)
|
||||
|
||||
|
||||
+2
-2
@@ -166,7 +166,7 @@ detect_architecture_symbols(
|
||||
"_M_MRX000")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH powerpc64
|
||||
ARCH ppc64
|
||||
SYMBOLS
|
||||
"__ppc64__"
|
||||
"__powerpc64__"
|
||||
@@ -174,7 +174,7 @@ detect_architecture_symbols(
|
||||
"_M_PPC64")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH powerpc
|
||||
ARCH ppc
|
||||
SYMBOLS
|
||||
"__ppc__"
|
||||
"__ppc"
|
||||
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
add_library(powah INTERFACE)
|
||||
target_include_directories(powah INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_options(powah INTERFACE -Wno-unused-parameter)
|
||||
|
||||
add_executable(powah_tests tests.cpp)
|
||||
create_target_directory_groups(powah_tests)
|
||||
target_link_libraries(powah_tests PRIVATE Catch2::Catch2WithMain)
|
||||
Vendored
-145
@@ -1,145 +0,0 @@
|
||||
vabsdub,VX,04,1027
|
||||
vabsduh,VX,04,1091
|
||||
vabsduw,VX,04,1155
|
||||
vaddcuw,VX,04,384
|
||||
vaddfp,VX,04,10
|
||||
vaddsbs,VX,04,768
|
||||
vaddshs,VX,04,832
|
||||
vaddsws,VX,04,896
|
||||
vaddubm,VX,04,0
|
||||
vaddubs,VX,04,512
|
||||
vadduhm,VX,04,64
|
||||
vadduhs,VX,04,576
|
||||
vadduwm,VX,04,128
|
||||
vadduws,VX,04,640
|
||||
vand,VX,04,1028
|
||||
vandc,VX,04,1092
|
||||
vavgsb,VX,04,1282
|
||||
vavgsh,VX,04,1346
|
||||
vavgsw,VX,04,1410
|
||||
vavgub,VX,04,1026
|
||||
vavguh,VX,04,1090
|
||||
vavguw,VX,04,1154
|
||||
vcfsx,VDU,04842
|
||||
vcfux,VDU,04778
|
||||
vcmpbfp,VXR,04,966
|
||||
vcmpeqfp,VXR,04,198
|
||||
vcmpequb,VXR,04,6
|
||||
vcmpequh,VXR,04,70
|
||||
vcmpequw,VXR,04,134
|
||||
vcmpgefp,VXR,04,454
|
||||
vcmpgtfp,VXR,04,710
|
||||
vcmpgtsb,VXR,04,774
|
||||
vcmpgtsh,VXR,04,838
|
||||
vcmpgtsw,VXR,04,902
|
||||
vcmpgtub,VXR,04,518
|
||||
vcmpgtuh,VXR,04,582
|
||||
vcmpgtuw,VXR,04,646
|
||||
vctsxs,VDU,04970
|
||||
vctuxs,VDU,04906
|
||||
vexptefp,VY,04,394
|
||||
vlogefp,VY,04,458
|
||||
vmaddfp,VXC,46
|
||||
vmaxfp,VX,04,1034
|
||||
vmaxsb,VX,04,258
|
||||
vmaxsh,VX,04,322
|
||||
vmaxsw,VX,04,386
|
||||
vmaxub,VX,04,2
|
||||
vmaxuh,VX,04,66
|
||||
vmaxuw,VX,04,130
|
||||
vmhaddshs,VXC,32
|
||||
vmhraddshs,VXC,33
|
||||
vminfp,VX,04,1098
|
||||
vminsb,VX,04,770
|
||||
vminsh,VX,04,834
|
||||
vminsw,VX,04,898
|
||||
vminub,VX,04,514
|
||||
vminuh,VX,04,578
|
||||
vminuw,VX,04,642
|
||||
vmladduhm,VXC,34
|
||||
vmrghb,VX,04,12
|
||||
vmrghh,VX,04,76
|
||||
vmrghw,VX,04,140
|
||||
vmrglb,VX,04,268
|
||||
vmrglh,VX,04,332
|
||||
vmrglw,VX,04,396
|
||||
vmsummbm,VXC,37
|
||||
vmsumshm,VXC,40
|
||||
vmsumshs,VXC,41
|
||||
vmsumubm,VXC,36
|
||||
vmsumuhm,VXC,38
|
||||
vmsumuhs,VXC,39
|
||||
vmulesb,VX,04,776
|
||||
vmulesh,VX,04,840
|
||||
vmuleub,VX,04,520
|
||||
vmuleuh,VX,04,584
|
||||
vmulosb,VX,04,264
|
||||
vmulosh,VX,04,328
|
||||
vmuloub,VX,04,8
|
||||
vmulouh,VX,04,72
|
||||
vnmsubfp,VXC,47
|
||||
vnor,VX,04,1284
|
||||
vor,VX,04,1156
|
||||
vperm,VXC,43
|
||||
vpkpx,VX,04,782
|
||||
vpkshss,VX,04,398
|
||||
vpkshus,VX,04,270
|
||||
vpkswss,VX,04,462
|
||||
vpkswus,VX,04,334
|
||||
vpkuhum,VX,04,14
|
||||
vpkuhus,VX,04,142
|
||||
vpkuwum,VX,04,78
|
||||
vpkuwus,VX,04,206
|
||||
vrefp,VY,04,266
|
||||
vrfim,VY,04,714
|
||||
vrfin,VY,04,522
|
||||
vrfip,VY,04,650
|
||||
vrfiz,VY,04,586
|
||||
vrlb,VX,04,4
|
||||
vrlh,VX,04,68
|
||||
vrlw,VX,04,132
|
||||
vrsqrtefp,VY,04,330
|
||||
vsel,VXC,42
|
||||
vsl,VX,04,452
|
||||
vslb,VX,04,260
|
||||
vsldoi,VX,04,/ SH 44
|
||||
vslh,VX,04,324
|
||||
vslo,VX,04,1036
|
||||
vslw,VX,04,388
|
||||
vspltb,VU,04,524
|
||||
vsplth,VU,04,588
|
||||
vspltisb,VS,04,780
|
||||
vspltish,VS,04,844
|
||||
vspltisw,VS,04,908
|
||||
vspltw,VSU,04,652
|
||||
vsr,VX,04,708
|
||||
vsrab,VX,04,772
|
||||
vsrah,VX,04,836
|
||||
vsraw,VX,04,900
|
||||
vsrb,VX,04,516
|
||||
vsrh,VX,04,580
|
||||
vsro,VX,04,1100
|
||||
vsrw,VX,04,644
|
||||
vsubcuw,VX,04,1408
|
||||
vsubfp,VX,04,74
|
||||
vsubsbs,VX,04,1792
|
||||
vsubshs,VX,04,1856
|
||||
vsubsws,VX,04,1920
|
||||
vsububm,VX,04,1024
|
||||
vsububs,VX,04,1536
|
||||
vsubuhm,VX,04,1088
|
||||
vsubuhs,VX,04,1600
|
||||
vsubuwm,VX,04,1152
|
||||
vsubuws,VX,04,1664
|
||||
vsumsws,VX,04,1928
|
||||
vsum2sws,VX,04,1672
|
||||
vsum4sbs,VX,04,1800
|
||||
vsum4shs,VX,04,1608
|
||||
vsum4ubs,VX,04,1544
|
||||
vupkhpx,VY,04,846
|
||||
vupkhsb,VY,04,526
|
||||
vupkhsh,VY,04,590
|
||||
vupklpx,VY,04,974
|
||||
vupklsb,VY,04,654
|
||||
vupklsh,VY,04,718
|
||||
vxor,VX,04,122
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
$CC data2code.c -o data2code && ./data2code data.txt >powah_gen_base.hpp
|
||||
$CC --target=powerpc64le test.S -c -o test && objdump -SC test
|
||||
Vendored
-222
@@ -1,222 +0,0 @@
|
||||
add[o][.],Add,XO,31,266
|
||||
addc[o][.],Add Carrying,XO,31,10
|
||||
adde[o][.],Add Extended,XO,31,138
|
||||
addi,Add Immediate,D,14,
|
||||
addic,Add Immediate Carrying,D,12,
|
||||
addic.,Add Immediate Carrying and Record,D,13,
|
||||
addis,Add Immediate Shifted,D,15,
|
||||
addme[o][.],Add to Minus One Extended,XO,31,234
|
||||
addze[o][.],Add to Zero Extended,XO,31,202
|
||||
and[.],AND,X,31,28
|
||||
andc[.],AND with Complement,X,31,60
|
||||
andi.,AND Immediate,D,28,
|
||||
andis.,AND Immediate Shifted,D,29,
|
||||
b[l][a],Branch,I,18,
|
||||
bc[l][a],Branch Conditional,B,16,
|
||||
bcctr[l],Branch Conditional to Count Register,XL,19,528
|
||||
bclr[l],Branch Conditional Link Register,XL,19,16
|
||||
cmp,Compare,X,31,0
|
||||
cmpi,Compare Immediate,D,11,
|
||||
cmpl,Compare Logical,X,31,32
|
||||
cmpli,Compare Logical Immediate,D,10,
|
||||
cntlzd,Count Leading Zeros Doubleword,X,31,58
|
||||
cntlzw[.],Count Leading Zeros Word,X,31,26
|
||||
crand,Condition Register AND,XL,19,257
|
||||
crandc,Condition Register AND with Complement,XL,19,129
|
||||
creqv,Condition Register Equivalent,XL,19,289
|
||||
crnand,Condition Register NAND,XL,19,225
|
||||
crnor,Condition Register NOR,XL,19,33
|
||||
cror,Condition Register OR,XL,19,449
|
||||
crorc,Condition Register OR with Complement,XL,19,417
|
||||
crxor,Condition Register XOR,XL,19,193
|
||||
dcbf,Data Cache Block Flush,X,31,86
|
||||
dcbi,Data Cache Block Invalidate,X,31,470
|
||||
dcbst,Data Cache Block Store,X,31,54
|
||||
dcbt,Data Cache Block Touch,X,31,278
|
||||
dcbtst,Data Cache Block Touch for Store,X,31,246
|
||||
dcbz,Data Cache Block Set to Zero,X,31,1014
|
||||
divd,Divide Doubleword,XO,31,489
|
||||
divdu,Divide Doubleword Unsigned,XO,31,457
|
||||
divw[o][.],Divide Word,XO,31,491
|
||||
divwu[o][.],Divide Word Unsigned,XO,31,459
|
||||
eciwx,External Control in Word Indexed (opt.),X,31,310
|
||||
ecowx,External Control out Word Indexed (opt.),X,31,438
|
||||
eieio,Enforce In-order Execution of I/O,X,31,854
|
||||
eqv[.],Equivalent,X,31,284
|
||||
extsb[.],Extend Sign Byte,X,31,954
|
||||
extsh[.],Extend Sign Halfword,XO,31,922
|
||||
extsw,Extend Sign Word,X,31,986
|
||||
fabs[.],Floating Absolute Value,X,63,264
|
||||
fadd[.],Floating Add,A,63,21
|
||||
fadds[.],Floating Add Single,A,59,21
|
||||
fcfid,Floating Convert from Integer Doubleword,X,63,846
|
||||
fcmpo,Floating Compare Ordered,X,63,32
|
||||
fcmpu,Floating Compare Unordered,XL,63,0
|
||||
fctid,Floating Convert to Integer Doubleword,X,63,814
|
||||
fctidz,Floating Convert to Integer Doubleword with Round Toward Zero,X,63,815
|
||||
fctiw[.],Floating Convert to Integer Word,X,63,14
|
||||
fctiwz[.],Floating Convert to Integer Word with Round to Zero,XL,63,15
|
||||
fdiv[.],Floating Divide,A,63,18
|
||||
fdivs[.],Floating Divide Single,A,59,18
|
||||
fmadd[.],Floating Multiply-Add,A,63,29
|
||||
fmadds[.],Floating Multiply-Add Single,A,59,29
|
||||
fmr[.],Floating Move Register,X,63,72
|
||||
fmsub[.],Floating Multiply-Subtract,A,63,28
|
||||
fmsubs[.],Floating Multiply-Subtract Single,A,59,28
|
||||
fmul[.],Floating Multiply,A,63,25
|
||||
fmuls[.],Floating Multiply Single,A,59,25
|
||||
fnabs[.],Floating Negative Absolute Value,X,63,136
|
||||
fneg[.],Floating Negate,X,63,40
|
||||
fnmadd[.],Floating Negative Multiply-Add,A,63,31
|
||||
fnmadds[.],Floating Negative Multiply-Add Single,A,59,31
|
||||
fnmsub[.],Floating Negative Multiply-Subtract,A,63,30
|
||||
fnmsubs[.],Floating Negative Multiply-Subtract Single,A,59,30
|
||||
fres[.],Floating Reciprocal Estimate Single (optional),A,59,24
|
||||
frsp[.],Floating Round to Single Precision,X,63,12
|
||||
frsqrte[.],Floating Reciprocal Square Root Estimate (optional),A,63,26
|
||||
fsel[.],Floating-Point Select (optional),A,63,23
|
||||
fsub[.],Floating Subtract,A,63,20
|
||||
fsubs[.],Floating Subtract Single,A,59,20
|
||||
icbi,Instruction Cache Block Invalidate,X,31,982
|
||||
isync,Instruction Synchronize,X,19,150
|
||||
lbz,Load Byte and Zero,D,34,
|
||||
lbzu,Load Byte and Zero with Update,D,35,
|
||||
lbzux,Load Byte and Zero with Update Indexed,X,31,119
|
||||
lbzx,Load Byte and Zero Indexed,X,31,87
|
||||
ld,Load Doubleword,DS,58,0
|
||||
ldarx,Load Doubleword and Reserve Indexed,X,31,84
|
||||
ldu,Load Doubleword with Update,DS,58,1
|
||||
ldux,Load Doubleword with Update Indexed,X,31,53
|
||||
ldx,Load Doubleword Indexed,X,31,21
|
||||
lfd,Load Floating-Point Double,D,50,
|
||||
lfdu,Load Floating-Point Double with Update,D,51,
|
||||
lfdux,Load Floating-Point Double with Update Indexed,X,31,631
|
||||
lfdx,Load Floating-Point Double Indexed,X,31,599
|
||||
lfs,Load Floating-Point Single,D,48,
|
||||
lfsu,Load Floating-Point Single with Update,D,49,
|
||||
lfsux,Load Floating-Point Single with Update Indexed,X,31,567
|
||||
lfsx,Load Floating-Point Single Indexed,X,31,535
|
||||
lha,Load Half Algebraic,D,42,
|
||||
lhau,Load Half Algebraic with Update,D,43,
|
||||
lhaux,Load Half Algebraic with Update Indexed,X,31,375
|
||||
lhax,Load Half Algebraic Indexed,X,31,343
|
||||
lhbrx,Load Half Byte-Reversed Indexed,X,31,790
|
||||
lhz,Load Half and Zero,D,40,
|
||||
lhzu,Load Half and Zero with Update,D,41,
|
||||
lhzux,Load Half and Zero with Update Indexed,X,31,331
|
||||
lhzx,Load Half and Zero Indexed,X,31,279
|
||||
lmw,Load Multiple Word,D,46,
|
||||
lswi,Load String Word Immediate,X,31,597
|
||||
lswx,Load String Word Indexed,X,31,533
|
||||
lwa,Load Word Algebraic,DS,58,2
|
||||
lwarx,Load Word and Reserve Indexed,X,31,20
|
||||
lwaux,Load Word Algebraic with Update Indexed,X,31,373
|
||||
lwax,Load Word Algebraic Indexed,X,31,341
|
||||
lwbrx,Load Word Byte-Reversed Indexed,X,31,534
|
||||
lwz,Load Word and Zero,D,32,
|
||||
lwzu,Load Word with Zero Update,D,33,
|
||||
lwzux,Load Word and Zero with Update Indexed,X,31,55
|
||||
lwzx,Load Word and Zero Indexed,X,31,23
|
||||
mcrf,Move Condition Register Field,XL,19,0
|
||||
mcrfs,Move to Condition Register from FPSCR,X,63,64
|
||||
mcrxr,Move to Condition Register from XER,X,31,512
|
||||
mfcr,Move from Condition Register,X,31,19
|
||||
mffs[.],Move from FPSCR,X,63,583
|
||||
mfmsr,Move from Machine State Register,X,31,83
|
||||
mfspr,Move from Special-Purpose Register,X,31,339
|
||||
mfsr,Move from Segment Register,X,31,595
|
||||
mfsrin,Move from Segment Register Indirect,X,31,659
|
||||
mtcrf,Move to Condition Register Fields,XFX,31,144
|
||||
mtfsb0[.],Move to FPSCR Bit 0,X,63,70
|
||||
mtfsb1[.],Move to FPSCR Bit 1,X,63,38
|
||||
mtfsf[.],Move to FPSCR Fields,XFL,63,711
|
||||
mtfsfi[.],Move to FPSCR Field Immediate,X,63,134
|
||||
mtmsr,Move to Machine State Register,X,31,146
|
||||
mtspr,Move to Special-Purpose Register,X,31,467
|
||||
mtsr,Move to Segment Register,X,31,210
|
||||
mtsrin,Move to Segment Register Indirect,X,31,242
|
||||
mulhd,Multiply High Doubleword,XO,31,73
|
||||
mulhdu,Multiply High Doubleword Unsigned,XO,31,9
|
||||
mulhw[.],Multiply High Word,XO,31,75
|
||||
mulhwu[.],Multiply High Word Unsigned,XO,31,11
|
||||
mulld,Multiply Low Doubleword,XO,31,233
|
||||
mulli,Multiply Low Immediate,D,07,
|
||||
mullw[o][.],Multiply Low Word,XO,31,235
|
||||
nand[.],NAND,X,31,476
|
||||
neg[o][.],Negate,XO,31,104
|
||||
nor[.],NOR,X,31,124
|
||||
or[.],OR,X,31,444
|
||||
orc[.],OR with Complement,X,31,412
|
||||
ori,OR Immediate,D,24,
|
||||
oris,OR Immediate Shifted,D,25,
|
||||
rfi,Return from Interrupt,X,19,50
|
||||
rldcl,Rotate Left Doubleword then Clear Left,MDS,30,8
|
||||
rldcr,Rotate Left Doubleword then Clear Right,MDS,30,9
|
||||
rldic,Rotate Left Doubleword Immediate then Clear,MD,30,2
|
||||
rldicl,Rotate Left Doubleword Immediate then Clear Left,MD,30,0
|
||||
rldicr,Rotate Left Doubleword Immediate then Clear Right,MD,30,1
|
||||
rldimi,Rotate Left Doubleword Immediate then Mask Insert,MD,30,3
|
||||
rlwimi[.],Rotate Left Word Immediate then Mask Insert,M,20,
|
||||
rlwinm[.],Rotate Left Word Immediate then AND with Mask,M,21,
|
||||
rlwnm[.],Rotate Left Word then AND with Mask,M,23,
|
||||
sc,System Call,SC,17,
|
||||
si,Subtract Immediate,D,12,
|
||||
si.,Subtract Immediate and Record,D,13,
|
||||
slbia,SLB Invalidate All,X,31,498
|
||||
slbie,SLB Invalidate Entry,X,31,434
|
||||
sld,Shift Left Doubleword,X,31,27
|
||||
slw[.],Shift Left Word,X,31,24
|
||||
srad,Shift Right Algebraic Doubleword,X,31,794
|
||||
sradi,Shift Right Algebraic Doubleword Immediate,XS,31,413
|
||||
srd,Shift Right Doubleword,X,31,539
|
||||
sraw[.],Shift Right Algebraic Word,X,31,792
|
||||
srawi[.],Shift Right Algebraic Word Immediate,X,31,824
|
||||
srw[.],Shift Right Word,X,31,536
|
||||
stb,Store Byte,D,38,
|
||||
stbu,Store Byte with Update,D,39,
|
||||
stbux,Store Byte with Update Indexed,X,31,247
|
||||
stbx,Store Byte Indexed,X,31,215
|
||||
std,Store Doubleword,DS,62,0
|
||||
stdcx,Store Doubleword Conditional Indexed,X,31,214
|
||||
stdu,Store Doubleword with Update,DS,62,1
|
||||
stdux,Store Doubleword with Update Indexed,X,31,181
|
||||
stdx,Store Doubleword Indexed,X,31,149
|
||||
stfd,Store Floating-Point Double,D,54,
|
||||
stfdu,Store Floating-Point Double with Update,D,55,
|
||||
stfdux,Store Floating-Point Double with Update Indexed,X,31,759
|
||||
stfdx,Store Floating-Point Double Indexed,X,31,727
|
||||
stfiwx,Store Floating-Point as Integer Word Indexed (optional),X,31,983
|
||||
stfs,Store Floating-Point Single,D,52,
|
||||
stfsu,Store Floating-Point Single with Update,D,53,
|
||||
stfsux,Store Floating-Point Single with Update Indexed,X,31,695
|
||||
stfsx,Store Floating-Point Single Indexed,X,31,663
|
||||
sth,Store Half,D,44,
|
||||
sthbrx,Store Half Byte-Reverse Indexed,X,31,918
|
||||
sthu,Store Half with Update,D,45,
|
||||
sthux,Store Half with Update Indexed,X,31,439
|
||||
sthx,Store Half Indexed,X,31,407
|
||||
stmw,Store Multiple Word,D,47,
|
||||
stswi,Store String Word Immediate,X,31,725
|
||||
stswx,Store String Word Indexed,X,31,661
|
||||
stw,Store,D,36,
|
||||
stwbrx,Store Word Byte-Reversed Indexed,X,31,662
|
||||
stwcx.,Store Word Conditional Indexed,X,31,150
|
||||
stwu,Store Word with Update,D,37,
|
||||
stwux,Store Word with Update Indexed,X,31,183
|
||||
stwx,Store Word Indexed,X,31,151
|
||||
subf[o][.],Subtract from,XO,31,40
|
||||
subfc[o][.],Subtract from Carrying,XO,31,08
|
||||
subfe[o][.],Subtract from Extended,XO,31,136
|
||||
subfic,Subtract from Immediate Carrying,D,08,
|
||||
subfme[o][.],Subtract from Minus One Extended,XO,31,232
|
||||
subfze[o][.],Subtract from Zero Extended,XO,31,200
|
||||
sync,Synchronize,X,31,598
|
||||
td,Trap Doubleword,X,31,68
|
||||
tdi,Trap Doubleword Immediate,D,2,
|
||||
tlbie,Translation Look-aside Buffer Invalidate Entry (optional),X,31,306
|
||||
tlbsync,Translation Look-aside Buffer Synchronize (optional),X,31,566
|
||||
tw,Trap Word,X,31,04
|
||||
twi,Trap Word Immediate,D,03,
|
||||
xor[.],XOR,X,31,316
|
||||
xori,XOR Immediate,D,26,
|
||||
xoris,XOR Immediate Shift,D,27,
|
||||
Vendored
-340
@@ -1,340 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
printf(
|
||||
"// this file is autogenerated DO NOT MODIFY\n"
|
||||
"#pragma once\n"
|
||||
);
|
||||
FILE* fp = fopen(argv[1], "rt");
|
||||
if (fp) {
|
||||
char line[80];
|
||||
while (fgets(line, sizeof line, fp) != NULL) {
|
||||
bool with_o = strstr(line, "[o]"), with_d = strstr(line, "[.]");
|
||||
char* p = strchr(line, '\n'), *name = strchr(line, ','), *mem = line;
|
||||
if (p) *p = '\0';
|
||||
if (name) {
|
||||
*name++ = '\0';
|
||||
char *form = strchr(name, ',');
|
||||
if (form) {
|
||||
*form++ = '\0';
|
||||
char *opc = strchr(form, ',');
|
||||
if (opc) {
|
||||
*opc++ = '\0';
|
||||
char *sec = strchr(opc, ',');
|
||||
if (sec) {
|
||||
struct b_info { const char *s; int o; int p; } infos[] = {
|
||||
{"",1,0},
|
||||
{"LT",1,12},
|
||||
{"LE",2,4},
|
||||
{"NG",2,4},
|
||||
{"EQ",3,12},
|
||||
{"GE",1,4},
|
||||
{"NL",1,4},
|
||||
{"GT",2,12},
|
||||
{"NE",3,4},
|
||||
{"SO",4,12},
|
||||
{"UN",4,12},
|
||||
{"NS",4,4},
|
||||
{"NU",4,4},
|
||||
};
|
||||
#define OP_EXT ((i_opcode << 26) | (i_extopc << 1))
|
||||
if (strchr(mem, '[') != NULL) *strchr(mem, '[') = '\0';
|
||||
if (strchr(mem, '.') != NULL) *strchr(mem, '.') = '_';
|
||||
*sec++ = '\0';
|
||||
for (int i = 0; i < strlen(mem); ++i)
|
||||
mem[i] = toupper(mem[i]);
|
||||
int i_opcode = atoi(opc);
|
||||
int i_extopc = atoi(sec);
|
||||
//printf("// %s\n", mem);
|
||||
if (!strcmp(form, "XO")) {
|
||||
if (!strcmp(mem, "EXTSH")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, false, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sC(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, true, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, false, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sC_(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, true, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, false, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sO(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, true, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, false, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sO_(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, true, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "X")) {
|
||||
if (!strcmp(mem, "CMPL") || !strcmp(mem, "CMP")) {
|
||||
printf(
|
||||
"void %s(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, GPR{(bf << 2) | l}, ra, rb, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else if (!strcmp(mem, "CNTLZD") || !strcmp(mem, "CNTLZW") || !strcmp(mem, "EXTSB") || !strcmp(mem, "EXTSH") || !strcmp(mem, "EXTSW")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, ra, rt, R0, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, ra, rt, R0, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, ra, rt, rb, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, ra, rt, rb, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "I")) {
|
||||
printf(
|
||||
"void %s(Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, i, false); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
printf(
|
||||
"void %sL(Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, i, true); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else if (!strcmp(form, "B")) {
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
printf(
|
||||
"void %s%s(CPR const cr, Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, %i, cr.index + %i, i, false); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, i_opcode << 26, infos[i].p, infos[i].o - 1);
|
||||
printf(
|
||||
"void %s%sL(CPR const cr, Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, %i, cr.index + %i, i, true); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, i_opcode << 26, infos[i].p, infos[i].o - 1);
|
||||
if (!strcmp(mem, "BC")) mem[1] = '\0';
|
||||
}
|
||||
} else if (!strcmp(form, "D")) {
|
||||
if (!strcmp(mem, "CMPLI") || !strcmp(mem, "CMPI")) {
|
||||
printf(
|
||||
"void %s(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, GPR{(bf << 2) | l}, ra, d); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else if (!strcmp(mem, "ANDIS_") || !strcmp(mem, "ANDI_")
|
||||
|| !strcmp(mem, "ORI") || !strcmp(mem, "ORIS")
|
||||
|| !strcmp(mem, "XORI") || !strcmp(mem, "XORIS")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, ra, rt, d); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, rt, ra, d); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
}
|
||||
} else if (!strcmp(form, "SC")) {
|
||||
printf(
|
||||
"void %s(uint32_t lev) {"
|
||||
" emit_%s(0x%08x, lev); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else if (!strcmp(form, "DS")) {
|
||||
#define OP_EXT_DS ((i_opcode << 26) | (i_extopc << 0))
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, rt, ra, d >> 2); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT_DS);
|
||||
#undef OP_EXT_DS
|
||||
} else if (!strcmp(form, "XS")) {
|
||||
#define OP_EXT_XS ((i_opcode << 26) | (i_extopc << 2))
|
||||
/* HUGE DIFFERENCE DO NOT REMOVE */
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, rt, ra, sh, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT_XS);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, rt, ra, sh, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT_XS);
|
||||
#undef OP_EXT_XS
|
||||
} else if (!strcmp(form, "XL")) {
|
||||
if (!strcmp(mem, "BCLR")) {
|
||||
printf(
|
||||
"void %s(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
if (!strcmp(mem, "BCLR")) mem[1] = '\0';
|
||||
for (int i = 1; i < 12; ++i) {
|
||||
printf(
|
||||
"void %s%sLR(CPR const cr) {"
|
||||
" emit_%s(0x%08x, %i, cr.index + %i, 0, false); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, OP_EXT, infos[i].p, infos[i].o - 1);
|
||||
printf(
|
||||
"void %s%sLRL(CPR const cr) {"
|
||||
" emit_%s(0x%08x, %i, cr.index + %i, 0, true); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, OP_EXT, infos[i].p, infos[i].o - 1);
|
||||
}
|
||||
} else if (mem[0] == 'B') {
|
||||
printf(
|
||||
"void %s(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s(GPR const bt, Cond const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, cond2offset(ba), bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(GPR const bt, Cond const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, cond2offset(ba), bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(CPR const bt, CPR const ba, CPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(CPR const bt, CPR const ba, CPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "M")) {
|
||||
if (!strcmp(mem, "RLWNM")) {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb.index, mb, me, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb.index, mb, me, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, sh, mb, me, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, sh, mb, me, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "MD")) {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, ra, rs, mb, sh, false); "
|
||||
"}\n"
|
||||
, mem, form, (i_opcode << 26) | (i_extopc << 2));
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, ra, rs, mb, sh, true); "
|
||||
"}\n"
|
||||
, mem, form, (i_opcode << 26) | (i_extopc << 2));
|
||||
} else if (!strcmp(form, "MDS")) {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb, mb, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb, mb, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else if (!strcmp(form, "A")) {
|
||||
printf(
|
||||
"void %s(FPR const frt, FPR const fra, FPR const frb, FPR const frc) {"
|
||||
" emit_%s(0x%08x, frt, fra, frb, frc, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) {"
|
||||
" emit_%s(0x%08x, frt, fra, frb, frc, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else if (!strcmp(form, "XFX")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, uint32_t spr) {"
|
||||
" emit_%s(0x%08x, rt, spr); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s() {"
|
||||
" emit_%s(0x%08x); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//printf("%s\n", line);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
Vendored
-398
@@ -1,398 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <sys/mman.h>
|
||||
#include <cstdlib>
|
||||
|
||||
//#ifndef __cpp_lib_unreachable
|
||||
namespace std {
|
||||
[[noreturn]] inline void unreachable() {
|
||||
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
|
||||
__assume(false);
|
||||
#else // GCC, Clang
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
|
||||
namespace powah {
|
||||
|
||||
// Symbolic conditions
|
||||
enum class Cond : uint8_t {
|
||||
LT, LE, NG, EQ, GE, NL, GT, NE, SO, UN, NS, NU
|
||||
};
|
||||
|
||||
struct GPR { uint32_t index; };
|
||||
struct FPR { uint32_t index; };
|
||||
struct VPR { uint32_t index; };
|
||||
struct CPR { uint32_t index; };
|
||||
struct Label { uint32_t index; };
|
||||
|
||||
constexpr inline GPR R0{0};
|
||||
constexpr inline GPR R1{1};
|
||||
constexpr inline GPR R2{2};
|
||||
constexpr inline GPR R3{3};
|
||||
constexpr inline GPR R4{4};
|
||||
constexpr inline GPR R5{5};
|
||||
constexpr inline GPR R6{6};
|
||||
constexpr inline GPR R7{7};
|
||||
constexpr inline GPR R8{8};
|
||||
constexpr inline GPR R9{9};
|
||||
constexpr inline GPR R10{10};
|
||||
constexpr inline GPR R11{11};
|
||||
constexpr inline GPR R12{12};
|
||||
constexpr inline GPR R13{13};
|
||||
constexpr inline GPR R14{14};
|
||||
constexpr inline GPR R15{15};
|
||||
constexpr inline GPR R16{16};
|
||||
constexpr inline GPR R17{17};
|
||||
constexpr inline GPR R18{18};
|
||||
constexpr inline GPR R19{19};
|
||||
constexpr inline GPR R20{20};
|
||||
constexpr inline GPR R21{21};
|
||||
constexpr inline GPR R22{22};
|
||||
constexpr inline GPR R23{23};
|
||||
constexpr inline GPR R24{24};
|
||||
constexpr inline GPR R25{25};
|
||||
constexpr inline GPR R26{26};
|
||||
constexpr inline GPR R27{27};
|
||||
constexpr inline GPR R28{28};
|
||||
constexpr inline GPR R29{29};
|
||||
constexpr inline GPR R30{30};
|
||||
constexpr inline GPR R31{31};
|
||||
|
||||
constexpr inline FPR FR0{0};
|
||||
constexpr inline FPR FR1{1};
|
||||
constexpr inline FPR FR2{2};
|
||||
constexpr inline FPR FR3{3};
|
||||
constexpr inline FPR FR4{4};
|
||||
constexpr inline FPR FR5{5};
|
||||
constexpr inline FPR FR6{6};
|
||||
constexpr inline FPR FR7{7};
|
||||
constexpr inline FPR FR8{8};
|
||||
constexpr inline FPR FR9{9};
|
||||
constexpr inline FPR FR10{10};
|
||||
constexpr inline FPR FR11{11};
|
||||
constexpr inline FPR FR12{12};
|
||||
constexpr inline FPR FR13{13};
|
||||
constexpr inline FPR FR14{14};
|
||||
constexpr inline FPR FR15{15};
|
||||
constexpr inline FPR FR16{16};
|
||||
constexpr inline FPR FR17{17};
|
||||
constexpr inline FPR FR18{18};
|
||||
constexpr inline FPR FR19{19};
|
||||
constexpr inline FPR FR20{20};
|
||||
constexpr inline FPR FR21{21};
|
||||
constexpr inline FPR FR22{22};
|
||||
constexpr inline FPR FR23{23};
|
||||
constexpr inline FPR FR24{24};
|
||||
constexpr inline FPR FR25{25};
|
||||
constexpr inline FPR FR26{26};
|
||||
constexpr inline FPR FR27{27};
|
||||
constexpr inline FPR FR28{28};
|
||||
constexpr inline FPR FR29{29};
|
||||
constexpr inline FPR FR30{30};
|
||||
constexpr inline FPR FR31{31};
|
||||
|
||||
// They call it CPR because when programmers see this code they-
|
||||
constexpr inline CPR CR0{0};
|
||||
constexpr inline CPR CR1{4};
|
||||
constexpr inline CPR CR2{8};
|
||||
constexpr inline CPR CR3{12};
|
||||
constexpr inline CPR CR4{16};
|
||||
constexpr inline CPR CR5{20};
|
||||
constexpr inline CPR CR6{24};
|
||||
constexpr inline CPR CR7{28};
|
||||
|
||||
constexpr uint32_t XER_SO = 32;
|
||||
constexpr uint32_t XER_OV = 33;
|
||||
constexpr uint32_t XER_CA = 34;
|
||||
|
||||
enum class RelocKind : uint8_t {
|
||||
FormB,
|
||||
FormI,
|
||||
};
|
||||
struct RelocInfo {
|
||||
uint32_t offset;
|
||||
RelocKind kind;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
Context() = default;
|
||||
Context(void* ptr, size_t size)
|
||||
: base{reinterpret_cast<uint32_t*>(ptr)}
|
||||
, offset{0}
|
||||
, size{uint32_t(size)} {
|
||||
|
||||
}
|
||||
~Context() = default;
|
||||
|
||||
std::vector<uint32_t> labels;
|
||||
std::vector<std::pair<uint32_t, RelocInfo>> relocs;
|
||||
|
||||
Label DefineLabel() {
|
||||
labels.push_back(0);
|
||||
return Label{ uint32_t(labels.size() - 1) };
|
||||
}
|
||||
|
||||
void ApplyRelocs() {
|
||||
for (auto const &[index, info] : relocs) {
|
||||
//assert(labels[index] != 0); //label must have an addr
|
||||
int32_t rel = (int32_t)labels[index] - (int32_t)info.offset;
|
||||
switch (info.kind) {
|
||||
case RelocKind::FormB:
|
||||
base[info.offset] |= bitExt(rel, 16, 14);
|
||||
break;
|
||||
case RelocKind::FormI:
|
||||
base[info.offset] |= bitExt(rel, 6, 24);
|
||||
break;
|
||||
}
|
||||
}
|
||||
relocs.clear();
|
||||
}
|
||||
|
||||
void LABEL(Label l) {
|
||||
assert(labels[l.index] == 0);
|
||||
labels[l.index] = offset;
|
||||
}
|
||||
|
||||
static constexpr uint32_t cond2offset(Cond c) noexcept {
|
||||
switch (c) {
|
||||
case Cond::LT: return 1;
|
||||
case Cond::LE: return 2;
|
||||
case Cond::NG: return 2;
|
||||
case Cond::EQ: return 3;
|
||||
case Cond::GE: return 1;
|
||||
case Cond::NL: return 1;
|
||||
case Cond::GT: return 2;
|
||||
case Cond::NE: return 3;
|
||||
case Cond::SO: return 4;
|
||||
case Cond::UN: return 4;
|
||||
case Cond::NS: return 4;
|
||||
case Cond::NU: return 4;
|
||||
default: return 0; //hopefully icc isn't stupid
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bitExt(uint32_t value, uint32_t offs, uint32_t n) {
|
||||
uint32_t mask = (1UL << n) - 1;
|
||||
return (value & mask) << (32 - (n + offs));
|
||||
}
|
||||
void emit_XO(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool oe, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| bitExt(oe, 21, 1)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_D(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| (d & 0xffff)
|
||||
);
|
||||
}
|
||||
void emit_X(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_XS(uint32_t op, GPR const rt, GPR const ra, uint32_t sh, bool rc) {
|
||||
base[offset++] = (op |
|
||||
((rt.index & 0x1f) << 16)
|
||||
| ((ra.index & 0x1f) << 21)
|
||||
| ((sh & 0x1f) << 11)
|
||||
| ((sh >> 4) & 0x02)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_reloc_I(uint32_t op, Label const& l, bool lk) {
|
||||
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormI });
|
||||
base[offset++] = (op |
|
||||
bitExt(lk, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_reloc_B(uint32_t op, uint32_t bo, uint32_t cri, Label const& l, bool lk) {
|
||||
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormB });
|
||||
base[offset++] = (op |
|
||||
bitExt(bo, 6, 5)
|
||||
| bitExt(cri, 11, 5)
|
||||
| bitExt(lk, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_XL(uint32_t op, uint32_t bt, uint32_t ba, uint32_t bb, bool lk) {
|
||||
base[offset++] = (op |
|
||||
bitExt(bt, 6, 5)
|
||||
| bitExt(ba, 11, 5)
|
||||
| bitExt(bb, 16, 5)
|
||||
| bitExt(lk, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_A(uint32_t op, FPR const frt, FPR const fra, FPR const frb, FPR const frc, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(frt.index, 6, 5)
|
||||
| bitExt(fra.index, 11, 5)
|
||||
| bitExt(frb.index, 16, 5)
|
||||
| bitExt(frc.index, 21, 5)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_DS(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
|
||||
//assert(d & 0x03 == 0);
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(d, 16, 14)
|
||||
);
|
||||
}
|
||||
void emit_M(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me, bool rc) {
|
||||
assert(sh <= 0x3f && mb <= 0x3f);
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| ((sh & 0x1f) << 11)
|
||||
| ((mb & 0x1f) << 6)
|
||||
| ((me & 0x1f) << 1)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_MD(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
|
||||
assert(mb <= 0x3f);
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| ((mb & 0x1f) << 6) | (mb & 0x20)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_MD(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, bool rc) {
|
||||
assert(sh <= 0x3f && mb <= 0x3f);
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| ((mb & 0x1f) << 6) | (mb & 0x20)
|
||||
| ((sh & 0x1f) << 11) | ((sh >> 4) & 0x02)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_MDS(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| ((mb & 0x1f) << 6) | (mb & 0x20)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_XFL(uint32_t op) {
|
||||
(void)op;
|
||||
std::abort();
|
||||
}
|
||||
void emit_XFX(uint32_t op, GPR const rt, uint32_t spr) {
|
||||
base[offset++] = (op |
|
||||
(rt.index & 0x1f) << 21
|
||||
| ((spr & 0xff) << 12)
|
||||
);
|
||||
}
|
||||
void emit_SC(uint32_t op, uint32_t lev) {
|
||||
(void)op;
|
||||
(void)lev;
|
||||
std::abort();
|
||||
}
|
||||
|
||||
// Extended Memmonics, hand coded :)
|
||||
void MR(GPR const ra, GPR const rs) { OR(ra, rs, rs); }
|
||||
void NOP() { ORI(R0, R0, 0); }
|
||||
void NOT(GPR const ra, GPR const rs) { NOR(ra, rs, rs); }
|
||||
|
||||
void ROTLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, n, 0); }
|
||||
void ROTRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, 0); }
|
||||
|
||||
void ROTLWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, n, 0, 31); }
|
||||
void ROTRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, 0, 31); }
|
||||
|
||||
void ROTLW(GPR const ra, GPR const rs, GPR const rb) { RLWNM(ra, rs, rb, 0, 31); }
|
||||
void ROTLD(GPR const ra, GPR const rs, GPR const rb) { RLDCL(ra, rs, rb, 0); }
|
||||
|
||||
void EXTLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, b, n - 1); }
|
||||
void SLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, n, 63 - n); }
|
||||
void CLRLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 0, n); }
|
||||
|
||||
void EXTRDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICL(ra, rs, b + n, 64 - n); }
|
||||
void SRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, n); }
|
||||
void CLLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, 0, n); }
|
||||
void CLRSLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, n, b - n); }
|
||||
|
||||
void EXTLWI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLWINM(ra, rs, b, 0, n - 1); }
|
||||
void SRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, n, 31); }
|
||||
void CLRRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 0, 0, 31 - n); }
|
||||
|
||||
void CRSET(CPR const bx) { CREQV(bx, bx, bx); }
|
||||
void CRCLR(CPR const bx) { CRXOR(bx, bx, bx); }
|
||||
void CRMOVE(CPR const bx, CPR const by) { CROR(bx, by, by); }
|
||||
void CRNOT(CPR const bx, CPR const by) { CRNOR(bx, by, by); }
|
||||
|
||||
void CMPLDI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 1, rx, v); }
|
||||
void CMPLWI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 0, rx, v); }
|
||||
void CMPLD(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 1, rx, ry); }
|
||||
void CMPLW(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 0, rx, ry); }
|
||||
void CMPLDI(GPR const rx, uint32_t v) { CMPLI(0, 1, rx, v); }
|
||||
void CMPLWI(GPR const rx, uint32_t v) { CMPLI(0, 0, rx, v); }
|
||||
void CMPLD(GPR const rx, GPR const ry) { CMPL(0, 1, rx, ry); }
|
||||
void CMPLW(GPR const rx, GPR const ry) { CMPL(0, 0, rx, ry); }
|
||||
|
||||
void CMPWI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 0, rx, si); }
|
||||
void CMPW(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 0, rx, ry); }
|
||||
void CMPDI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 1, rx, si); }
|
||||
void CMPD(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 1, rx, ry); }
|
||||
void CMPWI(GPR const rx, uint32_t si) { CMPI(0, 0, rx, si); }
|
||||
void CMPW(GPR const rx, GPR const ry) { CMP(0, 0, rx, ry); }
|
||||
void CMPDI(GPR const rx, uint32_t si) { CMPI(0, 1, rx, si); }
|
||||
void CMPD(GPR const rx, GPR const ry) { CMP(0, 1, rx, ry); }
|
||||
|
||||
void LI(GPR const rx, uint32_t value) { ADDI(rx, R0, value); }
|
||||
void LIS(GPR const rx, uint32_t value) { ADDIS(rx, R0, value); }
|
||||
|
||||
void MFLR(GPR const rt) { MFSPR(powah::GPR{8}, rt, powah::GPR{0}); }
|
||||
void MTLR(GPR const rt) { MTSPR(powah::GPR{8}, rt, powah::GPR{0}); }
|
||||
void BLR() { base[offset++] = 0x4e800020; } //BCLR(R0, CR0, R0);
|
||||
|
||||
void MFCTR(GPR const rt) { MFSPR(powah::GPR{9}, rt, powah::GPR{0}); }
|
||||
void MTCTR(GPR const rt) { MTSPR(powah::GPR{9}, rt, powah::GPR{0}); }
|
||||
void BCTRL() { base[offset++] = 0x4e800421; } //BCCTRL(R0, CR0, R0);
|
||||
void BCTR() { base[offset++] = 0x4e800420; } //BCCTR(R0, CR0, R0);
|
||||
|
||||
// TODO: PowerPC 11 stuff
|
||||
void ISEL(GPR const rd, GPR const ra, GPR const rb, uint32_t d) {
|
||||
(void)rd;
|
||||
(void)ra;
|
||||
(void)rb;
|
||||
(void)d;
|
||||
std::unreachable();
|
||||
}
|
||||
void ISELLT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 0); }
|
||||
void ISELGT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 1); }
|
||||
void ISELEQ(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 2); }
|
||||
|
||||
// Rawly pasted because fuck you
|
||||
#include "powah_gen_base.hpp"
|
||||
|
||||
uint32_t* base = nullptr;
|
||||
uint32_t offset = 0;
|
||||
uint32_t size = 0;
|
||||
};
|
||||
|
||||
}
|
||||
Vendored
-484
@@ -1,484 +0,0 @@
|
||||
// this file is autogenerated DO NOT MODIFY
|
||||
#pragma once
|
||||
void ADD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, false, false); }
|
||||
void ADDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, true, false); }
|
||||
void ADD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, false, true); }
|
||||
void ADDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, true, true); }
|
||||
void ADDC(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, false, false); }
|
||||
void ADDCO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, true, false); }
|
||||
void ADDC_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, false, true); }
|
||||
void ADDCO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, true, true); }
|
||||
void ADDE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, false, false); }
|
||||
void ADDEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, true, false); }
|
||||
void ADDE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, false, true); }
|
||||
void ADDEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, true, true); }
|
||||
void ADDI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x38000000, rt, ra, d); }
|
||||
void ADDIC(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x30000000, rt, ra, d); }
|
||||
void ADDIC_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x34000000, rt, ra, d); }
|
||||
void ADDIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x3c000000, rt, ra, d); }
|
||||
void ADDME(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, false, false); }
|
||||
void ADDMEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, true, false); }
|
||||
void ADDME_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, false, true); }
|
||||
void ADDMEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, true, true); }
|
||||
void ADDZE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, false, false); }
|
||||
void ADDZEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, true, false); }
|
||||
void ADDZE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, false, true); }
|
||||
void ADDZEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, true, true); }
|
||||
void AND(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000038, ra, rt, rb, false); }
|
||||
void AND_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000038, ra, rt, rb, true); }
|
||||
void ANDC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000078, ra, rt, rb, false); }
|
||||
void ANDC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000078, ra, rt, rb, true); }
|
||||
void ANDI_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x70000000, ra, rt, d); }
|
||||
void ANDIS_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x74000000, ra, rt, d); }
|
||||
void B(Label const& i) { emit_reloc_I(0x48000000, i, false); }
|
||||
void BL(Label const& i) { emit_reloc_I(0x48000000, i, true); }
|
||||
void BC(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 0, cr.index + 0, i, false); }
|
||||
void BCL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 0, cr.index + 0, i, true); }
|
||||
void BLT(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 0, i, false); }
|
||||
void BLTL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 0, i, true); }
|
||||
void BLE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, false); }
|
||||
void BLEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, true); }
|
||||
void BNG(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, false); }
|
||||
void BNGL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, true); }
|
||||
void BEQ(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 2, i, false); }
|
||||
void BEQL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 2, i, true); }
|
||||
void BGE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, false); }
|
||||
void BGEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, true); }
|
||||
void BNL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, false); }
|
||||
void BNLL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, true); }
|
||||
void BGT(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 1, i, false); }
|
||||
void BGTL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 1, i, true); }
|
||||
void BNE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 2, i, false); }
|
||||
void BNEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 2, i, true); }
|
||||
void BSO(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, false); }
|
||||
void BSOL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, true); }
|
||||
void BUN(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, false); }
|
||||
void BUNL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, true); }
|
||||
void BNS(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 3, i, false); }
|
||||
void BNSL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 3, i, true); }
|
||||
void BCCTR(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, ba.index, bb.index, false); }
|
||||
void BCCTRL(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, ba.index, bb.index, true); }
|
||||
void BCCTR(GPR const bt, Cond const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, cond2offset(ba), bb.index, false); }
|
||||
void BCCTRL(GPR const bt, Cond const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, cond2offset(ba), bb.index, true); }
|
||||
void BCLR(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000020, bt.index, ba.index, bb.index, false); }
|
||||
void BCLRL(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000020, bt.index, ba.index, bb.index, true); }
|
||||
void BLTLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 0, 0, false); }
|
||||
void BLTLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 0, 0, true); }
|
||||
void BLELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, false); }
|
||||
void BLELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, true); }
|
||||
void BNGLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, false); }
|
||||
void BNGLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, true); }
|
||||
void BEQLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 2, 0, false); }
|
||||
void BEQLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 2, 0, true); }
|
||||
void BGELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, false); }
|
||||
void BGELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, true); }
|
||||
void BNLLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, false); }
|
||||
void BNLLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, true); }
|
||||
void BGTLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 1, 0, false); }
|
||||
void BGTLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 1, 0, true); }
|
||||
void BNELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 2, 0, false); }
|
||||
void BNELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 2, 0, true); }
|
||||
void BSOLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, false); }
|
||||
void BSOLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, true); }
|
||||
void BUNLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, false); }
|
||||
void BUNLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, true); }
|
||||
void BNSLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 3, 0, false); }
|
||||
void BNSLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 3, 0, true); }
|
||||
void CMP(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) { emit_X(0x7c000000, GPR{(bf << 2) | l}, ra, rb, false); }
|
||||
void CMPI(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) { emit_D(0x2c000000, GPR{(bf << 2) | l}, ra, d); }
|
||||
void CMPL(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) { emit_X(0x7c000040, GPR{(bf << 2) | l}, ra, rb, false); }
|
||||
void CMPLI(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) { emit_D(0x28000000, GPR{(bf << 2) | l}, ra, d); }
|
||||
void CNTLZD(GPR const rt, GPR const ra) { emit_X(0x7c000074, ra, rt, R0, false); }
|
||||
void CNTLZD_(GPR const rt, GPR const ra) { emit_X(0x7c000074, ra, rt, R0, true); }
|
||||
void CNTLZW(GPR const rt, GPR const ra) { emit_X(0x7c000034, ra, rt, R0, false); }
|
||||
void CNTLZW_(GPR const rt, GPR const ra) { emit_X(0x7c000034, ra, rt, R0, true); }
|
||||
void CRAND(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000202, bt.index, ba.index, bb.index, false); }
|
||||
void CRANDL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000202, bt.index, ba.index, bb.index, true); }
|
||||
void CRANDC(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000102, bt.index, ba.index, bb.index, false); }
|
||||
void CRANDCL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000102, bt.index, ba.index, bb.index, true); }
|
||||
void CREQV(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000242, bt.index, ba.index, bb.index, false); }
|
||||
void CREQVL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000242, bt.index, ba.index, bb.index, true); }
|
||||
void CRNAND(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c0001c2, bt.index, ba.index, bb.index, false); }
|
||||
void CRNANDL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c0001c2, bt.index, ba.index, bb.index, true); }
|
||||
void CRNOR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000042, bt.index, ba.index, bb.index, false); }
|
||||
void CRNORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000042, bt.index, ba.index, bb.index, true); }
|
||||
void CROR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000382, bt.index, ba.index, bb.index, false); }
|
||||
void CRORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000382, bt.index, ba.index, bb.index, true); }
|
||||
void CRORC(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000342, bt.index, ba.index, bb.index, false); }
|
||||
void CRORCL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000342, bt.index, ba.index, bb.index, true); }
|
||||
void CRXOR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000182, bt.index, ba.index, bb.index, false); }
|
||||
void CRXORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000182, bt.index, ba.index, bb.index, true); }
|
||||
void DCBF(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ac, ra, rt, rb, false); }
|
||||
void DCBF_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ac, ra, rt, rb, true); }
|
||||
void DCBI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003ac, ra, rt, rb, false); }
|
||||
void DCBI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003ac, ra, rt, rb, true); }
|
||||
void DCBST(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006c, ra, rt, rb, false); }
|
||||
void DCBST_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006c, ra, rt, rb, true); }
|
||||
void DCBT(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022c, ra, rt, rb, false); }
|
||||
void DCBT_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022c, ra, rt, rb, true); }
|
||||
void DCBTST(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ec, ra, rt, rb, false); }
|
||||
void DCBTST_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ec, ra, rt, rb, true); }
|
||||
void DCBZ(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ec, ra, rt, rb, false); }
|
||||
void DCBZ_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ec, ra, rt, rb, true); }
|
||||
void DIVD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, false, false); }
|
||||
void DIVDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, true, false); }
|
||||
void DIVD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, false, true); }
|
||||
void DIVDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, true, true); }
|
||||
void DIVDU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, false, false); }
|
||||
void DIVDUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, true, false); }
|
||||
void DIVDU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, false, true); }
|
||||
void DIVDUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, true, true); }
|
||||
void DIVW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, false, false); }
|
||||
void DIVWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, true, false); }
|
||||
void DIVW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, false, true); }
|
||||
void DIVWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, true, true); }
|
||||
void DIVWU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, false, false); }
|
||||
void DIVWUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, true, false); }
|
||||
void DIVWU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, false, true); }
|
||||
void DIVWUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, true, true); }
|
||||
void ECIWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00026c, ra, rt, rb, false); }
|
||||
void ECIWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00026c, ra, rt, rb, true); }
|
||||
void ECOWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036c, ra, rt, rb, false); }
|
||||
void ECOWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036c, ra, rt, rb, true); }
|
||||
void EIEIO(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0006ac, ra, rt, rb, false); }
|
||||
void EIEIO_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0006ac, ra, rt, rb, true); }
|
||||
void EQV(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000238, ra, rt, rb, false); }
|
||||
void EQV_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000238, ra, rt, rb, true); }
|
||||
void EXTSB(GPR const rt, GPR const ra) { emit_X(0x7c000774, ra, rt, R0, false); }
|
||||
void EXTSB_(GPR const rt, GPR const ra) { emit_X(0x7c000774, ra, rt, R0, true); }
|
||||
void EXTSH(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, false, false); }
|
||||
void EXTSHC(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, true, false); }
|
||||
void EXTSH_(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, false, true); }
|
||||
void EXTSHC_(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, true, true); }
|
||||
void EXTSW(GPR const rt, GPR const ra) { emit_X(0x7c0007b4, ra, rt, R0, false); }
|
||||
void EXTSW_(GPR const rt, GPR const ra) { emit_X(0x7c0007b4, ra, rt, R0, true); }
|
||||
void FABS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000210, ra, rt, rb, false); }
|
||||
void FABS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000210, ra, rt, rb, true); }
|
||||
void FADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002a, frt, fra, frb, frc, false); }
|
||||
void FADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002a, frt, fra, frb, frc, true); }
|
||||
void FADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00002a, frt, fra, frb, frc, false); }
|
||||
void FADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00002a, frt, fra, frb, frc, true); }
|
||||
void FCFID(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00069c, ra, rt, rb, false); }
|
||||
void FCFID_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00069c, ra, rt, rb, true); }
|
||||
void FCMPO(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000040, ra, rt, rb, false); }
|
||||
void FCMPO_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000040, ra, rt, rb, true); }
|
||||
void FCMPU(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc000000, bt.index, ba.index, bb.index, false); }
|
||||
void FCMPUL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc000000, bt.index, ba.index, bb.index, true); }
|
||||
void FCTID(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065c, ra, rt, rb, false); }
|
||||
void FCTID_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065c, ra, rt, rb, true); }
|
||||
void FCTIDZ(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065e, ra, rt, rb, false); }
|
||||
void FCTIDZ_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065e, ra, rt, rb, true); }
|
||||
void FCTIW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00001c, ra, rt, rb, false); }
|
||||
void FCTIW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00001c, ra, rt, rb, true); }
|
||||
void FCTIWZ(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc00001e, bt.index, ba.index, bb.index, false); }
|
||||
void FCTIWZL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc00001e, bt.index, ba.index, bb.index, true); }
|
||||
void FDIV(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000024, frt, fra, frb, frc, false); }
|
||||
void FDIV_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000024, frt, fra, frb, frc, true); }
|
||||
void FDIVS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000024, frt, fra, frb, frc, false); }
|
||||
void FDIVS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000024, frt, fra, frb, frc, true); }
|
||||
void FMADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003a, frt, fra, frb, frc, false); }
|
||||
void FMADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003a, frt, fra, frb, frc, true); }
|
||||
void FMADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003a, frt, fra, frb, frc, false); }
|
||||
void FMADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003a, frt, fra, frb, frc, true); }
|
||||
void FMR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000090, ra, rt, rb, false); }
|
||||
void FMR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000090, ra, rt, rb, true); }
|
||||
void FMSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000038, frt, fra, frb, frc, false); }
|
||||
void FMSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000038, frt, fra, frb, frc, true); }
|
||||
void FMSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000038, frt, fra, frb, frc, false); }
|
||||
void FMSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000038, frt, fra, frb, frc, true); }
|
||||
void FMUL(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000032, frt, fra, frb, frc, false); }
|
||||
void FMUL_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000032, frt, fra, frb, frc, true); }
|
||||
void FMULS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000032, frt, fra, frb, frc, false); }
|
||||
void FMULS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000032, frt, fra, frb, frc, true); }
|
||||
void FNABS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000110, ra, rt, rb, false); }
|
||||
void FNABS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000110, ra, rt, rb, true); }
|
||||
void FNEG(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000050, ra, rt, rb, false); }
|
||||
void FNEG_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000050, ra, rt, rb, true); }
|
||||
void FNMADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003e, frt, fra, frb, frc, false); }
|
||||
void FNMADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003e, frt, fra, frb, frc, true); }
|
||||
void FNMADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003e, frt, fra, frb, frc, false); }
|
||||
void FNMADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003e, frt, fra, frb, frc, true); }
|
||||
void FNMSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003c, frt, fra, frb, frc, false); }
|
||||
void FNMSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003c, frt, fra, frb, frc, true); }
|
||||
void FNMSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003c, frt, fra, frb, frc, false); }
|
||||
void FNMSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003c, frt, fra, frb, frc, true); }
|
||||
void FRES(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000030, frt, fra, frb, frc, false); }
|
||||
void FRES_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000030, frt, fra, frb, frc, true); }
|
||||
void FRSP(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000018, ra, rt, rb, false); }
|
||||
void FRSP_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000018, ra, rt, rb, true); }
|
||||
void FRSQRTE(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000034, frt, fra, frb, frc, false); }
|
||||
void FRSQRTE_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000034, frt, fra, frb, frc, true); }
|
||||
void FSEL(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002e, frt, fra, frb, frc, false); }
|
||||
void FSEL_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002e, frt, fra, frb, frc, true); }
|
||||
void FSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000028, frt, fra, frb, frc, false); }
|
||||
void FSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000028, frt, fra, frb, frc, true); }
|
||||
void FSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000028, frt, fra, frb, frc, false); }
|
||||
void FSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000028, frt, fra, frb, frc, true); }
|
||||
void ICBI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ac, ra, rt, rb, false); }
|
||||
void ICBI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ac, ra, rt, rb, true); }
|
||||
void ISYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c00012c, ra, rt, rb, false); }
|
||||
void ISYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c00012c, ra, rt, rb, true); }
|
||||
void LBZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x88000000, rt, ra, d); }
|
||||
void LBZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x8c000000, rt, ra, d); }
|
||||
void LBZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ee, ra, rt, rb, false); }
|
||||
void LBZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ee, ra, rt, rb, true); }
|
||||
void LBZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ae, ra, rt, rb, false); }
|
||||
void LBZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ae, ra, rt, rb, true); }
|
||||
void LD(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000000, rt, ra, d >> 2); }
|
||||
void LDARX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a8, ra, rt, rb, false); }
|
||||
void LDARX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a8, ra, rt, rb, true); }
|
||||
void LDU(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000001, rt, ra, d >> 2); }
|
||||
void LDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006a, ra, rt, rb, false); }
|
||||
void LDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006a, ra, rt, rb, true); }
|
||||
void LDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002a, ra, rt, rb, false); }
|
||||
void LDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002a, ra, rt, rb, true); }
|
||||
void LFD(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc8000000, rt, ra, d); }
|
||||
void LFDU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xcc000000, rt, ra, d); }
|
||||
void LFDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ee, ra, rt, rb, false); }
|
||||
void LFDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ee, ra, rt, rb, true); }
|
||||
void LFDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ae, ra, rt, rb, false); }
|
||||
void LFDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ae, ra, rt, rb, true); }
|
||||
void LFS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc0000000, rt, ra, d); }
|
||||
void LFSU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc4000000, rt, ra, d); }
|
||||
void LFSUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046e, ra, rt, rb, false); }
|
||||
void LFSUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046e, ra, rt, rb, true); }
|
||||
void LFSX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042e, ra, rt, rb, false); }
|
||||
void LFSX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042e, ra, rt, rb, true); }
|
||||
void LHA(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa8000000, rt, ra, d); }
|
||||
void LHAU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xac000000, rt, ra, d); }
|
||||
void LHAUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ee, ra, rt, rb, false); }
|
||||
void LHAUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ee, ra, rt, rb, true); }
|
||||
void LHAX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ae, ra, rt, rb, false); }
|
||||
void LHAX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ae, ra, rt, rb, true); }
|
||||
void LHBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00062c, ra, rt, rb, false); }
|
||||
void LHBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00062c, ra, rt, rb, true); }
|
||||
void LHZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa0000000, rt, ra, d); }
|
||||
void LHZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa4000000, rt, ra, d); }
|
||||
void LHZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000296, ra, rt, rb, false); }
|
||||
void LHZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000296, ra, rt, rb, true); }
|
||||
void LHZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022e, ra, rt, rb, false); }
|
||||
void LHZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022e, ra, rt, rb, true); }
|
||||
void LMW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb8000000, rt, ra, d); }
|
||||
void LSWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004aa, ra, rt, rb, false); }
|
||||
void LSWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004aa, ra, rt, rb, true); }
|
||||
void LSWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042a, ra, rt, rb, false); }
|
||||
void LSWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042a, ra, rt, rb, true); }
|
||||
void LWA(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000002, rt, ra, d >> 2); }
|
||||
void LWARX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000028, ra, rt, rb, false); }
|
||||
void LWARX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000028, ra, rt, rb, true); }
|
||||
void LWAUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ea, ra, rt, rb, false); }
|
||||
void LWAUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ea, ra, rt, rb, true); }
|
||||
void LWAX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002aa, ra, rt, rb, false); }
|
||||
void LWAX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002aa, ra, rt, rb, true); }
|
||||
void LWBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042c, ra, rt, rb, false); }
|
||||
void LWBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042c, ra, rt, rb, true); }
|
||||
void LWZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x80000000, rt, ra, d); }
|
||||
void LWZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x84000000, rt, ra, d); }
|
||||
void LWZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006e, ra, rt, rb, false); }
|
||||
void LWZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006e, ra, rt, rb, true); }
|
||||
void LWZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002e, ra, rt, rb, false); }
|
||||
void LWZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002e, ra, rt, rb, true); }
|
||||
void MCRF(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000000, bt.index, ba.index, bb.index, false); }
|
||||
void MCRFL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000000, bt.index, ba.index, bb.index, true); }
|
||||
void MCRFS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000080, ra, rt, rb, false); }
|
||||
void MCRFS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000080, ra, rt, rb, true); }
|
||||
void MCRXR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000400, ra, rt, rb, false); }
|
||||
void MCRXR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000400, ra, rt, rb, true); }
|
||||
void MFCR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000026, ra, rt, rb, false); }
|
||||
void MFCR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000026, ra, rt, rb, true); }
|
||||
void MFFS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00048e, ra, rt, rb, false); }
|
||||
void MFFS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00048e, ra, rt, rb, true); }
|
||||
void MFMSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a6, ra, rt, rb, false); }
|
||||
void MFMSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a6, ra, rt, rb, true); }
|
||||
void MFSPR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002a6, ra, rt, rb, false); }
|
||||
void MFSPR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002a6, ra, rt, rb, true); }
|
||||
void MFSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004a6, ra, rt, rb, false); }
|
||||
void MFSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004a6, ra, rt, rb, true); }
|
||||
void MFSRIN(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000526, ra, rt, rb, false); }
|
||||
void MFSRIN_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000526, ra, rt, rb, true); }
|
||||
void MTCRF(GPR const rt, uint32_t spr) { emit_XFX(0x7c000120, rt, spr); }
|
||||
void MTFSB0(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00008c, ra, rt, rb, false); }
|
||||
void MTFSB0_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00008c, ra, rt, rb, true); }
|
||||
void MTFSB1(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00004c, ra, rt, rb, false); }
|
||||
void MTFSB1_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00004c, ra, rt, rb, true); }
|
||||
void MTFSF() { emit_XFL(0xfc00058e); }
|
||||
void MTFSFI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00010c, ra, rt, rb, false); }
|
||||
void MTFSFI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00010c, ra, rt, rb, true); }
|
||||
void MTMSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000124, ra, rt, rb, false); }
|
||||
void MTMSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000124, ra, rt, rb, true); }
|
||||
void MTSPR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003a6, ra, rt, rb, false); }
|
||||
void MTSPR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003a6, ra, rt, rb, true); }
|
||||
void MTSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001a4, ra, rt, rb, false); }
|
||||
void MTSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001a4, ra, rt, rb, true); }
|
||||
void MTSRIN(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001e4, ra, rt, rb, false); }
|
||||
void MTSRIN_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001e4, ra, rt, rb, true); }
|
||||
void MULHD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, false, false); }
|
||||
void MULHDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, true, false); }
|
||||
void MULHD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, false, true); }
|
||||
void MULHDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, true, true); }
|
||||
void MULHDU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, false, false); }
|
||||
void MULHDUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, true, false); }
|
||||
void MULHDU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, false, true); }
|
||||
void MULHDUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, true, true); }
|
||||
void MULHW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, false, false); }
|
||||
void MULHWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, true, false); }
|
||||
void MULHW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, false, true); }
|
||||
void MULHWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, true, true); }
|
||||
void MULHWU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, false, false); }
|
||||
void MULHWUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, true, false); }
|
||||
void MULHWU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, false, true); }
|
||||
void MULHWUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, true, true); }
|
||||
void MULLD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, false, false); }
|
||||
void MULLDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, true, false); }
|
||||
void MULLD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, false, true); }
|
||||
void MULLDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, true, true); }
|
||||
void MULLI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x1c000000, rt, ra, d); }
|
||||
void MULLW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, false, false); }
|
||||
void MULLWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, true, false); }
|
||||
void MULLW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, false, true); }
|
||||
void MULLWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, true, true); }
|
||||
void NAND(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003b8, ra, rt, rb, false); }
|
||||
void NAND_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003b8, ra, rt, rb, true); }
|
||||
void NEG(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, false, false); }
|
||||
void NEGO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, true, false); }
|
||||
void NEG_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, false, true); }
|
||||
void NEGO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, true, true); }
|
||||
void NOR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000f8, ra, rt, rb, false); }
|
||||
void NOR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000f8, ra, rt, rb, true); }
|
||||
void OR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000378, ra, rt, rb, false); }
|
||||
void OR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000378, ra, rt, rb, true); }
|
||||
void ORC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000338, ra, rt, rb, false); }
|
||||
void ORC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000338, ra, rt, rb, true); }
|
||||
void ORI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x60000000, ra, rt, d); }
|
||||
void ORIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x64000000, ra, rt, d); }
|
||||
void RFI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c000064, ra, rt, rb, false); }
|
||||
void RFI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c000064, ra, rt, rb, true); }
|
||||
void RLDCL(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000010, ra, rs, rb, mb, false); }
|
||||
void RLDCL_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000010, ra, rs, rb, mb, true); }
|
||||
void RLDCR(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000012, ra, rs, rb, mb, false); }
|
||||
void RLDCR_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000012, ra, rs, rb, mb, true); }
|
||||
void RLDIC(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000008, ra, rs, mb, sh, false); }
|
||||
void RLDIC_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000008, ra, rs, mb, sh, true); }
|
||||
void RLDICL(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000000, ra, rs, mb, sh, false); }
|
||||
void RLDICL_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000000, ra, rs, mb, sh, true); }
|
||||
void RLDICR(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000004, ra, rs, mb, sh, false); }
|
||||
void RLDICR_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000004, ra, rs, mb, sh, true); }
|
||||
void RLDIMI(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x7800000c, ra, rs, mb, sh, false); }
|
||||
void RLDIMI_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x7800000c, ra, rs, mb, sh, true); }
|
||||
void RLWIMI(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x50000000, ra, rs, sh, mb, me, false); }
|
||||
void RLWIMI_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x50000000, ra, rs, sh, mb, me, true); }
|
||||
void RLWINM(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x54000000, ra, rs, sh, mb, me, false); }
|
||||
void RLWINM_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x54000000, ra, rs, sh, mb, me, true); }
|
||||
void RLWNM(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) { emit_M(0x5c000000, ra, rs, rb.index, mb, me, false); }
|
||||
void RLWNM_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) { emit_M(0x5c000000, ra, rs, rb.index, mb, me, true); }
|
||||
void SC(uint32_t lev) { emit_SC(0x44000000, lev); }
|
||||
void SI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x30000000, rt, ra, d); }
|
||||
void SI_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x34000000, rt, ra, d); }
|
||||
void SLBIA(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003e4, ra, rt, rb, false); }
|
||||
void SLBIA_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003e4, ra, rt, rb, true); }
|
||||
void SLBIE(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000364, ra, rt, rb, false); }
|
||||
void SLBIE_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000364, ra, rt, rb, true); }
|
||||
void SLD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000036, ra, rt, rb, false); }
|
||||
void SLD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000036, ra, rt, rb, true); }
|
||||
void SLW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000030, ra, rt, rb, false); }
|
||||
void SLW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000030, ra, rt, rb, true); }
|
||||
void SRAD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000634, ra, rt, rb, false); }
|
||||
void SRAD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000634, ra, rt, rb, true); }
|
||||
void SRADI(GPR const rt, GPR const ra, uint32_t sh) { emit_XS(0x7c000674, rt, ra, sh, false); }
|
||||
void SRADI_(GPR const rt, GPR const ra, uint32_t sh) { emit_XS(0x7c000674, rt, ra, sh, true); }
|
||||
void SRD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000436, ra, rt, rb, false); }
|
||||
void SRD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000436, ra, rt, rb, true); }
|
||||
void SRAW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000630, ra, rt, rb, false); }
|
||||
void SRAW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000630, ra, rt, rb, true); }
|
||||
void SRAWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000670, ra, rt, rb, false); }
|
||||
void SRAWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000670, ra, rt, rb, true); }
|
||||
void SRW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000430, ra, rt, rb, false); }
|
||||
void SRW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000430, ra, rt, rb, true); }
|
||||
void STB(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x98000000, rt, ra, d); }
|
||||
void STBU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x9c000000, rt, ra, d); }
|
||||
void STBUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ee, ra, rt, rb, false); }
|
||||
void STBUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ee, ra, rt, rb, true); }
|
||||
void STBX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ae, ra, rt, rb, false); }
|
||||
void STBX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ae, ra, rt, rb, true); }
|
||||
void STD(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xf8000000, rt, ra, d >> 2); }
|
||||
void STDCX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ac, ra, rt, rb, false); }
|
||||
void STDCX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ac, ra, rt, rb, true); }
|
||||
void STDU(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xf8000001, rt, ra, d >> 2); }
|
||||
void STDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016a, ra, rt, rb, false); }
|
||||
void STDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016a, ra, rt, rb, true); }
|
||||
void STDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012a, ra, rt, rb, false); }
|
||||
void STDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012a, ra, rt, rb, true); }
|
||||
void STFD(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd8000000, rt, ra, d); }
|
||||
void STFDU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xdc000000, rt, ra, d); }
|
||||
void STFDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ee, ra, rt, rb, false); }
|
||||
void STFDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ee, ra, rt, rb, true); }
|
||||
void STFDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ae, ra, rt, rb, false); }
|
||||
void STFDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ae, ra, rt, rb, true); }
|
||||
void STFIWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ae, ra, rt, rb, false); }
|
||||
void STFIWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ae, ra, rt, rb, true); }
|
||||
void STFS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd0000000, rt, ra, d); }
|
||||
void STFSU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd4000000, rt, ra, d); }
|
||||
void STFSUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00056e, ra, rt, rb, false); }
|
||||
void STFSUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00056e, ra, rt, rb, true); }
|
||||
void STFSX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052e, ra, rt, rb, false); }
|
||||
void STFSX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052e, ra, rt, rb, true); }
|
||||
void STH(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb0000000, rt, ra, d); }
|
||||
void STHBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00072c, ra, rt, rb, false); }
|
||||
void STHBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00072c, ra, rt, rb, true); }
|
||||
void STHU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb4000000, rt, ra, d); }
|
||||
void STHUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036e, ra, rt, rb, false); }
|
||||
void STHUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036e, ra, rt, rb, true); }
|
||||
void STHX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00032e, ra, rt, rb, false); }
|
||||
void STHX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00032e, ra, rt, rb, true); }
|
||||
void STMW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xbc000000, rt, ra, d); }
|
||||
void STSWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005aa, ra, rt, rb, false); }
|
||||
void STSWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005aa, ra, rt, rb, true); }
|
||||
void STSWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052a, ra, rt, rb, false); }
|
||||
void STSWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052a, ra, rt, rb, true); }
|
||||
void STW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x90000000, rt, ra, d); }
|
||||
void STWBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052c, ra, rt, rb, false); }
|
||||
void STWBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052c, ra, rt, rb, true); }
|
||||
void STWCX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012c, ra, rt, rb, false); }
|
||||
void STWCX__(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012c, ra, rt, rb, true); }
|
||||
void STWU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x94000000, rt, ra, d); }
|
||||
void STWUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016e, ra, rt, rb, false); }
|
||||
void STWUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016e, ra, rt, rb, true); }
|
||||
void STWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012e, ra, rt, rb, false); }
|
||||
void STWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012e, ra, rt, rb, true); }
|
||||
void SUBF(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, false, false); }
|
||||
void SUBFO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, true, false); }
|
||||
void SUBF_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, false, true); }
|
||||
void SUBFO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, true, true); }
|
||||
void SUBFC(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, false, false); }
|
||||
void SUBFCO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, true, false); }
|
||||
void SUBFC_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, false, true); }
|
||||
void SUBFCO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, true, true); }
|
||||
void SUBFE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, false, false); }
|
||||
void SUBFEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, true, false); }
|
||||
void SUBFE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, false, true); }
|
||||
void SUBFEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, true, true); }
|
||||
void SUBFIC(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x20000000, rt, ra, d); }
|
||||
void SUBFME(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, false, false); }
|
||||
void SUBFMEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, true, false); }
|
||||
void SUBFME_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, false, true); }
|
||||
void SUBFMEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, true, true); }
|
||||
void SUBFZE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, false, false); }
|
||||
void SUBFZEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, true, false); }
|
||||
void SUBFZE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, false, true); }
|
||||
void SUBFZEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, true, true); }
|
||||
void SYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ac, ra, rt, rb, false); }
|
||||
void SYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ac, ra, rt, rb, true); }
|
||||
void TD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000088, ra, rt, rb, false); }
|
||||
void TD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000088, ra, rt, rb, true); }
|
||||
void TDI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x08000000, rt, ra, d); }
|
||||
void TLBIE(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000264, ra, rt, rb, false); }
|
||||
void TLBIE_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000264, ra, rt, rb, true); }
|
||||
void TLBSYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046c, ra, rt, rb, false); }
|
||||
void TLBSYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046c, ra, rt, rb, true); }
|
||||
void TW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000008, ra, rt, rb, false); }
|
||||
void TW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000008, ra, rt, rb, true); }
|
||||
void TWI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x0c000000, rt, ra, d); }
|
||||
void XOR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000278, ra, rt, rb, false); }
|
||||
void XOR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000278, ra, rt, rb, true); }
|
||||
void XORI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x68000000, ra, rt, d); }
|
||||
void XORIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x6c000000, ra, rt, d); }
|
||||
Vendored
-340
@@ -1,340 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <sys/mman.h>
|
||||
#include "powah_emit.hpp"
|
||||
|
||||
#define EB32(X) __bswap32(X)
|
||||
|
||||
TEST_CASE("ppc64: addi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
|
||||
ctx.ADDI(powah::R2, powah::R2, 0);
|
||||
ctx.ADDIS(powah::R2, powah::R12, 0);
|
||||
REQUIRE(data[0] == EB32(0x00004238));
|
||||
REQUIRE(data[1] == EB32(0x00004c3c));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: std/mr", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
|
||||
ctx.STD(powah::R26, powah::R1, 144);
|
||||
ctx.MR(powah::R26, powah::R4);
|
||||
ctx.STD(powah::R30, powah::R1, 176);
|
||||
ctx.MR(powah::R30, powah::R3);
|
||||
ctx.RLDICR(powah::R4, powah::R5, 25, 6);
|
||||
ctx.STD(powah::R28, powah::R1, 160);
|
||||
REQUIRE(data[0] == EB32(0x900041fb));
|
||||
REQUIRE(data[1] == EB32(0x78239a7c));
|
||||
REQUIRE(data[2] == EB32(0xb000c1fb));
|
||||
REQUIRE(data[3] == EB32(0x781b7e7c));
|
||||
REQUIRE(data[4] == EB32(0x84c9a478));
|
||||
REQUIRE(data[5] == EB32(0xa00081fb));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: sldi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
|
||||
ctx.SLDI(powah::R3, powah::R5, 37);
|
||||
REQUIRE(data[0] == EB32(0x862ea378));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: rldicr", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.RLDICR(powah::R1, powah::R1, 0, 0);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 1, 0);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 0, 1);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 1, 1);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 31, 31);
|
||||
REQUIRE(data[0] == EB32(0x04002178));
|
||||
REQUIRE(data[1] == EB32(0x04082178));
|
||||
REQUIRE(data[2] == EB32(0x44002178));
|
||||
REQUIRE(data[3] == EB32(0x44082178));
|
||||
REQUIRE(data[4] == EB32(0xc4ff2178));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: mr/or/std", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.MR(powah::R27, powah::R7);
|
||||
ctx.MR(powah::R29, powah::R6);
|
||||
ctx.OR(powah::R3, powah::R3, powah::R4);
|
||||
ctx.MR(powah::R4, powah::R28);
|
||||
ctx.CRMOVE(powah::CPR{8}, powah::CPR{1});
|
||||
ctx.MR(powah::R25, powah::R5);
|
||||
ctx.OR(powah::R3, powah::R3, powah::R26);
|
||||
ctx.STD(powah::R3, powah::R1, 56);
|
||||
ctx.MR(powah::R3, powah::R30);
|
||||
REQUIRE(data[0] == EB32(0x783bfb7c));
|
||||
REQUIRE(data[1] == EB32(0x7833dd7c));
|
||||
REQUIRE(data[2] == EB32(0x7823637c));
|
||||
REQUIRE(data[3] == EB32(0x78e3847f));
|
||||
REQUIRE(data[4] == EB32(0x820b014d));
|
||||
REQUIRE(data[5] == EB32(0x782bb97c));
|
||||
REQUIRE(data[6] == EB32(0x78d3637c));
|
||||
REQUIRE(data[7] == EB32(0x380061f8));
|
||||
REQUIRE(data[8] == EB32(0x78f3c37f));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: ld/crand+addi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.LD(powah::R4, powah::R1, 72);
|
||||
ctx.LWZ(powah::R5, powah::R1, 80);
|
||||
ctx.CRAND(powah::CPR{20}, powah::CPR{10}, powah::CPR{9});
|
||||
ctx.ADDI(powah::R6, powah::R4, 4);
|
||||
ctx.ANDIS_(powah::R4, powah::R5, 1992);
|
||||
ctx.LD(powah::R5, powah::R30, 1984);
|
||||
ctx.LBZ(powah::R3, powah::R1, 84);
|
||||
ctx.CLRLDI(powah::R26, powah::R6, 8);
|
||||
ctx.STW(powah::R4, powah::R1, 80);
|
||||
ctx.ADDI(powah::R5, powah::R5, 1);
|
||||
ctx.STD(powah::R26, powah::R1, 72);
|
||||
ctx.STD(powah::R5, powah::R30, 1984);
|
||||
REQUIRE(data[0] == EB32(0x480081e8));
|
||||
REQUIRE(data[1] == EB32(0x5000a180));
|
||||
REQUIRE(data[2] == EB32(0x024a8a4e));
|
||||
REQUIRE(data[3] == EB32(0x0400c438));
|
||||
REQUIRE(data[4] == EB32(0xc807a474));
|
||||
REQUIRE(data[5] == EB32(0xc007bee8));
|
||||
REQUIRE(data[6] == EB32(0x54006188));
|
||||
REQUIRE(data[7] == EB32(0x0002da78));
|
||||
REQUIRE(data[8] == EB32(0x50008190));
|
||||
REQUIRE(data[9] == EB32(0x0100a538));
|
||||
REQUIRE(data[10] == EB32(0x480041fb));
|
||||
REQUIRE(data[11] == EB32(0xc007bef8));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: rotldi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 16);
|
||||
ctx.ROTLDI(powah::R4, powah::R3, 8);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 8, 48);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 24);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 16, 40);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 32);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 24, 32);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 48);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 40, 16);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 56);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 48, 8);
|
||||
ctx.RLDIMI(powah::R4, powah::R3, 56, 0);
|
||||
ctx.MR(powah::R3, powah::R4);
|
||||
REQUIRE(data[0] == EB32(0x00806578));
|
||||
REQUIRE(data[1] == EB32(0x00406478));
|
||||
REQUIRE(data[2] == EB32(0x2c44a478));
|
||||
REQUIRE(data[3] == EB32(0x00c06578));
|
||||
REQUIRE(data[4] == EB32(0x2c82a478));
|
||||
REQUIRE(data[5] == EB32(0x02006578));
|
||||
REQUIRE(data[6] == EB32(0x2cc0a478));
|
||||
REQUIRE(data[7] == EB32(0x02806578));
|
||||
REQUIRE(data[8] == EB32(0x0e44a478));
|
||||
REQUIRE(data[9] == EB32(0x02c06578));
|
||||
REQUIRE(data[10] == EB32(0x0e82a478));
|
||||
REQUIRE(data[11] == EB32(0x0ec06478));
|
||||
REQUIRE(data[12] == EB32(0x7823837c));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: branch-backwards", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
powah::Label l_3 = ctx.DefineLabel();
|
||||
ctx.LABEL(l_3);
|
||||
ctx.ADD(powah::R1, powah::R2, powah::R3);
|
||||
ctx.B(l_3);
|
||||
ctx.ApplyRelocs();
|
||||
REQUIRE(data[0] == EB32(0x141a227c));
|
||||
REQUIRE(data[1] == EB32(0xfcffff4b));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: rlwimi madness", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.ROTLWI(powah::R10, powah::R3, 24);
|
||||
ctx.SRDI(powah::R9, powah::R3, 32);
|
||||
ctx.RLWIMI(powah::R10, powah::R3, 8, 8, 15);
|
||||
ctx.RLWIMI(powah::R10, powah::R3, 8, 24, 31);
|
||||
ctx.ROTLWI(powah::R3, powah::R9, 24);
|
||||
ctx.RLWIMI(powah::R3, powah::R9, 8, 8, 15);
|
||||
ctx.RLWIMI(powah::R3, powah::R9, 8, 24, 31);
|
||||
ctx.RLDIMI(powah::R3, powah::R10, 32, 0);
|
||||
REQUIRE(data[0] == EB32(0x3ec06a54));
|
||||
REQUIRE(data[1] == EB32(0x22006978));
|
||||
REQUIRE(data[2] == EB32(0x1e426a50));
|
||||
REQUIRE(data[3] == EB32(0x3e466a50));
|
||||
REQUIRE(data[4] == EB32(0x3ec02355));
|
||||
REQUIRE(data[5] == EB32(0x1e422351));
|
||||
REQUIRE(data[6] == EB32(0x3e462351));
|
||||
REQUIRE(data[7] == EB32(0x0e004379));
|
||||
}
|
||||
|
||||
/*
|
||||
0: 78 1b 68 7c mr 8, 3
|
||||
4: 38 28 83 7c and 3, 4, 5
|
||||
8: 74 00 6a 7c cntlzd 10, 3
|
||||
c: 76 06 69 7c sradi 9, 3, 32
|
||||
10: 82 d1 4a 79 rldicl 10, 10, 58, 6
|
||||
14: 00 00 29 55 rlwinm 9, 9, 0, 0, 0
|
||||
18: 64 f0 4a 79 sldi 10, 10, 30
|
||||
1c: 78 53 29 7d or 9, 9, 10
|
||||
20: 00 00 28 f9 std 9, 0(8)
|
||||
24: 20 00 80 4e blr
|
||||
*/
|
||||
TEST_CASE("ppc64: functor-2", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.MR(powah::R8, powah::R3);
|
||||
ctx.AND(powah::R3, powah::R4, powah::R5);
|
||||
ctx.CNTLZD(powah::R10, powah::R3);
|
||||
ctx.SRADI(powah::R9, powah::R3, 32);
|
||||
ctx.RLDICL(powah::R10, powah::R10, 58, 6);
|
||||
ctx.RLWINM(powah::R9, powah::R9, 0, 0, 0);
|
||||
ctx.SLDI(powah::R10, powah::R10, 30);
|
||||
ctx.OR(powah::R9, powah::R9, powah::R10);
|
||||
ctx.STD(powah::R9, powah::R8, 0);
|
||||
ctx.BLR();
|
||||
REQUIRE(data[0] == EB32(0x781b687c));
|
||||
REQUIRE(data[1] == EB32(0x3828837c));
|
||||
REQUIRE(data[2] == EB32(0x74006a7c));
|
||||
REQUIRE(data[3] == EB32(0x7606697c));
|
||||
REQUIRE(data[4] == EB32(0x82d14a79));
|
||||
REQUIRE(data[5] == EB32(0x00002955));
|
||||
REQUIRE(data[6] == EB32(0x64f04a79));
|
||||
REQUIRE(data[7] == EB32(0x7853297d));
|
||||
REQUIRE(data[8] == EB32(0x000028f9));
|
||||
REQUIRE(data[9] == EB32(0x2000804e));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: functor-1", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
powah::Label l_4 = ctx.DefineLabel();
|
||||
powah::Label l_7 = ctx.DefineLabel();
|
||||
ctx.CMPLD(powah::CR0, powah::R3, powah::R4);
|
||||
ctx.MR(powah::R9, powah::R3);
|
||||
ctx.BGT(powah::CR0, l_7);
|
||||
ctx.CMPLDI(powah::CR0, powah::R3, 3781);
|
||||
ctx.LI(powah::R3, 1);
|
||||
ctx.BGTLR(powah::CR0);
|
||||
ctx.CMPLD(powah::CR0, powah::R9, powah::R5);
|
||||
ctx.BLE(powah::CR0, l_4);
|
||||
ctx.SUBF(powah::R5, powah::R9, powah::R5);
|
||||
ctx.XOR(powah::R3, powah::R5, powah::R4);
|
||||
ctx.BLR();
|
||||
ctx.LABEL(l_4);
|
||||
ctx.ADD(powah::R5, powah::R4, powah::R5);
|
||||
ctx.ADD(powah::R3, powah::R5, powah::R9);
|
||||
ctx.BLR();
|
||||
ctx.LABEL(l_7);
|
||||
ctx.ADD(powah::R3, powah::R4, powah::R5);
|
||||
ctx.BLR();
|
||||
ctx.ApplyRelocs();
|
||||
REQUIRE(data[0] == EB32(0x4020237c));
|
||||
REQUIRE(data[1] == EB32(0x781b697c));
|
||||
REQUIRE(data[2] == EB32(0x30008141));
|
||||
REQUIRE(data[3] == EB32(0xc50e2328));
|
||||
REQUIRE(data[4] == EB32(0x01006038));
|
||||
REQUIRE(data[5] == EB32(0x2000814d));
|
||||
REQUIRE(data[6] == EB32(0x4028297c));
|
||||
REQUIRE(data[7] == EB32(0x10008140));
|
||||
REQUIRE(data[8] == EB32(0x5028a97c));
|
||||
REQUIRE(data[9] == EB32(0x7822a37c));
|
||||
REQUIRE(data[10] == EB32(0x2000804e));
|
||||
REQUIRE(data[11] == EB32(0x142aa47c));
|
||||
REQUIRE(data[12] == EB32(0x144a657c));
|
||||
REQUIRE(data[13] == EB32(0x2000804e));
|
||||
REQUIRE(data[14] == EB32(0x142a647c));
|
||||
REQUIRE(data[15] == EB32(0x2000804e));
|
||||
}
|
||||
|
||||
#if defined(ARCHITECTURE_powerpc64)
|
||||
/*
|
||||
0: d619637c mullw 3, 3, 3
|
||||
4: 20006378 clrldi 3, 3, 32
|
||||
8: 2000804e blr
|
||||
*/
|
||||
TEST_CASE("ppc64: live-exec square", "[ppc64]") {
|
||||
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
REQUIRE(data != nullptr);
|
||||
|
||||
powah::Context ctx((void*)data, 4096);
|
||||
ctx.MULLW(powah::R3, powah::R3, powah::R3);
|
||||
ctx.CLRLDI(powah::R3, powah::R3, 32);
|
||||
ctx.BLR();
|
||||
REQUIRE(data[0] == EB32(0xd619637c));
|
||||
REQUIRE(data[1] == EB32(0x20006378));
|
||||
REQUIRE(data[2] == EB32(0x2000804e));
|
||||
|
||||
auto* fn = (int (*)(int))data;
|
||||
REQUIRE(fn(4) == 4 * 4);
|
||||
REQUIRE(fn(8) == 8 * 8);
|
||||
|
||||
munmap((void*)data, 4096);
|
||||
}
|
||||
|
||||
/*
|
||||
int f(int a, int b, int c) {
|
||||
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
|
||||
return a * a + b ^ c & b;
|
||||
}
|
||||
*/
|
||||
TEST_CASE("ppc64: live-exec xoralu", "[ppc64]") {
|
||||
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
REQUIRE(data != nullptr);
|
||||
|
||||
powah::Context ctx((void*)data, 4096);
|
||||
powah::Label l_2 = ctx.DefineLabel();
|
||||
powah::Label l_3 = ctx.DefineLabel();
|
||||
ctx.CMPW(powah::R3, powah::R4);
|
||||
ctx.BGT(powah::CR0, l_2);
|
||||
ctx.ADDI(powah::R6, powah::R5, 402);
|
||||
ctx.B(l_3);
|
||||
ctx.LABEL(l_2);
|
||||
ctx.ADDI(powah::R6, powah::R4, 1);
|
||||
ctx.ADDIS(powah::R6, powah::R6, -1);
|
||||
ctx.LABEL(l_3);
|
||||
ctx.ADDI(powah::R4, powah::R4, 2);
|
||||
ctx.ADD(powah::R3, powah::R6, powah::R3);
|
||||
ctx.MULLW(powah::R6, powah::R4, powah::R5);
|
||||
ctx.MULLW(powah::R3, powah::R3, powah::R3);
|
||||
ctx.AND(powah::R5, powah::R5, powah::R6);
|
||||
ctx.ADD(powah::R3, powah::R3, powah::R4);
|
||||
ctx.AND(powah::R4, powah::R5, powah::R4);
|
||||
ctx.XOR(powah::R3, powah::R3, powah::R4);
|
||||
ctx.EXTSW(powah::R3, powah::R3);
|
||||
ctx.BLR();
|
||||
ctx.ApplyRelocs();
|
||||
REQUIRE(data[0] == EB32(0x0020037c));
|
||||
REQUIRE(data[1] == EB32(0x0c008141));
|
||||
REQUIRE(data[2] == EB32(0x9201c538));
|
||||
REQUIRE(data[3] == EB32(0x0c000048));
|
||||
REQUIRE(data[4] == EB32(0x0100c438));
|
||||
REQUIRE(data[5] == EB32(0xffffc63c));
|
||||
REQUIRE(data[6] == EB32(0x02008438));
|
||||
REQUIRE(data[7] == EB32(0x141a667c));
|
||||
REQUIRE(data[8] == EB32(0xd629c47c));
|
||||
REQUIRE(data[9] == EB32(0xd619637c));
|
||||
REQUIRE(data[10] == EB32(0x3830a57c));
|
||||
REQUIRE(data[11] == EB32(0x1422637c));
|
||||
REQUIRE(data[12] == EB32(0x3820a47c));
|
||||
REQUIRE(data[13] == EB32(0x7822637c));
|
||||
REQUIRE(data[14] == EB32(0xb407637c));
|
||||
REQUIRE(data[15] == EB32(0x2000804e));
|
||||
|
||||
auto const orig = [](int a, int b, int c) {
|
||||
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
|
||||
return a * a + b ^ c & b;
|
||||
};
|
||||
auto* fn = (int (*)(int, int, int))data;
|
||||
REQUIRE(fn(0, 1, 2) == orig(0, 1, 2));
|
||||
REQUIRE(fn(6456, 4564, 4564561) == orig(6456, 4564, 4564561));
|
||||
|
||||
munmap((void*)data, 4096);
|
||||
}
|
||||
#endif
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
include_directories(.)
|
||||
|
||||
# Dynarmic
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64 OR ARCHITECTURE_powerpc64) AND NOT YUZU_STATIC_ROOM)
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
|
||||
add_subdirectory(dynarmic)
|
||||
add_library(dynarmic::dynarmic ALIAS dynarmic)
|
||||
endif()
|
||||
|
||||
@@ -539,6 +539,40 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun installKeys(path: String, ext: String): Int
|
||||
|
||||
/**
|
||||
* @return Whether this GPU can run the Lossless Scaling frame generation shaders,
|
||||
* which are built against the Vulkan memory model.
|
||||
*/
|
||||
external fun supportsFrameGeneration(): Boolean
|
||||
|
||||
/**
|
||||
* @return Path the user-supplied Lossless Scaling library is expected at.
|
||||
*/
|
||||
external fun getLosslessDllPath(): String
|
||||
|
||||
/**
|
||||
* Parses the installed Lossless Scaling library and checks that every shader the
|
||||
* frame generation chain needs is present.
|
||||
*
|
||||
* @return The result code, matching the losslessDllResults array.
|
||||
*/
|
||||
external fun validateLosslessDll(): Int
|
||||
|
||||
/**
|
||||
* Translates the frame generation shaders out of the installed Lossless Scaling library
|
||||
* and writes them to the SPIR-V cache. Slow, so call it off the main thread.
|
||||
*
|
||||
* @return The result code, matching the losslessDllResults array.
|
||||
*/
|
||||
external fun prepareLosslessDll(): Int
|
||||
|
||||
/**
|
||||
* Deletes the installed Lossless Scaling library.
|
||||
*
|
||||
* @return Whether the library is gone after the call.
|
||||
*/
|
||||
external fun removeLosslessDll(): Boolean
|
||||
|
||||
/**
|
||||
* Checks the PatchManager for any addons that are available
|
||||
*
|
||||
|
||||
@@ -559,6 +559,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
|
||||
private fun enableFullscreenImmersive() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
window.decorView.systemUiVisibility =
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
|
||||
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars())
|
||||
controller.systemBarsBehavior =
|
||||
|
||||
+4
@@ -37,6 +37,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_PATCH_OLD_QCOM_DRIVERS("patch_old_qcom_drivers"),
|
||||
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
|
||||
RENDERER_SAMPLE_SHADING("sample_shading"),
|
||||
RENDERER_FRAME_GEN("frame_gen"),
|
||||
RENDERER_FRAME_GEN_FP16("frame_gen_fp16"),
|
||||
RENDERER_FRAME_GEN_FLOW_SCALE_AUTO("frame_gen_flow_scale_auto"),
|
||||
RENDERER_FRAME_GEN_DUMP_FLOW("frame_gen_dump_flow"),
|
||||
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
|
||||
PICTURE_IN_PICTURE("picture_in_picture"),
|
||||
USE_CUSTOM_RTC("custom_rtc_enabled"),
|
||||
|
||||
@@ -19,6 +19,10 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
RENDERER_ASTC_DECODE_METHOD("accelerate_astc"),
|
||||
RENDERER_ACCURACY("gpu_accuracy"),
|
||||
RENDERER_RESOLUTION("resolution_setup"),
|
||||
RENDERER_FRAME_GEN_MULTIPLIER("frame_gen_multiplier"),
|
||||
RENDERER_FRAME_GEN_TARGET_RATE("frame_gen_target_rate"),
|
||||
RENDERER_FRAME_GEN_QUEUE_TARGET("frame_gen_queue_target"),
|
||||
RENDERER_FRAME_GEN_FLOW_SCALE("frame_gen_flow_scale"),
|
||||
RENDERER_VSYNC("use_vsync"),
|
||||
RENDERER_SCALING_FILTER("scaling_filter"),
|
||||
RENDERER_ANTI_ALIASING("anti_aliasing"),
|
||||
|
||||
@@ -11,6 +11,7 @@ object Settings {
|
||||
SECTION_ROOT(R.string.advanced_settings),
|
||||
SECTION_SYSTEM(R.string.preferences_system),
|
||||
SECTION_RENDERER(R.string.preferences_graphics),
|
||||
SECTION_FRAME_GEN(R.string.frame_gen),
|
||||
SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options),
|
||||
SECTION_INPUT_OVERLAY(R.string.input_overlay_options),
|
||||
SECTION_SOC_OVERLAY(R.string.soc_overlay_options),
|
||||
|
||||
+104
@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.network.NetDataValidators
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
|
||||
/**
|
||||
@@ -65,6 +66,19 @@ abstract class SettingsItem(
|
||||
return NativeLibrary.isFirmwareAvailable()
|
||||
}
|
||||
|
||||
if (setting.key in frameGenKeys &&
|
||||
!(LosslessScalingHelper.isInstalled() && LosslessScalingHelper.isSupportedByGpu())
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// A frame rate target moves the multiplier on its own
|
||||
if (setting.key == IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key &&
|
||||
frameGenTargetRate != 0
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Can't edit settings that aren't saveable in per-game config even if they are switchable
|
||||
if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
|
||||
return false
|
||||
@@ -88,7 +102,31 @@ abstract class SettingsItem(
|
||||
val clearable: Boolean
|
||||
get() = !setting.global && NativeConfig.isPerGameConfigLoaded()
|
||||
|
||||
private val frameGenTargetRate: Int
|
||||
get() {
|
||||
val key = IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key
|
||||
val needsGlobal = if (NativeLibrary.isRunning() &&
|
||||
!NativeConfig.isPerGameConfigLoaded()
|
||||
) {
|
||||
!NativeConfig.usingGlobal(key)
|
||||
} else {
|
||||
NativeConfig.usingGlobal(key)
|
||||
}
|
||||
return IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt(needsGlobal)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val frameGenKeys = setOf(
|
||||
BooleanSetting.RENDERER_FRAME_GEN.key,
|
||||
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key,
|
||||
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key,
|
||||
IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key,
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key,
|
||||
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key,
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FP16.key,
|
||||
BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key
|
||||
)
|
||||
|
||||
const val TYPE_HEADER = 0
|
||||
const val TYPE_SWITCH = 1
|
||||
const val TYPE_SINGLE_CHOICE = 2
|
||||
@@ -586,6 +624,7 @@ abstract class SettingsItem(
|
||||
IntSetting.FSR_SHARPENING_SLIDER,
|
||||
titleId = R.string.fsr_sharpness,
|
||||
descriptionId = R.string.fsr_sharpness_description,
|
||||
max = 200,
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
@@ -607,6 +646,71 @@ abstract class SettingsItem(
|
||||
valuesId = R.array.rendererAntiAliasingValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_FRAME_GEN,
|
||||
titleId = R.string.frame_gen,
|
||||
descriptionId = R.string.frame_gen_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER,
|
||||
titleId = R.string.frame_gen_multiplier,
|
||||
descriptionId = R.string.frame_gen_multiplier_description,
|
||||
choicesId = R.array.frameGenMultiplierNames,
|
||||
valuesId = R.array.frameGenMultiplierValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE,
|
||||
titleId = R.string.frame_gen_target_rate,
|
||||
descriptionId = R.string.frame_gen_target_rate_description,
|
||||
choicesId = R.array.frameGenTargetRateNames,
|
||||
valuesId = R.array.frameGenTargetRateValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET,
|
||||
titleId = R.string.frame_gen_queue_target,
|
||||
descriptionId = R.string.frame_gen_queue_target_description,
|
||||
choicesId = R.array.frameGenQueueTargetNames,
|
||||
valuesId = R.array.frameGenQueueTargetValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO,
|
||||
titleId = R.string.frame_gen_flow_scale_auto,
|
||||
descriptionId = R.string.frame_gen_flow_scale_auto_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SliderSetting(
|
||||
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE,
|
||||
titleId = R.string.frame_gen_flow_scale,
|
||||
descriptionId = R.string.frame_gen_flow_scale_description,
|
||||
min = 25,
|
||||
max = 100,
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FP16,
|
||||
titleId = R.string.frame_gen_fp16,
|
||||
descriptionId = R.string.frame_gen_fp16_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW,
|
||||
titleId = R.string.frame_gen_dump_flow,
|
||||
descriptionId = R.string.frame_gen_dump_flow_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.RENDERER_SCREEN_LAYOUT,
|
||||
|
||||
+3
-1
@@ -382,7 +382,9 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
||||
}
|
||||
scSetting.setSelectedValue(value)
|
||||
|
||||
if (scSetting.setting.key == IntSetting.RENDERER_SCALING_FILTER.key) {
|
||||
if (scSetting.setting.key == IntSetting.RENDERER_SCALING_FILTER.key ||
|
||||
scSetting.setting.key == IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key
|
||||
) {
|
||||
settingsViewModel.setShouldReloadSettingsList(true)
|
||||
}
|
||||
|
||||
|
||||
+38
-1
@@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||
@@ -76,6 +77,41 @@ class SettingsFragmentPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFrameGenSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
if (!LosslessScalingHelper.isSupportedByGpu()) {
|
||||
add(
|
||||
RunnableSetting(
|
||||
titleId = R.string.frame_gen_unsupported,
|
||||
descriptionId = R.string.frame_gen_unsupported_description,
|
||||
isRunnable = false
|
||||
) {}
|
||||
)
|
||||
} else if (!LosslessScalingHelper.isInstalled()) {
|
||||
add(
|
||||
RunnableSetting(
|
||||
titleId = R.string.lossless_scaling_missing,
|
||||
descriptionId = R.string.lossless_scaling_missing_description,
|
||||
isRunnable = false
|
||||
) {}
|
||||
)
|
||||
}
|
||||
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN.key)
|
||||
add(IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key)
|
||||
add(IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key)
|
||||
add(IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key)
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key)
|
||||
if (!BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(
|
||||
getNeedsGlobalForKey(BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key)
|
||||
)
|
||||
) {
|
||||
add(IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key)
|
||||
}
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN_FP16.key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSharpnessScalingFilterSelected(): Boolean {
|
||||
val needsGlobal = getNeedsGlobalForKey(IntSetting.RENDERER_SCALING_FILTER.key)
|
||||
val selectedFilter = IntSetting.RENDERER_SCALING_FILTER.getInt(needsGlobal)
|
||||
@@ -120,6 +156,7 @@ class SettingsFragmentPresenter(
|
||||
MenuTag.SECTION_ROOT -> addConfigSettings(sl)
|
||||
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||
MenuTag.SECTION_FRAME_GEN -> addFrameGenSettings(sl)
|
||||
MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl)
|
||||
MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl)
|
||||
MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
|
||||
@@ -302,7 +339,6 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
||||
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
||||
add(BooleanSetting.EMULATE_BGR565.key)
|
||||
add(BooleanSetting.RESCALE_HACK.key)
|
||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
||||
@@ -1296,6 +1332,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.DUMP_GUEST_SHADERS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.DUMP_MACROS.key)
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
|
||||
+2
@@ -29,6 +29,7 @@ enum class SettingsSubscreen {
|
||||
DRIVER_MANAGER,
|
||||
DRIVER_FETCHER,
|
||||
FREEDRENO_SETTINGS,
|
||||
LOSSLESS_MANAGER,
|
||||
APPLET_LAUNCHER,
|
||||
INSTALLABLE,
|
||||
GAME_FOLDERS,
|
||||
@@ -126,6 +127,7 @@ class SettingsSubscreenActivity : AppCompatActivity() {
|
||||
SettingsSubscreen.DRIVER_MANAGER -> R.id.driverManagerFragment
|
||||
SettingsSubscreen.DRIVER_FETCHER -> R.id.driverFetcherFragment
|
||||
SettingsSubscreen.FREEDRENO_SETTINGS -> R.id.freedrenoSettingsFragment
|
||||
SettingsSubscreen.LOSSLESS_MANAGER -> R.id.losslessManagerFragment
|
||||
SettingsSubscreen.APPLET_LAUNCHER -> R.id.appletLauncherFragment
|
||||
SettingsSubscreen.INSTALLABLE -> R.id.installableFragment
|
||||
SettingsSubscreen.GAME_FOLDERS -> R.id.gameFoldersFragment
|
||||
|
||||
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
container,
|
||||
IntSetting.FSR_SHARPENING_SLIDER,
|
||||
minValue = 0,
|
||||
maxValue = 100,
|
||||
maxValue = 200,
|
||||
units = "%"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -369,6 +369,21 @@ class GamePropertiesFragment : Fragment() {
|
||||
)
|
||||
)
|
||||
}
|
||||
add(
|
||||
SubmenuProperty(
|
||||
R.string.frame_gen,
|
||||
R.string.frame_gen_per_game_description,
|
||||
R.drawable.ic_duck,
|
||||
action = {
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
args.game,
|
||||
Settings.MenuTag.SECTION_FRAME_GEN
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if (GpuDriverHelper.isAdrenoGpu()) {
|
||||
add(
|
||||
SubmenuProperty(
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||
|
||||
class HomeSettingsFragment : Fragment() {
|
||||
@@ -170,6 +171,24 @@ class HomeSettingsFragment : Fragment() {
|
||||
)
|
||||
)
|
||||
}
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.lossless_scaling,
|
||||
R.string.lossless_scaling_description,
|
||||
R.drawable.ic_duck,
|
||||
{
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
|
||||
SettingsSubscreen.LOSSLESS_MANAGER,
|
||||
null
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
},
|
||||
{ true },
|
||||
0,
|
||||
0,
|
||||
LosslessScalingHelper.statusText
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.multiplayer,
|
||||
@@ -337,6 +356,7 @@ class HomeSettingsFragment : Fragment() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
driverViewModel.updateDriverNameForGame(null)
|
||||
LosslessScalingHelper.refreshStatus()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentLosslessManagerBinding
|
||||
import org.yuzu.yuzu_emu.features.fetcher.SpacingItemDecoration
|
||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||
import org.yuzu.yuzu_emu.utils.collect
|
||||
|
||||
class LosslessManagerFragment : Fragment() {
|
||||
private var _binding: FragmentLosslessManagerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentLosslessManagerBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.toolbarLossless.setNavigationOnClickListener {
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
binding.losslessOptionsList.apply {
|
||||
layoutManager =
|
||||
GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
|
||||
addItemDecoration(
|
||||
SpacingItemDecoration(resources.getDimensionPixelSize(R.dimen.spacing_small))
|
||||
)
|
||||
}
|
||||
|
||||
LosslessScalingHelper.statusText.collect(viewLifecycleOwner) { refreshOptions() }
|
||||
|
||||
setInsets()
|
||||
}
|
||||
|
||||
private fun refreshOptions() {
|
||||
binding.losslessOptionsList.adapter = HomeSettingAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
viewLifecycleOwner,
|
||||
buildOptions()
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildOptions(): List<HomeSetting> {
|
||||
val installed = LosslessScalingHelper.isInstalled()
|
||||
return listOf(
|
||||
HomeSetting(
|
||||
if (installed) R.string.lossless_scaling_replace else R.string.lossless_scaling_install,
|
||||
if (installed) {
|
||||
R.string.lossless_scaling_replace_description
|
||||
} else {
|
||||
R.string.lossless_scaling_install_description
|
||||
},
|
||||
R.drawable.ic_install,
|
||||
{ dllPickerLauncher.launch(arrayOf("*/*")) },
|
||||
{ !NativeLibrary.isRunning() },
|
||||
R.string.lossless_scaling_locked,
|
||||
R.string.lossless_scaling_locked_description,
|
||||
LosslessScalingHelper.statusText
|
||||
),
|
||||
HomeSetting(
|
||||
R.string.lossless_scaling_remove,
|
||||
R.string.lossless_scaling_remove_description,
|
||||
R.drawable.ic_delete,
|
||||
{ confirmRemoval() },
|
||||
{ installed && !NativeLibrary.isRunning() },
|
||||
if (installed) {
|
||||
R.string.lossless_scaling_locked
|
||||
} else {
|
||||
R.string.lossless_scaling_remove_unavailable
|
||||
},
|
||||
if (installed) {
|
||||
R.string.lossless_scaling_locked_description
|
||||
} else {
|
||||
R.string.lossless_scaling_remove_unavailable_description
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun confirmRemoval() {
|
||||
MessageDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
titleId = R.string.lossless_scaling_remove,
|
||||
descriptionId = R.string.lossless_scaling_remove_confirmation,
|
||||
positiveButtonTitleId = R.string.lossless_scaling_remove,
|
||||
positiveAction = { LosslessScalingHelper.remove() },
|
||||
showNegativeButton = true,
|
||||
negativeAction = {}
|
||||
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private val dllPickerLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val resultStrings = resources.getStringArray(R.array.losslessDllResults)
|
||||
ProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.lossless_scaling_installing,
|
||||
false
|
||||
) { _, _ ->
|
||||
val installResult = LosslessScalingHelper.install(result)
|
||||
if (installResult == LosslessScalingHelper.RESULT_OK) {
|
||||
getString(R.string.lossless_scaling_install_success)
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId = R.string.lossless_scaling_install_failed,
|
||||
descriptionString = resultStrings[installResult]
|
||||
)
|
||||
}
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
binding.appbarLossless.updateMargins(
|
||||
left = barInsets.left + cutoutInsets.left,
|
||||
right = barInsets.right + cutoutInsets.right
|
||||
)
|
||||
|
||||
binding.scrollViewLossless.updatePadding(bottom = barInsets.bottom)
|
||||
|
||||
binding.losslessOptionsList.updatePadding(
|
||||
left = barInsets.left + cutoutInsets.left,
|
||||
right = barInsets.right + cutoutInsets.right
|
||||
)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
@@ -42,6 +42,7 @@ import org.yuzu.yuzu_emu.model.SetupPage
|
||||
import org.yuzu.yuzu_emu.model.PageState
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||
@@ -202,6 +203,24 @@ class SetupFragment : Fragment() {
|
||||
R.string.install_firmware_warning_help,
|
||||
)
|
||||
)
|
||||
add(
|
||||
PageButton(
|
||||
R.drawable.ic_duck,
|
||||
R.string.lossless_scaling,
|
||||
R.string.lossless_scaling_setup_description,
|
||||
{
|
||||
pageButtonCallback = it
|
||||
getLosslessDll.launch(arrayOf("*/*"))
|
||||
},
|
||||
{
|
||||
if (LosslessScalingHelper.isInstalled()) {
|
||||
ButtonState.BUTTON_ACTION_COMPLETE
|
||||
} else {
|
||||
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
PageButton(
|
||||
R.drawable.ic_controller,
|
||||
@@ -446,6 +465,32 @@ class SetupFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
val getLosslessDll =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val resultStrings = resources.getStringArray(R.array.losslessDllResults)
|
||||
ProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.lossless_scaling_installing,
|
||||
false
|
||||
) { _, _ ->
|
||||
val installResult = LosslessScalingHelper.install(result)
|
||||
if (installResult == LosslessScalingHelper.RESULT_OK) {
|
||||
getString(R.string.lossless_scaling_install_success)
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId = R.string.lossless_scaling_install_failed,
|
||||
descriptionString = resultStrings[installResult]
|
||||
)
|
||||
}
|
||||
}.apply {
|
||||
onDialogComplete = { checkForButtonState.invoke() }
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
val getGamesDirectory =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
if (result != null) {
|
||||
|
||||
+13
-1
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
@@ -72,6 +72,18 @@ class SystemInfoDialogFragment : DialogFragment() {
|
||||
|
||||
val vulkanDriver = NativeLibrary.getVulkanDriverVersion()
|
||||
appendLine("${getString(R.string.vulkan_driver_version)}: $vulkanDriver")
|
||||
|
||||
val frameGen = NativeLibrary.supportsFrameGeneration()
|
||||
appendLine(
|
||||
"${getString(R.string.frame_generation_support)}: " +
|
||||
getString(
|
||||
if (frameGen) {
|
||||
R.string.frame_generation_supported
|
||||
} else {
|
||||
R.string.frame_generation_unsupported
|
||||
}
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
appendLine("${getString(R.string.error_getting_emulator_info)}: ${e.message}")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.net.Uri
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import java.io.File
|
||||
|
||||
object LosslessScalingHelper {
|
||||
const val RESULT_OK = 0
|
||||
const val RESULT_NOT_INSTALLED = 1
|
||||
|
||||
private val _statusText = MutableStateFlow("")
|
||||
val statusText: StateFlow<String> = _statusText.asStateFlow()
|
||||
|
||||
private var installed: Boolean? = null
|
||||
private var gpuSupported: Boolean? = null
|
||||
|
||||
fun isInstalled(): Boolean = installed ?: refreshStatus()
|
||||
|
||||
fun isSupportedByGpu(): Boolean {
|
||||
val cached = gpuSupported
|
||||
if (cached != null) {
|
||||
return cached
|
||||
}
|
||||
val result = NativeLibrary.supportsFrameGeneration()
|
||||
gpuSupported = result
|
||||
return result
|
||||
}
|
||||
|
||||
fun refreshStatus(): Boolean {
|
||||
val result = NativeLibrary.validateLosslessDll() == RESULT_OK
|
||||
installed = result
|
||||
|
||||
val context = YuzuApplication.appContext
|
||||
_statusText.value = if (result) {
|
||||
context.getString(R.string.lossless_scaling_installed)
|
||||
} else {
|
||||
context.getString(R.string.lossless_scaling_not_installed)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun install(source: Uri): Int {
|
||||
val destination = File(NativeLibrary.getLosslessDllPath())
|
||||
destination.parentFile?.mkdirs()
|
||||
|
||||
val copied = FileUtil.copyUriToInternalStorage(
|
||||
source,
|
||||
destination.parent!!,
|
||||
destination.name
|
||||
)
|
||||
if (copied == null) {
|
||||
refreshStatus()
|
||||
return RESULT_NOT_INSTALLED
|
||||
}
|
||||
|
||||
val result = NativeLibrary.prepareLosslessDll()
|
||||
if (result != RESULT_OK) {
|
||||
NativeLibrary.removeLosslessDll()
|
||||
}
|
||||
refreshStatus()
|
||||
return result
|
||||
}
|
||||
|
||||
fun remove(): Boolean {
|
||||
val removed = NativeLibrary.removeLosslessDll()
|
||||
refreshStatus()
|
||||
return removed
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,8 @@ void AndroidConfig::SaveAllValues() {
|
||||
}
|
||||
|
||||
void AndroidConfig::ReadAndroidValues() {
|
||||
ReadAndroidUIValues();
|
||||
if (global) {
|
||||
ReadAndroidUIValues();
|
||||
ReadUIValues();
|
||||
BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
|
||||
Settings::values.ext_content_from_game_dirs = ReadBooleanSetting(
|
||||
@@ -223,8 +223,8 @@ void AndroidConfig::ReadAndroidControlValues() {
|
||||
}
|
||||
|
||||
void AndroidConfig::SaveAndroidValues() {
|
||||
SaveAndroidUIValues();
|
||||
if (global) {
|
||||
SaveAndroidUIValues();
|
||||
SaveUIValues();
|
||||
SaveOverlayValues();
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace AndroidSettings {
|
||||
&show_performance_overlay};
|
||||
|
||||
|
||||
Settings::Setting<s32> pipeline_worker_count{linkage, 4, "pipeline_worker_count",
|
||||
Settings::SwitchableSetting<s32> pipeline_worker_count{linkage, 2, "pipeline_worker_count",
|
||||
Settings::Category::Android,
|
||||
Settings::Specialization::Default,
|
||||
true,
|
||||
|
||||
@@ -124,9 +124,18 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
|
||||
return QuantizeFrameRateHint(verified_rate);
|
||||
}
|
||||
|
||||
float EmuWindow_Android::GetPresentedFrameMultiplier() {
|
||||
if (!Settings::values.frame_gen.GetValue()) {
|
||||
return 1.0f;
|
||||
}
|
||||
return static_cast<float>(std::clamp<u32>(Settings::values.frame_gen_multiplier.GetValue(), 2, 4));
|
||||
}
|
||||
|
||||
float EmuWindow_Android::GetFrameRateHint() const {
|
||||
const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f);
|
||||
const float frame_time_verified_hint = GetFrameTimeVerifiedHint();
|
||||
const float presented_multiplier = GetPresentedFrameMultiplier();
|
||||
const float observed_rate =
|
||||
std::clamp(m_smoothed_present_rate * presented_multiplier, 0.0f, 240.0f);
|
||||
const float frame_time_verified_hint = GetFrameTimeVerifiedHint() * presented_multiplier;
|
||||
|
||||
if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) {
|
||||
const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f);
|
||||
@@ -150,9 +159,9 @@ float EmuWindow_Android::GetFrameRateHint() const {
|
||||
return frame_time_verified_hint;
|
||||
}
|
||||
|
||||
constexpr float NominalFrameRate = 60.0f;
|
||||
const float nominal_rate = 60.0f * presented_multiplier;
|
||||
if (!Settings::values.use_speed_limit.GetValue()) {
|
||||
return NominalFrameRate;
|
||||
return QuantizeFrameRateHint(nominal_rate);
|
||||
}
|
||||
|
||||
const u16 speed_limit = Settings::SpeedLimit();
|
||||
@@ -161,7 +170,7 @@ float EmuWindow_Android::GetFrameRateHint() const {
|
||||
}
|
||||
|
||||
const float speed_limited_rate =
|
||||
NominalFrameRate * (static_cast<float>(std::min<u16>(speed_limit, 100)) / 100.0f);
|
||||
nominal_rate * (static_cast<float>(std::min<u16>(speed_limit, 100)) / 100.0f);
|
||||
return QuantizeFrameRateHint(speed_limited_rate);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ private:
|
||||
void UpdateObservedFrameRate();
|
||||
[[nodiscard]] float GetFrameRateHint() const;
|
||||
[[nodiscard]] float GetFrameTimeVerifiedHint() const;
|
||||
[[nodiscard]] static float GetPresentedFrameMultiplier();
|
||||
[[nodiscard]] static float QuantizeFrameRateHint(float frame_rate);
|
||||
|
||||
float m_window_width{};
|
||||
|
||||
@@ -44,6 +44,7 @@ extern "C" {
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/fs/fs_util.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scm_rev.h"
|
||||
@@ -90,6 +91,7 @@ extern "C" {
|
||||
#include "hid_core/hid_types.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
#include "jni/native.h"
|
||||
#include "video_core/frame_gen/lossless_dll.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "video_core/capture.h"
|
||||
@@ -998,7 +1000,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuSummary(JNIEnv* env, jobject
|
||||
FILE* f = std::fopen(CPUINFO_PATH, "r");
|
||||
if (!f) return Common::Android::ToJString(env, result);
|
||||
|
||||
char buf[512];
|
||||
char buf[4096];
|
||||
|
||||
if (f) {
|
||||
std::set<std::string> feature_set;
|
||||
@@ -1029,7 +1031,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuSummary(JNIEnv* env, jobject
|
||||
bool has_dotprod = feature_set.count("asimddp") || feature_set.count("dotprod");
|
||||
bool has_i8mm = feature_set.count("i8mm");
|
||||
bool has_bf16 = feature_set.count("bf16");
|
||||
bool has_fp16 = feature_set.count("fphp") || feature_set.count("asimdhp");
|
||||
bool has_atomics = feature_set.count("atomics") || feature_set.count("lse");
|
||||
bool has_lse2 = feature_set.count("uscat");
|
||||
bool has_rcpc = feature_set.count("lrcpc");
|
||||
bool has_rcpc2 = feature_set.count("ilrcpc");
|
||||
bool has_flagm = feature_set.count("flagm");
|
||||
bool has_flagm2 = feature_set.count("flagm2");
|
||||
|
||||
std::string features;
|
||||
if (has_neon || has_fp) {
|
||||
@@ -1037,6 +1045,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuSummary(JNIEnv* env, jobject
|
||||
if (has_dotprod) features += "+DP";
|
||||
if (has_i8mm) features += "+I8MM";
|
||||
if (has_bf16) features += "+BF16";
|
||||
if (has_fp16) features += "+FP16";
|
||||
}
|
||||
|
||||
if (has_sve) {
|
||||
@@ -1053,6 +1062,19 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuSummary(JNIEnv* env, jobject
|
||||
if (has_atomics) {
|
||||
if (!features.empty()) features += " | ";
|
||||
features += "LSE";
|
||||
if (has_lse2) features += "2";
|
||||
}
|
||||
|
||||
if (has_rcpc) {
|
||||
if (!features.empty()) features += " | ";
|
||||
features += "RCpc";
|
||||
if (has_rcpc2) features += "2";
|
||||
}
|
||||
|
||||
if (has_flagm) {
|
||||
if (!features.empty()) features += " | ";
|
||||
features += "FlagM";
|
||||
if (has_flagm2) features += "2";
|
||||
}
|
||||
|
||||
if (!features.empty()) {
|
||||
@@ -1090,6 +1112,34 @@ VkPhysicalDeviceProperties GetVulkanDeviceProperties() {
|
||||
const Vulkan::vk::PhysicalDevice physical_device(physical_devices[0], dld);
|
||||
return physical_device.GetProperties();
|
||||
}
|
||||
|
||||
bool GetVulkanMemoryModelSupport() {
|
||||
Common::DynamicLibrary library;
|
||||
if (!library.Open("libvulkan.so")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vulkan::vk::InstanceDispatch dld;
|
||||
const auto instance = Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1);
|
||||
const auto physical_devices = instance.EnumeratePhysicalDevices();
|
||||
if (physical_devices.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Vulkan::vk::PhysicalDevice physical_device(physical_devices[0], dld);
|
||||
|
||||
VkPhysicalDeviceVulkanMemoryModelFeatures memory_model{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
VkPhysicalDeviceFeatures2 features{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.pNext = &memory_model,
|
||||
};
|
||||
physical_device.GetFeatures2(features);
|
||||
|
||||
return memory_model.vulkanMemoryModel == VK_TRUE;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getVulkanDriverVersion(JNIEnv* env, jobject jobj) {
|
||||
@@ -1165,6 +1215,14 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getVulkanApiVersion(JNIEnv* env, j
|
||||
}
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_supportsFrameGeneration(JNIEnv* env, jobject jobj) {
|
||||
try {
|
||||
return static_cast<jboolean>(GetVulkanMemoryModelSupport());
|
||||
} catch (...) {
|
||||
return static_cast<jboolean>(false);
|
||||
}
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuModel(JNIEnv* env, jobject jobj) {
|
||||
const auto props = GetVulkanDeviceProperties();
|
||||
if (props.deviceID == 0) {
|
||||
@@ -1390,6 +1448,39 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_installKeys(JNIEnv* env, jclass clazz
|
||||
return static_cast<int>(FirmwareManager::InstallKeys(path, ext));
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getLosslessDllPath(JNIEnv* env, jclass clazz) {
|
||||
#ifdef HAS_LSFG
|
||||
const auto path = VideoCore::FrameGen::GetLosslessDllPath();
|
||||
return Common::Android::ToJString(env, Common::FS::PathToUTF8String(path));
|
||||
#else
|
||||
return Common::Android::ToJString(env, "");
|
||||
#endif
|
||||
}
|
||||
|
||||
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_validateLosslessDll(JNIEnv* env, jclass clazz) {
|
||||
#ifdef HAS_LSFG
|
||||
return static_cast<jint>(VideoCore::FrameGen::GetInstalledLosslessStatus());
|
||||
#else
|
||||
return static_cast<jint>(VideoCore::FrameGen::LosslessStatus::NotInstalled);
|
||||
#endif
|
||||
}
|
||||
|
||||
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_prepareLosslessDll(JNIEnv* env, jclass clazz) {
|
||||
#ifdef HAS_LSFG
|
||||
return static_cast<jint>(VideoCore::FrameGen::BuildShaderCache());
|
||||
#else
|
||||
return static_cast<jint>(VideoCore::FrameGen::LosslessStatus::NotInstalled);
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_removeLosslessDll(JNIEnv* env, jclass clazz) {
|
||||
#ifdef HAS_LSFG
|
||||
return static_cast<jboolean>(VideoCore::FrameGen::RemoveInstalledLosslessDll());
|
||||
#else
|
||||
return static_cast<jboolean>(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
|
||||
jstring jpath,
|
||||
jstring jprogramId) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M11,10.6a8,5.2 0 1,0 0,10.4a8,5.2 0 1,0 0,-10.4z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M4.6,12.6L0.8,10.2L3.2,15.5z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M15.3,3.5a4,4 0 1,0 0,8a4,4 0 1,0 0,-8zM16.6,4.9a1,1 0 1,0 0,2a1,1 0 1,0 0,-2z" />
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M18.6,6.5L23.2,7.8L18.6,9.3z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_lossless"
|
||||
style="@style/Widget.Eden.TransparentTopAppBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_lossless"
|
||||
style="@style/Widget.Eden.TransparentTopToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
app:navigationIcon="@drawable/ic_back"
|
||||
app:title="@string/lossless_scaling" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/scroll_view_lossless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:clipToPadding="false"
|
||||
android:defaultFocusHighlightEnabled="false"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar_lossless">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/lossless_options_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -47,6 +47,12 @@
|
||||
android:defaultValue="@null" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/losslessManagerFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.LosslessManagerFragment"
|
||||
android:label="@string/lossless_scaling"
|
||||
tools:layout="@layout/fragment_lossless_manager" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/appletLauncherFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.AppletLauncherFragment"
|
||||
|
||||
@@ -1088,7 +1088,6 @@
|
||||
<string name="theme_mode_light">فاتح</string>
|
||||
<string name="theme_mode_dark">داكن</string>
|
||||
|
||||
<string name="multiplier_none">لا شيء</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">خلفيات سوداء</string>
|
||||
|
||||
@@ -1004,7 +1004,6 @@ Wirklich fortfahren?</string>
|
||||
<string name="theme_mode_light">Hell</string>
|
||||
<string name="theme_mode_dark">Dunkel</string>
|
||||
|
||||
<string name="multiplier_none">Keine</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Schwarze Hintergründe</string>
|
||||
|
||||
@@ -1080,7 +1080,6 @@
|
||||
<string name="theme_mode_light">Claro</string>
|
||||
<string name="theme_mode_dark">Oscuro</string>
|
||||
|
||||
<string name="multiplier_none">Nada</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Fondos oscuros</string>
|
||||
|
||||
@@ -808,9 +808,6 @@
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">پسزمینه مشکی</string>
|
||||
|
||||
@@ -1016,7 +1016,6 @@
|
||||
<string name="theme_mode_light">Lumineux</string>
|
||||
<string name="theme_mode_dark">Sombre</string>
|
||||
|
||||
<string name="multiplier_none">Aucun</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Arrière-plan noir</string>
|
||||
|
||||
@@ -948,7 +948,6 @@
|
||||
<string name="theme_mode_light">Jasny</string>
|
||||
<string name="theme_mode_dark">Ciemny</string>
|
||||
|
||||
<string name="multiplier_none">Brak</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Czarne tła</string>
|
||||
|
||||
@@ -897,7 +897,6 @@
|
||||
<string name="theme_mode_light">Claro</string>
|
||||
<string name="theme_mode_dark">Escuro</string>
|
||||
|
||||
<string name="multiplier_none">Nenhum</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Planos de fundo pretos</string>
|
||||
|
||||
@@ -1084,7 +1084,6 @@
|
||||
<string name="theme_mode_light">Светлая</string>
|
||||
<string name="theme_mode_dark">Темная</string>
|
||||
|
||||
<string name="multiplier_none">Отключено</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Чёрный фон</string>
|
||||
|
||||
@@ -1066,7 +1066,6 @@
|
||||
<string name="theme_mode_light">Світла</string>
|
||||
<string name="theme_mode_dark">Темна</string>
|
||||
|
||||
<string name="multiplier_none">Жодного</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Чорний фон</string>
|
||||
|
||||
@@ -1078,7 +1078,6 @@
|
||||
<string name="theme_mode_light">浅色</string>
|
||||
<string name="theme_mode_dark">深色</string>
|
||||
|
||||
<string name="multiplier_none">无</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">使用黑色背景</string>
|
||||
|
||||
@@ -926,7 +926,6 @@
|
||||
<string name="theme_mode_light">淺色</string>
|
||||
<string name="theme_mode_dark">深色</string>
|
||||
|
||||
<string name="multiplier_none">無</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">黑色背景</string>
|
||||
|
||||
@@ -162,6 +162,48 @@
|
||||
<item>@string/resolution_four</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="frameGenMultiplierNames">
|
||||
<item>@string/frame_gen_multiplier_2x</item>
|
||||
<item>@string/frame_gen_multiplier_3x</item>
|
||||
<item>@string/frame_gen_multiplier_4x</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="frameGenMultiplierValues">
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="frameGenTargetRateNames">
|
||||
<item>@string/frame_gen_target_rate_off</item>
|
||||
<item>@string/frame_gen_target_rate_60</item>
|
||||
<item>@string/frame_gen_target_rate_90</item>
|
||||
<item>@string/frame_gen_target_rate_120</item>
|
||||
<item>@string/frame_gen_target_rate_144</item>
|
||||
<item>@string/frame_gen_target_rate_165</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="frameGenTargetRateValues">
|
||||
<item>0</item>
|
||||
<item>60</item>
|
||||
<item>90</item>
|
||||
<item>120</item>
|
||||
<item>144</item>
|
||||
<item>165</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="frameGenQueueTargetNames">
|
||||
<item>@string/frame_gen_queue_target_0</item>
|
||||
<item>@string/frame_gen_queue_target_1</item>
|
||||
<item>@string/frame_gen_queue_target_2</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="frameGenQueueTargetValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="rendererVSyncNames">
|
||||
<item>@string/renderer_vsync_immediate</item>
|
||||
<item>@string/renderer_vsync_mailbox</item>
|
||||
@@ -471,9 +513,6 @@
|
||||
<item>@string/multiplier_x4</item>
|
||||
<item>@string/multiplier_x8</item>
|
||||
<item>@string/multiplier_x16</item>
|
||||
<item>@string/multiplier_x32</item>
|
||||
<item>@string/multiplier_x64</item>
|
||||
<item>@string/multiplier_none</item>
|
||||
</string-array>
|
||||
<integer-array name="anisoValues">
|
||||
<item>0</item>
|
||||
@@ -482,9 +521,6 @@
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
<item>8</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="verticalAlignmentEntries">
|
||||
@@ -638,6 +674,16 @@
|
||||
<item>@string/error_keys_failed_init</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="losslessDllResults">
|
||||
<item>""</item>
|
||||
<item>@string/error_lossless_copy_failed</item>
|
||||
<item>@string/error_lossless_unreadable</item>
|
||||
<item>@string/error_lossless_not_pe</item>
|
||||
<item>@string/error_lossless_missing_shaders</item>
|
||||
<item>@string/error_lossless_translation_failed</item>
|
||||
<item>@string/error_lossless_cache_failed</item>
|
||||
</string-array>
|
||||
|
||||
<!-- GPU Logging Arrays -->
|
||||
<string-array name="gpuLogLevelEntries">
|
||||
<item>Off</item>
|
||||
|
||||
@@ -298,6 +298,67 @@
|
||||
<string name="gpu_driver_fetcher">GPU driver fetcher</string>
|
||||
<string name="gpu_driver_manager">GPU driver manager</string>
|
||||
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||
<string name="frame_gen">Frame generation</string>
|
||||
<string name="frame_gen_per_game_description">Configure frame generation for this game</string>
|
||||
<string name="frame_gen_description">Insert interpolated frames between rendered ones using Lossless Scaling. Forces FIFO presentation while enabled.</string>
|
||||
<string name="frame_gen_multiplier">Frame multiplier</string>
|
||||
<string name="frame_gen_multiplier_description">How many frames to display for each rendered frame. Higher values cost proportionally more GPU time. Asking for more than your display can present will slow emulation down.</string>
|
||||
<string name="frame_gen_multiplier_2x">2x</string>
|
||||
<string name="frame_gen_multiplier_3x">3x</string>
|
||||
<string name="frame_gen_multiplier_4x">4x</string>
|
||||
<string name="frame_gen_target_rate">Target frame rate</string>
|
||||
<string name="frame_gen_target_rate_description">Pick the rate your display can actually show. The multiplier then rises or falls on its own to hold it, and rolls back any step that makes the game itself run slower.</string>
|
||||
<string name="frame_gen_target_rate_off">Use a fixed multiplier</string>
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_target_rate_144">144 FPS</string>
|
||||
<string name="frame_gen_target_rate_165">165 FPS</string>
|
||||
<string name="frame_gen_queue_target">Frame queue target</string>
|
||||
<string name="frame_gen_queue_target_description">How many finished frames may wait ahead of the display. Larger queues absorb GPU spikes at the cost of input latency.</string>
|
||||
<string name="frame_gen_queue_target_0">Lowest latency (Unbuffered)</string>
|
||||
<string name="frame_gen_queue_target_1">Balanced (1 frame)</string>
|
||||
<string name="frame_gen_queue_target_2">Smoothest (2 frames)</string>
|
||||
<string name="frame_gen_flow_scale_auto">Match motion estimation to the game</string>
|
||||
<string name="frame_gen_flow_scale_auto_description">Estimate motion at the resolution the game actually renders instead of the upscaled output. Costs nothing in accuracy, since upscaling adds no motion detail.</string>
|
||||
<string name="frame_gen_flow_scale">Motion estimation resolution</string>
|
||||
<string name="frame_gen_flow_scale_description">Resolution of the optical flow pass, as a fraction of the output. Lowering it is the cheapest way to reclaim performance.</string>
|
||||
<string name="frame_gen_fp16">Half precision shaders</string>
|
||||
<string name="frame_gen_fp16_description">Use the 16-bit shader variant. Falls back automatically if the driver or the file lacks it.</string>
|
||||
<string name="frame_gen_dump_flow">Dump generated frame</string>
|
||||
<string name="frame_gen_dump_flow_description">Write the optical flow mip levels and the interpolated frame to the lossless/debug folder once, for troubleshooting</string>
|
||||
<string name="frame_gen_unsupported">Frame generation unavailable</string>
|
||||
<string name="frame_gen_unsupported_description">This GPU driver does not support the Vulkan memory model, which the Lossless Scaling shaders require.</string>
|
||||
<string name="lossless_scaling_setup_description">Optional. Provide your own Lossless.dll to enable frame generation later</string>
|
||||
<string name="lossless_scaling_install">Install Lossless.dll</string>
|
||||
<string name="lossless_scaling_install_description">Frame generation needs your own legal copy of Lossless.dll from Lossless Scaling</string>
|
||||
<string name="lossless_scaling_replace_description">Select a different copy of Lossless.dll</string>
|
||||
<string name="frame_generation_support">Frame generation</string>
|
||||
<string name="frame_generation_supported">Supported</string>
|
||||
<string name="frame_generation_unsupported">Unsupported (no Vulkan memory model)</string>
|
||||
<string name="lossless_scaling">Lossless Scaling</string>
|
||||
<string name="lossless_scaling_description">Provide your own copy of Lossless.dll to enable frame generation</string>
|
||||
<string name="lossless_scaling_installed">Installed</string>
|
||||
<string name="lossless_scaling_not_installed">Not installed</string>
|
||||
<string name="lossless_scaling_replace">Replace</string>
|
||||
<string name="lossless_scaling_remove">Remove</string>
|
||||
<string name="lossless_scaling_remove_description">Delete the installed Lossless.dll and its prepared shaders</string>
|
||||
<string name="lossless_scaling_remove_confirmation">Frame generation will stop working until you install Lossless.dll again. Your original file is not affected.</string>
|
||||
<string name="lossless_scaling_missing">Lossless.dll not installed</string>
|
||||
<string name="lossless_scaling_missing_description">Install it from Settings › Lossless Scaling to use frame generation.</string>
|
||||
<string name="lossless_scaling_locked">Close the game first</string>
|
||||
<string name="lossless_scaling_locked_description">Lossless.dll cannot be changed while a game is running.</string>
|
||||
<string name="lossless_scaling_remove_unavailable">Nothing to remove</string>
|
||||
<string name="lossless_scaling_remove_unavailable_description">Lossless.dll is not installed yet.</string>
|
||||
<string name="lossless_scaling_installing">Preparing frame generation shaders…</string>
|
||||
<string name="lossless_scaling_install_success">Lossless.dll installed successfully</string>
|
||||
<string name="lossless_scaling_install_failed">Could not install Lossless.dll</string>
|
||||
<string name="error_lossless_copy_failed">The selected file could not be copied.</string>
|
||||
<string name="error_lossless_unreadable">The selected file could not be read.</string>
|
||||
<string name="error_lossless_not_pe">The selected file is not a Windows library. Select Lossless.dll from your Lossless Scaling installation.</string>
|
||||
<string name="error_lossless_missing_shaders">This copy of Lossless.dll does not contain the frame generation shaders. Update Lossless Scaling and try again.</string>
|
||||
<string name="error_lossless_translation_failed">The frame generation shaders could not be translated. This version of Lossless Scaling is not supported yet.</string>
|
||||
<string name="error_lossless_cache_failed">The translated shaders could not be written to storage. Check that there is free space available.</string>
|
||||
<string name="advanced_settings">Advanced settings</string>
|
||||
<string name="settings_description">Configure emulator settings</string>
|
||||
<string name="search_recently_played">Recently played</string>
|
||||
@@ -1185,9 +1246,6 @@
|
||||
<string name="multiplier_x4" translatable="false">x4</string>
|
||||
<string name="multiplier_x8" translatable="false">x8</string>
|
||||
<string name="multiplier_x16" translatable="false">x16</string>
|
||||
<string name="multiplier_x32" translatable="false">x32</string>
|
||||
<string name="multiplier_x64" translatable="false">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Black backgrounds</string>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -20,6 +20,7 @@
|
||||
#define KEYS_DIR "keys"
|
||||
#define LOAD_DIR "load"
|
||||
#define LOG_DIR "log"
|
||||
#define LOSSLESS_DIR "lossless"
|
||||
#define NAND_DIR "nand"
|
||||
#define PLAY_TIME_DIR "play_time"
|
||||
#define SCREENSHOTS_DIR "screenshots"
|
||||
@@ -37,3 +38,5 @@
|
||||
|
||||
// yuzu-specific files
|
||||
#define LOG_FILE "eden_log.txt"
|
||||
#define LOSSLESS_DLL_FILE "Lossless.dll"
|
||||
#define LOSSLESS_CACHE_FILE "lsfg_spirv.cache"
|
||||
|
||||
@@ -157,6 +157,7 @@ public:
|
||||
GenerateEdenPath(EdenPath::KeysDir, eden_path / KEYS_DIR);
|
||||
GenerateEdenPath(EdenPath::LoadDir, eden_path / LOAD_DIR);
|
||||
GenerateEdenPath(EdenPath::LogDir, eden_path / LOG_DIR);
|
||||
GenerateEdenPath(EdenPath::LosslessDir, eden_path / LOSSLESS_DIR);
|
||||
GenerateEdenPath(EdenPath::NANDDir, eden_path / NAND_DIR);
|
||||
GenerateEdenPath(EdenPath::PlayTimeDir, eden_path / PLAY_TIME_DIR);
|
||||
GenerateEdenPath(EdenPath::SaveDir, eden_path / NAND_DIR);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -23,6 +23,7 @@ enum class EdenPath {
|
||||
KeysDir, // Where key files are stored.
|
||||
LoadDir, // Where cheat/mod files are stored.
|
||||
LogDir, // Where log files are stored.
|
||||
LosslessDir, // Where the user-supplied Lossless Scaling library is stored.
|
||||
NANDDir, // Where the emulated NAND is stored.
|
||||
PlayTimeDir, // Where play time data is stored.
|
||||
SaveDir, // Where save data is stored.
|
||||
|
||||
@@ -380,6 +380,28 @@ void UpdateRescalingInfo() {
|
||||
TranslateResolutionInfo(setup, info);
|
||||
}
|
||||
|
||||
u32 FrameGenMultiplier() {
|
||||
return std::clamp(values.frame_gen_multiplier.GetValue(), MIN_FRAME_GEN_MULTIPLIER,
|
||||
MAX_FRAME_GEN_MULTIPLIER);
|
||||
}
|
||||
|
||||
size_t FrameGenGenerations() {
|
||||
if (!values.frame_gen.GetValue()) {
|
||||
return 0;
|
||||
}
|
||||
return FrameGenMultiplier() - 1;
|
||||
}
|
||||
|
||||
size_t FrameGenMaxGenerations() {
|
||||
if (!values.frame_gen.GetValue()) {
|
||||
return 0;
|
||||
}
|
||||
if (values.frame_gen_target_rate.GetValue() != 0) {
|
||||
return MAX_FRAME_GEN_MULTIPLIER - 1;
|
||||
}
|
||||
return FrameGenMultiplier() - 1;
|
||||
}
|
||||
|
||||
void RestoreGlobalState(bool is_powered_on) {
|
||||
// If a game is running, DO NOT restore the global settings state
|
||||
if (is_powered_on) {
|
||||
|
||||
+76
-8
@@ -352,7 +352,7 @@ struct Values {
|
||||
true};
|
||||
|
||||
SwitchableSetting<ScalingFilter> scaling_filter{linkage,
|
||||
ScalingFilter::Bilinear,
|
||||
ScalingFilter::NearestNeighbor,
|
||||
"scaling_filter",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
@@ -388,6 +388,69 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
|
||||
Specialization::Default, true, false};
|
||||
|
||||
SwitchableSetting<u32, true> frame_gen_multiplier{linkage,
|
||||
2,
|
||||
2,
|
||||
4,
|
||||
"frame_gen_multiplier",
|
||||
Category::Renderer,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
false,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<u32, true> frame_gen_target_rate{linkage,
|
||||
0,
|
||||
0,
|
||||
240,
|
||||
"frame_gen_target_rate",
|
||||
Category::Renderer,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<bool> frame_gen_flow_scale_auto{linkage,
|
||||
true,
|
||||
"frame_gen_flow_scale_auto",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
false,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<u32, true> frame_gen_flow_scale{linkage,
|
||||
75,
|
||||
25,
|
||||
100,
|
||||
"frame_gen_flow_scale",
|
||||
Category::Renderer,
|
||||
Specialization::Countable |
|
||||
Specialization::Percentage,
|
||||
true,
|
||||
true,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<u32, true> frame_gen_queue_target{linkage,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
"frame_gen_queue_target",
|
||||
Category::Renderer,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
false,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer,
|
||||
Specialization::Default, true, false, &frame_gen};
|
||||
|
||||
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
|
||||
Category::Renderer};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage,
|
||||
#ifdef __ANDROID__
|
||||
false,
|
||||
@@ -569,13 +632,8 @@ struct Values {
|
||||
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<bool> rescale_hack{linkage,
|
||||
#ifdef __ANDROID__
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
"rescale_hack", Category::RendererHacks};
|
||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||
Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
false,
|
||||
"enable_gpu_buffer_readback",
|
||||
@@ -875,10 +933,20 @@ struct Values {
|
||||
|
||||
// Per-game overrides
|
||||
bool use_squashed_iterated_blend;
|
||||
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
constexpr u32 MIN_FRAME_GEN_MULTIPLIER = 2;
|
||||
constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
|
||||
|
||||
[[nodiscard]] u32 FrameGenMultiplier();
|
||||
|
||||
[[nodiscard]] size_t FrameGenGenerations();
|
||||
|
||||
[[nodiscard]] size_t FrameGenMaxGenerations();
|
||||
|
||||
bool getDebugKnobAt(u8 i);
|
||||
|
||||
void UpdateGPUAccuracy();
|
||||
|
||||
@@ -128,7 +128,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt
|
||||
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
||||
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
||||
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
|
||||
|
||||
@@ -1251,7 +1251,7 @@ if (HAS_NCE)
|
||||
target_link_libraries(core PRIVATE merry::oaknut)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_powerpc64 OR ARCHITECTURE_powerpc64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
|
||||
@@ -88,25 +88,10 @@ Result IApplicationDisplayService::GetIndirectDisplayTransactionService(
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) {
|
||||
LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
|
||||
|
||||
// Ensure the display name is null-terminated
|
||||
display_name[display_name.size() - 1] = '\0';
|
||||
|
||||
// According to switchbrew, only "Default", "External", "Edid", "Internal" and "Null" are valid
|
||||
const std::array<std::string_view, 5> valid_names = {
|
||||
"Default", "External", "Edid", "Internal", "Null"
|
||||
};
|
||||
|
||||
bool valid_name = false;
|
||||
for (const auto& name : valid_names) {
|
||||
if (name == display_name.data()) {
|
||||
valid_name = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
R_UNLESS(valid_name, ResultOperationFailed);
|
||||
LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
|
||||
|
||||
R_RETURN(m_container->OpenDisplay(out_display_id, display_name));
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ add_library(dynarmic STATIC
|
||||
interface/halt_reason.h
|
||||
interface/exclusive_monitor.h
|
||||
interface/optimization_flags.h
|
||||
interface/halt_reason.h
|
||||
ir/acc_type.h
|
||||
ir/basic_block.cpp
|
||||
ir/basic_block.h
|
||||
@@ -140,8 +139,10 @@ if ("x86_64" IN_LIST ARCHITECTURE)
|
||||
# Newer versions of xbyak (>= 7.25.0) have stricter checks that currently
|
||||
# fail in dynarmic
|
||||
target_compile_definitions(dynarmic PRIVATE XBYAK_STRICT_CHECK_MEM_REG_SIZE=0)
|
||||
|
||||
target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1)
|
||||
target_link_libraries(dynarmic PRIVATE xbyak::xbyak)
|
||||
|
||||
target_architecture_specific_sources(dynarmic "x86_64"
|
||||
backend/x64/abi.cpp
|
||||
backend/x64/abi.h
|
||||
@@ -293,35 +294,6 @@ if ("riscv64" IN_LIST ARCHITECTURE)
|
||||
)
|
||||
message(WARNING "TODO: Incomplete frontend for this host architecture")
|
||||
endif()
|
||||
if ("ppc64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(dynarmic PRIVATE powah)
|
||||
target_sources(dynarmic PRIVATE
|
||||
backend/ppc64/abi.h
|
||||
backend/ppc64/emit_context.h
|
||||
backend/ppc64/emit_ppc64_a32.cpp
|
||||
backend/ppc64/emit_ppc64_a64.cpp
|
||||
backend/ppc64/emit_ppc64_misc.cpp
|
||||
backend/ppc64/emit_ppc64_data_processing.cpp
|
||||
backend/ppc64/emit_ppc64_floating_point.cpp
|
||||
backend/ppc64/emit_ppc64_vector.cpp
|
||||
backend/ppc64/emit_ppc64.cpp
|
||||
backend/ppc64/emit_ppc64.h
|
||||
backend/ppc64/exclusive_monitor.cpp
|
||||
backend/ppc64/reg_alloc.cpp
|
||||
backend/ppc64/reg_alloc.h
|
||||
backend/ppc64/stack_layout.h
|
||||
backend/ppc64/hostloc.h
|
||||
common/spin_lock_ppc64.cpp
|
||||
common/spin_lock_ppc64.h
|
||||
# A32
|
||||
backend/ppc64/a32_core.h
|
||||
backend/ppc64/a32_interface.cpp
|
||||
# A64
|
||||
backend/ppc64/a64_core.h
|
||||
backend/ppc64/a64_interface.cpp
|
||||
backend/ppc64/code_block.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if ("loongarch64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(dynarmic PRIVATE lagoon::lagoon)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
class BlockOfCode;
|
||||
}
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
namespace oaknut {
|
||||
class CodeBlock;
|
||||
@@ -30,10 +30,6 @@ class CodeBlock;
|
||||
namespace Dynarmic::Backend::LoongArch64 {
|
||||
class CodeBlock;
|
||||
} // namespace Dynarmic::Backend::LoongArch64
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
class CodeBlock;
|
||||
}
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
@@ -57,9 +53,6 @@ struct FakeCall {
|
||||
struct FakeCall {
|
||||
u64 call_pc;
|
||||
};
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
struct FakeCall {
|
||||
};
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
@@ -77,8 +70,6 @@ public:
|
||||
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
void Register(LoongArch64::CodeBlock& mem, std::size_t mem_size);
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
void Register(PPC64::CodeBlock& mem, std::size_t mem_size);
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
@@ -17,21 +17,24 @@ ExceptionHandler::ExceptionHandler() = default;
|
||||
ExceptionHandler::~ExceptionHandler() = default;
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
void ExceptionHandler::Register(X64::BlockOfCode&)
|
||||
void ExceptionHandler::Register(X64::BlockOfCode&) {
|
||||
// Do nothing
|
||||
}
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t)
|
||||
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
|
||||
// Do nothing
|
||||
}
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t)
|
||||
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
|
||||
// Do nothing
|
||||
}
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t)
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
void ExceptionHandler::Register(PPC64::CodeBlock&, std::size_t)
|
||||
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t) {
|
||||
// Do nothing
|
||||
}
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
bool ExceptionHandler::SupportsFastmem() const noexcept {
|
||||
return false;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -35,8 +34,6 @@
|
||||
# include "dynarmic/backend/riscv64/code_block.h"
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
# include "dynarmic/backend/loongarch64/code_block.h"
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
# include "dynarmic/backend/ppc64/code_block.h"
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
@@ -171,7 +168,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
||||
@@ -233,10 +230,6 @@ void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
|
||||
void ExceptionHandler::Register(LoongArch64::CodeBlock& mem, std::size_t size) {
|
||||
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
|
||||
}
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
void ExceptionHandler::Register(PPC64::CodeBlock& mem, std::size_t size) {
|
||||
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
|
||||
}
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include "dynarmic/backend/ppc64/code_block.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||
#include "dynarmic/interface/A32/a32.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct A32JitState {
|
||||
std::array<u32, 16> regs{};
|
||||
alignas(16) std::array<u32, 64> ext_regs{};
|
||||
u32 upper_location_descriptor;
|
||||
u32 exclusive_state = 0;
|
||||
u32 cpsr_nzcv = 0;
|
||||
u32 fpscr = 0;
|
||||
u8 check_bit = 0;
|
||||
|
||||
IR::LocationDescriptor GetLocationDescriptor() const {
|
||||
return IR::LocationDescriptor{regs[15] | (u64(upper_location_descriptor) << 32)};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,250 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||
#include "dynarmic/frontend/A32/FPSCR.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/common/atomic.h"
|
||||
#include "dynarmic/ir/opt_passes.h"
|
||||
#include "dynarmic/interface/A32/a32.h"
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
using namespace Dynarmic::Backend::PPC64;
|
||||
using CodePtr = std::uint32_t*;
|
||||
|
||||
struct A32AddressSpace final {
|
||||
explicit A32AddressSpace(const A32::UserConfig& conf)
|
||||
: conf(conf)
|
||||
, cb(conf.code_cache_size)
|
||||
, as(cb.ptr<u8*>(), conf.code_cache_size) {
|
||||
|
||||
}
|
||||
|
||||
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
|
||||
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
|
||||
return it->second;
|
||||
|
||||
ir_block.Reset(A32::LocationDescriptor{desc});
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{desc}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
Optimization::Optimize(ir_block, conf, {});
|
||||
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
|
||||
|
||||
block_infos.insert_or_assign(desc.Value(), block_info);
|
||||
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
|
||||
return block_info.entry_point;
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
block_entries.clear();
|
||||
block_infos.clear();
|
||||
}
|
||||
|
||||
EmittedBlockInfo Emit(IR::Block block) {
|
||||
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
|
||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||
.always_little_endian = conf.always_little_endian,
|
||||
.a64_variant = false
|
||||
});
|
||||
Link(block_info);
|
||||
return block_info;
|
||||
}
|
||||
|
||||
void Link(EmittedBlockInfo& block_info) {
|
||||
//UNREACHABLE();
|
||||
}
|
||||
|
||||
IR::Block ir_block = {LocationDescriptor(0, PSR(0), FPSCR(0), false)};
|
||||
const A32::UserConfig conf;
|
||||
CodeBlock cb;
|
||||
powah::Context as;
|
||||
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
|
||||
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
|
||||
};
|
||||
|
||||
struct A32Core final {
|
||||
static HaltReason Run(A32AddressSpace& process, A32JitState& thread_ctx, volatile u32* halt_reason) {
|
||||
auto const loc = thread_ctx.GetLocationDescriptor();
|
||||
auto const entry = process.GetOrEmit(loc);
|
||||
using CodeFn = HaltReason (*)(A32AddressSpace*, A32JitState*, volatile u32*, void*);
|
||||
return (CodeFn(entry))(&process, &thread_ctx, halt_reason, reinterpret_cast<void*>(&A32Core::Run));
|
||||
}
|
||||
};
|
||||
|
||||
struct Jit::Impl final {
|
||||
Impl(Jit* jit_interface, A32::UserConfig conf)
|
||||
: conf(conf)
|
||||
, current_address_space(conf)
|
||||
, jit_interface(jit_interface) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
is_executing = false;
|
||||
HaltReason hr = A32Core::Run(current_address_space, jit_state, &halt_reason);
|
||||
is_executing = true;
|
||||
RequestCacheInvalidation();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
// HaltReason hr = A32Core::Step(current_address_space, jit_state, &halt_reason);
|
||||
// RequestCacheInvalidation();
|
||||
return HaltReason{};
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(u32 start_address, size_t length) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, u32(start_address + length - 1)));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
jit_state = {};
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&halt_reason, ~u32(hr));
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&halt_reason, ~u32(hr));
|
||||
}
|
||||
|
||||
std::array<u32, 16>& Regs() {
|
||||
return jit_state.regs;
|
||||
}
|
||||
|
||||
const std::array<u32, 16>& Regs() const {
|
||||
return jit_state.regs;
|
||||
}
|
||||
|
||||
std::array<u32, 64>& ExtRegs() {
|
||||
return jit_state.ext_regs;
|
||||
}
|
||||
|
||||
const std::array<u32, 64>& ExtRegs() const {
|
||||
return jit_state.ext_regs;
|
||||
}
|
||||
|
||||
u32 Cpsr() const {
|
||||
return jit_state.cpsr_nzcv;
|
||||
}
|
||||
|
||||
void SetCpsr(u32 value) {
|
||||
jit_state.cpsr_nzcv = value;
|
||||
}
|
||||
|
||||
u32 Fpscr() const {
|
||||
return jit_state.fpscr;
|
||||
}
|
||||
|
||||
void SetFpscr(u32 value) {
|
||||
jit_state.fpscr = value;
|
||||
}
|
||||
|
||||
void ClearExclusiveState() {
|
||||
jit_state.exclusive_state = false;
|
||||
}
|
||||
|
||||
private:
|
||||
void RequestCacheInvalidation() {
|
||||
// UNREACHABLE();
|
||||
invalidate_entire_cache = false;
|
||||
invalid_cache_ranges.clear();
|
||||
}
|
||||
|
||||
A32::UserConfig conf;
|
||||
A32JitState jit_state{};
|
||||
A32AddressSpace current_address_space;
|
||||
Jit* jit_interface;
|
||||
volatile u32 halt_reason = 0;
|
||||
bool is_executing = false;
|
||||
|
||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
std::mutex invalidation_mutex;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf) : impl(std::make_unique<Impl>(this, conf)) {}
|
||||
Jit::~Jit() = default;
|
||||
|
||||
HaltReason Jit::Run() {
|
||||
return impl->Run();
|
||||
}
|
||||
|
||||
HaltReason Jit::Step() {
|
||||
return impl->Step();
|
||||
}
|
||||
|
||||
void Jit::ClearCache() {
|
||||
impl->ClearCache();
|
||||
}
|
||||
|
||||
void Jit::InvalidateCacheRange(u32 start_address, std::size_t length) {
|
||||
impl->InvalidateCacheRange(start_address, length);
|
||||
}
|
||||
|
||||
void Jit::Reset() {
|
||||
impl->Reset();
|
||||
}
|
||||
|
||||
void Jit::HaltExecution(HaltReason hr) {
|
||||
impl->HaltExecution(hr);
|
||||
}
|
||||
|
||||
void Jit::ClearHalt(HaltReason hr) {
|
||||
impl->ClearHalt(hr);
|
||||
}
|
||||
|
||||
std::array<u32, 16>& Jit::Regs() {
|
||||
return impl->Regs();
|
||||
}
|
||||
|
||||
const std::array<u32, 16>& Jit::Regs() const {
|
||||
return impl->Regs();
|
||||
}
|
||||
|
||||
std::array<u32, 64>& Jit::ExtRegs() {
|
||||
return impl->ExtRegs();
|
||||
}
|
||||
|
||||
const std::array<u32, 64>& Jit::ExtRegs() const {
|
||||
return impl->ExtRegs();
|
||||
}
|
||||
|
||||
u32 Jit::Cpsr() const {
|
||||
return impl->Cpsr();
|
||||
}
|
||||
|
||||
void Jit::SetCpsr(u32 value) {
|
||||
impl->SetCpsr(value);
|
||||
}
|
||||
|
||||
u32 Jit::Fpscr() const {
|
||||
return impl->Fpscr();
|
||||
}
|
||||
|
||||
void Jit::SetFpscr(u32 value) {
|
||||
impl->SetFpscr(value);
|
||||
}
|
||||
|
||||
void Jit::ClearExclusiveState() {
|
||||
impl->ClearExclusiveState();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::A32
|
||||
@@ -1,40 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include "dynarmic/backend/ppc64/code_block.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||
#include "dynarmic/interface/A64/a64.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct A64JitState {
|
||||
using ProgramCounterType = u32;
|
||||
std::array<u64, 31> regs{};
|
||||
u64 pc = 0;
|
||||
alignas(16) std::array<u64, 64> vec{};
|
||||
u64 sp = 0;
|
||||
u32 upper_location_descriptor = 0;
|
||||
u32 exclusive_state = 0;
|
||||
u32 pstate = 0;
|
||||
u32 fpcr = 0;
|
||||
u32 fpsr = 0;
|
||||
volatile u32 halt_reason = 0;
|
||||
u8 check_bit = 0;
|
||||
|
||||
IR::LocationDescriptor GetLocationDescriptor() const {
|
||||
const u64 fpcr_u64 = u64(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
|
||||
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
|
||||
return IR::LocationDescriptor{pc_u64 | fpcr_u64};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,368 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/common/atomic.h"
|
||||
#include "dynarmic/ir/opt_passes.h"
|
||||
#include "dynarmic/interface/A64/a64.h"
|
||||
|
||||
namespace Dynarmic::A64 {
|
||||
|
||||
using namespace Dynarmic::Backend::PPC64;
|
||||
using CodePtr = std::uint32_t*;
|
||||
|
||||
struct A64AddressSpace final {
|
||||
explicit A64AddressSpace(const A64::UserConfig& conf)
|
||||
: conf(conf)
|
||||
, cb(conf.code_cache_size)
|
||||
, as(cb.ptr<u8*>(), conf.code_cache_size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
|
||||
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
|
||||
return it->second;
|
||||
auto const get_code = [this](u64 vaddr) {
|
||||
return conf.callbacks->MemoryReadCode(vaddr);
|
||||
};
|
||||
ir_block.Reset(A64::LocationDescriptor{desc});
|
||||
A64::Translate(ir_block, A64::LocationDescriptor{desc}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
Optimization::Optimize(ir_block, conf, {});
|
||||
fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
|
||||
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
|
||||
block_infos.insert_or_assign(desc.Value(), block_info);
|
||||
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
|
||||
return block_info.entry_point;
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
block_entries.clear();
|
||||
block_infos.clear();
|
||||
}
|
||||
|
||||
EmittedBlockInfo Emit(IR::Block block) {
|
||||
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
|
||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||
.always_little_endian = true,
|
||||
.a64_variant = true
|
||||
});
|
||||
Link(block_info);
|
||||
return block_info;
|
||||
}
|
||||
|
||||
void Link(EmittedBlockInfo& block_info) {
|
||||
// TODO(lizzie): Block linking
|
||||
// UNREACHABLE();
|
||||
}
|
||||
|
||||
IR::Block ir_block = {LocationDescriptor(0, FP::FPCR(0), false)};
|
||||
const A64::UserConfig conf;
|
||||
CodeBlock cb;
|
||||
powah::Context as;
|
||||
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
|
||||
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
|
||||
};
|
||||
|
||||
__attribute__((noinline)) HaltReason test_thunk(A64AddressSpace* a, A64JitState* b, volatile u32* c, void* d) {
|
||||
printf("%p,%p,%p,%p\n", a, b, c, d);
|
||||
return HaltReason::UserDefined2;
|
||||
}
|
||||
|
||||
struct A64Core final {
|
||||
static HaltReason Run(A64AddressSpace& process, A64JitState& thread_ctx, volatile u32* halt_reason) {
|
||||
const auto loc = thread_ctx.GetLocationDescriptor();
|
||||
const auto entry = process.GetOrEmit(loc);
|
||||
using AbstractCodeFn = HaltReason (*)(A64AddressSpace*, A64JitState*, volatile u32*, void (*fn)());
|
||||
using CodeFn = HaltReason (*)(A64AddressSpace*, A64JitState*, volatile u32*, AbstractCodeFn fn);
|
||||
return (CodeFn(entry))(&process, &thread_ctx, halt_reason, AbstractCodeFn(test_thunk));
|
||||
}
|
||||
};
|
||||
|
||||
struct Jit::Impl final {
|
||||
Impl(Jit* jit_interface, A64::UserConfig conf)
|
||||
: conf(conf)
|
||||
, current_address_space(conf)
|
||||
, jit_interface(jit_interface) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
is_executing = true;
|
||||
HaltReason hr = A64Core::Run(current_address_space, jit_state, &halt_reason);
|
||||
is_executing = false;
|
||||
RequestCacheInvalidation();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
// HaltReason hr = A64Core::Step(current_address_space, jit_state, &halt_reason);
|
||||
// RequestCacheInvalidation();
|
||||
return HaltReason{};
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(u64 start_address, size_t length) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
const auto end_address = u64(start_address + length - 1);
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u64>::closed(start_address, end_address));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ASSERT(!is_executing);
|
||||
jit_state = {};
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&jit_state.halt_reason, u32(hr));
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&jit_state.halt_reason, ~u32(hr));
|
||||
}
|
||||
|
||||
u64 GetSP() const {
|
||||
return jit_state.sp;
|
||||
}
|
||||
|
||||
void SetSP(u64 value) {
|
||||
jit_state.sp = value;
|
||||
}
|
||||
|
||||
u64 GetPC() const {
|
||||
return jit_state.pc;
|
||||
}
|
||||
|
||||
void SetPC(u64 value) {
|
||||
jit_state.pc = value;
|
||||
}
|
||||
|
||||
u64 GetRegister(size_t index) const {
|
||||
return index == 31 ? GetSP() : jit_state.regs.at(index);
|
||||
}
|
||||
|
||||
void SetRegister(size_t index, u64 value) {
|
||||
if (index == 31)
|
||||
return SetSP(value);
|
||||
jit_state.regs.at(index) = value;
|
||||
}
|
||||
|
||||
std::array<u64, 31> GetRegisters() const {
|
||||
return jit_state.regs;
|
||||
}
|
||||
|
||||
void SetRegisters(const std::array<u64, 31>& value) {
|
||||
jit_state.regs = value;
|
||||
}
|
||||
|
||||
Vector GetVector(size_t index) const {
|
||||
return {jit_state.vec.at(index * 2), jit_state.vec.at(index * 2 + 1)};
|
||||
}
|
||||
|
||||
void SetVector(size_t index, Vector value) {
|
||||
jit_state.vec.at(index * 2) = value[0];
|
||||
jit_state.vec.at(index * 2 + 1) = value[1];
|
||||
}
|
||||
|
||||
std::array<Vector, 32> GetVectors() const {
|
||||
std::array<Vector, 32> ret;
|
||||
static_assert(sizeof(ret) == sizeof(jit_state.vec));
|
||||
std::memcpy(ret.data(), jit_state.vec.data(), sizeof(jit_state.vec));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SetVectors(const std::array<Vector, 32>& value) {
|
||||
static_assert(sizeof(value) == sizeof(jit_state.vec));
|
||||
std::memcpy(jit_state.vec.data(), value.data(), sizeof(jit_state.vec));
|
||||
}
|
||||
|
||||
u32 GetFpcr() const {
|
||||
return jit_state.fpcr;
|
||||
}
|
||||
|
||||
void SetFpcr(u32 value) {
|
||||
jit_state.fpcr = value;
|
||||
}
|
||||
|
||||
u32 GetFpsr() const {
|
||||
return jit_state.fpsr;
|
||||
}
|
||||
|
||||
void SetFpsr(u32 value) {
|
||||
jit_state.fpsr = value;
|
||||
}
|
||||
|
||||
u32 GetPstate() const {
|
||||
return jit_state.pstate;
|
||||
}
|
||||
|
||||
void SetPstate(u32 value) {
|
||||
jit_state.pstate = value;
|
||||
}
|
||||
|
||||
void ClearExclusiveState() {
|
||||
jit_state.exclusive_state = 0;
|
||||
}
|
||||
|
||||
bool IsExecuting() const {
|
||||
return is_executing;
|
||||
}
|
||||
|
||||
std::string Disassemble() const {
|
||||
// const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||
// auto const* p = reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||
// return Common::DisassemblePPC64(p, p + size);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
void RequestCacheInvalidation() {
|
||||
// UNREACHABLE();
|
||||
invalidate_entire_cache = false;
|
||||
invalid_cache_ranges.clear();
|
||||
}
|
||||
|
||||
A64::UserConfig conf;
|
||||
A64JitState jit_state{};
|
||||
A64AddressSpace current_address_space;
|
||||
Jit* jit_interface;
|
||||
volatile u32 halt_reason = 0;
|
||||
bool is_executing = false;
|
||||
|
||||
boost::icl::interval_set<u64> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
std::mutex invalidation_mutex;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf) : impl(std::make_unique<Jit::Impl>(this, conf)) {}
|
||||
Jit::~Jit() = default;
|
||||
|
||||
HaltReason Jit::Run() {
|
||||
return impl->Run();
|
||||
}
|
||||
|
||||
HaltReason Jit::Step() {
|
||||
return impl->Step();
|
||||
}
|
||||
|
||||
void Jit::ClearCache() {
|
||||
impl->ClearCache();
|
||||
}
|
||||
|
||||
void Jit::InvalidateCacheRange(u64 start_address, size_t length) {
|
||||
impl->InvalidateCacheRange(start_address, length);
|
||||
}
|
||||
|
||||
void Jit::Reset() {
|
||||
impl->Reset();
|
||||
}
|
||||
|
||||
void Jit::HaltExecution(HaltReason hr) {
|
||||
impl->HaltExecution(hr);
|
||||
}
|
||||
|
||||
void Jit::ClearHalt(HaltReason hr) {
|
||||
impl->ClearHalt(hr);
|
||||
}
|
||||
|
||||
u64 Jit::GetSP() const {
|
||||
return impl->GetSP();
|
||||
}
|
||||
|
||||
void Jit::SetSP(u64 value) {
|
||||
impl->SetSP(value);
|
||||
}
|
||||
|
||||
u64 Jit::GetPC() const {
|
||||
return impl->GetPC();
|
||||
}
|
||||
|
||||
void Jit::SetPC(u64 value) {
|
||||
impl->SetPC(value);
|
||||
}
|
||||
|
||||
u64 Jit::GetRegister(size_t index) const {
|
||||
return impl->GetRegister(index);
|
||||
}
|
||||
|
||||
void Jit::SetRegister(size_t index, u64 value) {
|
||||
impl->SetRegister(index, value);
|
||||
}
|
||||
|
||||
std::array<u64, 31> Jit::GetRegisters() const {
|
||||
return impl->GetRegisters();
|
||||
}
|
||||
|
||||
void Jit::SetRegisters(const std::array<u64, 31>& value) {
|
||||
impl->SetRegisters(value);
|
||||
}
|
||||
|
||||
Vector Jit::GetVector(size_t index) const {
|
||||
return impl->GetVector(index);
|
||||
}
|
||||
|
||||
void Jit::SetVector(size_t index, Vector value) {
|
||||
impl->SetVector(index, value);
|
||||
}
|
||||
|
||||
std::array<Vector, 32> Jit::GetVectors() const {
|
||||
return impl->GetVectors();
|
||||
}
|
||||
|
||||
void Jit::SetVectors(const std::array<Vector, 32>& value) {
|
||||
impl->SetVectors(value);
|
||||
}
|
||||
|
||||
u32 Jit::GetFpcr() const {
|
||||
return impl->GetFpcr();
|
||||
}
|
||||
|
||||
void Jit::SetFpcr(u32 value) {
|
||||
impl->SetFpcr(value);
|
||||
}
|
||||
|
||||
u32 Jit::GetFpsr() const {
|
||||
return impl->GetFpsr();
|
||||
}
|
||||
|
||||
void Jit::SetFpsr(u32 value) {
|
||||
impl->SetFpsr(value);
|
||||
}
|
||||
|
||||
u32 Jit::GetPstate() const {
|
||||
return impl->GetPstate();
|
||||
}
|
||||
|
||||
void Jit::SetPstate(u32 value) {
|
||||
impl->SetPstate(value);
|
||||
}
|
||||
|
||||
void Jit::ClearExclusiveState() {
|
||||
impl->ClearExclusiveState();
|
||||
}
|
||||
|
||||
bool Jit::IsExecuting() const {
|
||||
return impl->IsExecuting();
|
||||
}
|
||||
|
||||
std::string Jit::Disassemble() const {
|
||||
return impl->Disassemble();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::A64
|
||||
@@ -1,78 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
/*
|
||||
https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
|
||||
|
||||
r0 Volatile register used in function prologs
|
||||
r1 Stack frame pointer
|
||||
r2 TOC pointer
|
||||
r3 Volatile parameter and return value register
|
||||
r4-r10 Volatile registers used for function parameters
|
||||
r11 Volatile register used in calls by pointer and as an environment pointer for languages which require one
|
||||
r12 Volatile register used for exception handling and glink code
|
||||
r13 Reserved for use as system thread ID
|
||||
r14-r31 Nonvolatile registers used for local variables
|
||||
|
||||
f0 Volatile scratch register
|
||||
f1-f4 Volatile floating point parameter and return value registers
|
||||
f5-f13 Volatile floating point parameter registers
|
||||
f14-f31 Nonvolatile registers
|
||||
|
||||
LR Link register (volatile)
|
||||
CTR Loop counter register (volatile)
|
||||
XER Fixed point exception register (volatile)
|
||||
FPSCR Floating point status and control register (volatile)
|
||||
|
||||
CR0-CR1 Volatile condition code register fields
|
||||
CR2-CR4 Nonvolatile condition code register fields
|
||||
CR5-CR7 Volatile condition code register fields
|
||||
|
||||
v0-v1 Volatile scratch registers
|
||||
v2-v13 Volatile vector parameters registers
|
||||
v14-v19 Volatile scratch registers
|
||||
v20-v31 Non-volatile registers
|
||||
vrsave Non-volatile 32-bit register
|
||||
*/
|
||||
|
||||
// Jit fn signature => (AXXAddressSpace& process, AXXJitState& thread_ctx, volatile u32* halt_reason)
|
||||
constexpr powah::GPR RPROCESS = powah::R3;
|
||||
constexpr powah::GPR RJIT = powah::R4;
|
||||
constexpr powah::GPR RHALTREASON = powah::R5;
|
||||
constexpr powah::GPR RTOCPTR = powah::R6;
|
||||
// temporals
|
||||
constexpr powah::GPR RNZCV = powah::R7;
|
||||
constexpr powah::GPR RCHECKBIT = powah::R8;
|
||||
|
||||
constexpr powah::GPR ABI_PARAM1 = powah::R3;
|
||||
constexpr powah::GPR ABI_PARAM2 = powah::R4;
|
||||
constexpr powah::GPR ABI_PARAM3 = powah::R5;
|
||||
constexpr powah::GPR ABI_PARAM4 = powah::R6;
|
||||
|
||||
// See https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
|
||||
constexpr std::initializer_list<u32> GPR_ORDER{
|
||||
// r13 is thread-id
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 //non-volatile
|
||||
};
|
||||
|
||||
constexpr std::initializer_list<powah::GPR> ABI_CALLEER_SAVED{
|
||||
powah::R3, powah::R4, powah::R5, powah::R6,
|
||||
powah::R7, powah::R8, powah::R9, powah::R10,
|
||||
powah::R11, powah::R12
|
||||
};
|
||||
constexpr std::initializer_list<powah::GPR> ABI_CALLEE_SAVED{
|
||||
powah::R14, powah::R15, powah::R16, powah::R17,
|
||||
powah::R18, powah::R19, powah::R20, powah::R21,
|
||||
powah::R22, powah::R23, powah::R24, powah::R25,
|
||||
powah::R26, powah::R27, powah::R28, powah::R29,
|
||||
powah::R30, powah::R31
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <new>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/common/assert.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
class CodeBlock {
|
||||
public:
|
||||
explicit CodeBlock(size_t size_) noexcept : size{size_} {
|
||||
block = (u8*)mmap(nullptr, size, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
//printf("block = %p, size= %zx\n", block, size);
|
||||
ASSERT(block != nullptr);
|
||||
}
|
||||
~CodeBlock() noexcept {
|
||||
if (block != nullptr)
|
||||
munmap(block, size);
|
||||
}
|
||||
template<typename T>
|
||||
T ptr() const noexcept {
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uintptr_t> || std::is_same_v<T, intptr_t>);
|
||||
return reinterpret_cast<T>(block);
|
||||
}
|
||||
protected:
|
||||
void* block = nullptr;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,24 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
class Block;
|
||||
} // namespace Dynarmic::IR
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct EmitConfig;
|
||||
|
||||
struct EmitContext {
|
||||
IR::Block& block;
|
||||
RegAlloc& reg_alloc;
|
||||
const EmitConfig& emit_conf;
|
||||
EmittedBlockInfo& ebi;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,293 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/backend/ppc64/stack_layout.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
#include "stack_layout.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Void>(powah::Context&, EmitContext&, IR::Inst*) {}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Identity>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.MR(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Breakpoint>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CallHostFunction>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PushRSB>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetCarryFromOp>(powah::Context&, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetOverflowFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetGEFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetNZCVFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (ctx.reg_alloc.IsValueLive(inst)) {
|
||||
ASSERT(false && "unimp value live");
|
||||
return;
|
||||
}
|
||||
|
||||
// All logical operations whom set (RC) are going to compute as the following:
|
||||
// 1. Rt <- logical_op (Ra, Rb)
|
||||
// 2. Compare as Signed(Rt) against 0
|
||||
// Basically, it's equivalent to say:
|
||||
//
|
||||
// add r0, r1, r2 -> add. r0, r1, r2
|
||||
// cmpli r0, 0
|
||||
//
|
||||
// CR0:
|
||||
// 0 - 0x08 - N/LT, result is negative
|
||||
// 1 - 0x04 - P/GT, result is positive
|
||||
// 2 - 0x02 - Z/EQ, result is zero
|
||||
// 3 - 0x01 - S/SO, summary overflow (carry?)
|
||||
// XER:
|
||||
// 32 - SO
|
||||
// 33 - Overflow
|
||||
// 34 - Carry
|
||||
// NZCV -> (CR0.0, CR0.2, XER.34, XER.33)
|
||||
if (false) {
|
||||
//code.MFSPR(powah::GPR{1}, tmp4, powah::R0); // From XER
|
||||
/*uint64_t nzcv(uint64_t cr0, uint64_t xer) {
|
||||
return ((xer >> 33) & 1)
|
||||
| (((xer >> 34) & 1) << 1)
|
||||
| (((cr0 >> (32 + 2)) & 1) << 2)
|
||||
| (((cr0 >> (32 + 0)) & 1) << 3);
|
||||
}*/
|
||||
// auto const tmp9 = ctx.reg_alloc.ScratchGpr();
|
||||
// auto const tmp10 = ctx.reg_alloc.ScratchGpr();
|
||||
// code.SRDI(tmp9, tmp3, 34);
|
||||
// code.RLDICL(tmp10, tmp4, 31, 63);
|
||||
// code.SRDI(tmp3, tmp3, 32);
|
||||
// code.RLDIC(tmp9, tmp9, 2, 61);
|
||||
// code.OR(tmp9, tmp9, tmp10);
|
||||
// code.RLDIC(tmp3, tmp3, 3, 60);
|
||||
// code.SRDI(tmp4, tmp4, 34);
|
||||
// code.OR(tmp3, tmp9, tmp3);
|
||||
// code.RLDIC(tmp4, tmp4, 1, 62);
|
||||
// code.OR(tmp3, tmp3, tmp4);
|
||||
} else {
|
||||
// MFCR Fills RT 32:63, RT 0:31 left blank
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
code.MR(tmp3, PPC64::RNZCV);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetNZFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetUpperFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetLowerFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetCFlagFromNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::NZCVFromPackedFlags>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
namespace {
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool) {
|
||||
if (ctx.emit_conf.a64_variant) {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LI(tmp, terminal.next.Value());
|
||||
code.STD(tmp, PPC64::RJIT, offsetof(A64JitState, pc));
|
||||
code.LD(tmp, PPC64::RTOCPTR, 0);
|
||||
code.MTCTR(tmp);
|
||||
code.LD(powah::R2, PPC64::RTOCPTR, 8);
|
||||
code.LD(powah::R11, PPC64::RTOCPTR, 16);
|
||||
code.BCTR();
|
||||
} else {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LI(tmp, terminal.next.Value());
|
||||
code.STW(tmp, PPC64::RJIT, offsetof(A32JitState, regs) + sizeof(u32) * 15);
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool) {
|
||||
if (ctx.emit_conf.a64_variant) {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LI(tmp, terminal.next.Value());
|
||||
code.STD(tmp, PPC64::RJIT, offsetof(A64JitState, pc));
|
||||
code.LD(tmp, PPC64::RTOCPTR, 0);
|
||||
code.MTCTR(tmp);
|
||||
code.LD(powah::R2, PPC64::RTOCPTR, 8);
|
||||
code.LD(powah::R11, PPC64::RTOCPTR, 16);
|
||||
code.BCTR();
|
||||
} else {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LI(tmp, terminal.next.Value());
|
||||
code.STW(tmp, PPC64::RJIT, offsetof(A32JitState, regs) + sizeof(u32) * 15);
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::PopRSBHint, IR::LocationDescriptor, bool) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
powah::Label const l_else = code.DefineLabel();
|
||||
powah::Label const l_end = code.DefineLabel();
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.MR(tmp, PPC64::RCHECKBIT);
|
||||
code.CMPLDI(tmp, 0);
|
||||
code.BEQ(powah::CR0, l_else);
|
||||
// CheckBit == 1
|
||||
EmitTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
code.B(l_end);
|
||||
// CheckBit == 0
|
||||
code.LABEL(l_else);
|
||||
EmitTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.LABEL(l_end);
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
boost::apply_visitor([&](const auto& t) {
|
||||
EmitTerminal(code, ctx, t, initial_location, is_single_step);
|
||||
}, terminal);
|
||||
}
|
||||
}
|
||||
|
||||
EmittedBlockInfo EmitPPC64(powah::Context& code, IR::Block block, const EmitConfig& emit_conf) {
|
||||
EmittedBlockInfo ebi;
|
||||
RegAlloc reg_alloc{code};
|
||||
EmitContext ctx{block, reg_alloc, emit_conf, ebi};
|
||||
|
||||
size_t const stack_size = 112 + ABI_CALLEE_SAVED.size() * 8;
|
||||
auto const start_offset = code.offset;
|
||||
ebi.entry_point = &code.base[start_offset];
|
||||
if (!block.instructions.empty()) {
|
||||
code.MFLR(powah::R0);
|
||||
code.STD(powah::R0, powah::R1, 16);
|
||||
// Non-volatile saves
|
||||
std::vector<powah::GPR> gp_regs{ABI_CALLEE_SAVED};
|
||||
for (size_t i = 0; i < gp_regs.size(); ++i)
|
||||
code.STD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
|
||||
code.STDU(powah::R1, powah::R1, uint32_t(-stack_size));
|
||||
code.STD(powah::R2, powah::R1, 40);
|
||||
|
||||
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) {
|
||||
IR::Inst* inst = &*iter;
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
EmitIR<IR::Opcode::name>(code, ctx, inst); \
|
||||
break;
|
||||
#define A32OPC(name, type, ...) \
|
||||
case IR::Opcode::A32##name: \
|
||||
EmitIR<IR::Opcode::A32##name>(code, ctx, inst); \
|
||||
break;
|
||||
#define A64OPC(name, type, ...) \
|
||||
case IR::Opcode::A64##name: \
|
||||
EmitIR<IR::Opcode::A64##name>(code, ctx, inst); \
|
||||
break;
|
||||
#include "dynarmic/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
#undef A32OPC
|
||||
#undef A64OPC
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// auto const cycles_to_add = block.CycleCount();
|
||||
code.ADDI(powah::R1, powah::R1, stack_size);
|
||||
for (size_t i = 0; i < gp_regs.size(); ++i)
|
||||
code.LD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
|
||||
code.LD(powah::R0, powah::R1, 16);
|
||||
code.MTLR(powah::R0);
|
||||
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
|
||||
} else {
|
||||
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
|
||||
}
|
||||
code.ApplyRelocs();
|
||||
|
||||
/*
|
||||
llvm-objcopy -I binary -O elf64-powerpc --rename-section=.data=.text,code test.bin test.elf && llvm-objdump -d test.elf
|
||||
*/
|
||||
static FILE* fp = fopen("test.bin", "wb");
|
||||
fwrite(code.base, code.offset - start_offset, sizeof(uint32_t), fp);
|
||||
fflush(fp);
|
||||
|
||||
ebi.size = code.offset - start_offset;
|
||||
return ebi;
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,41 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
namespace biscuit {
|
||||
class Assembler;
|
||||
} // namespace biscuit
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
class Block;
|
||||
class Inst;
|
||||
enum class Cond;
|
||||
enum class Opcode;
|
||||
} // namespace Dynarmic::IR
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct EmittedBlockInfo {
|
||||
std::uint32_t* entry_point;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct EmitConfig {
|
||||
bool enable_cycle_counting;
|
||||
bool always_little_endian;
|
||||
bool a64_variant;
|
||||
};
|
||||
|
||||
struct EmitContext;
|
||||
|
||||
EmittedBlockInfo EmitPPC64(powah::Context& code, IR::Block block, const EmitConfig& emit_conf);
|
||||
template<IR::Opcode op>
|
||||
void EmitIR(powah::Context& code, EmitContext& ctx, IR::Inst* inst);
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,318 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/frontend/A32/a32_types.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCheckBit>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.ADDI(result, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
|
||||
code.LWZ(result, result, offsetof(A32JitState, regs));
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetVector>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
|
||||
auto const addr = ctx.reg_alloc.ScratchGpr();
|
||||
code.ADDI(addr, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
|
||||
code.STW(value, addr, offsetof(A32JitState, regs));
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetVector>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCVRaw>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCVQ>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZ>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZC>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetCFlag>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.LD(result, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.RLWINM(result, result, 31, 31, 31);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32OrQFlag>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetGEFlagsCompressed>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32BXWritePC>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32UpdateUpperLocationDescriptor>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CallSupervisor>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExceptionRaised>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32DataSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32DataMemoryBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32InstructionSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Memory
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ClearExclusive>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Coprocesor
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocInternalOperation>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocLoadWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocStoreWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,347 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/frontend/A64/a64_types.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetCheckBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.MR(PPC64::RCHECKBIT, value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCFlag>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetNZCVRaw>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetNZCVRaw>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.STW(value, PPC64::RJIT, offsetof(A64JitState, pstate));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetW>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
// Need to account for endianess here...
|
||||
#ifdef __ORDER_BIG_ENDIAN__
|
||||
constexpr u32 pe_offset64 = 4;
|
||||
#else
|
||||
constexpr u32 pe_offset64 = 0;
|
||||
#endif
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64)
|
||||
+ pe_offset64;
|
||||
code.LWZ(result, PPC64::RJIT, offs);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetX>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
|
||||
code.LD(result, PPC64::RJIT, offs);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetS>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetD>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetSP>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetFPCR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetFPSR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetW>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
|
||||
code.RLDICL(tmp, value, 0, 32);
|
||||
code.STD(tmp, PPC64::RJIT, offs);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetX>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
|
||||
code.STD(value, PPC64::RJIT, offs);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetS>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetD>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetSP>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetFPCR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetFPSR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetPC>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64CallSupervisor>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExceptionRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataCacheOperationRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64InstructionCacheOperationRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataSynchronizationBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataMemoryBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64InstructionSynchronizationBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCNTFRQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCNTPCT>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCTR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetDCZID>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetTPIDR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetTPIDRRO>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetTPIDR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Memory
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ClearExclusive>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,933 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
// uint64_t pack2x1(uint32_t lo, uint32_t hi) { return (uint64_t)lo | ((uint64_t)hi << 32); }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Pack2x32To1x64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const lo = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const hi = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.SLDI(result, hi, 32);
|
||||
code.OR(result, result, lo);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Pack2x64To1x128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// uint64_t lsw(uint64_t a) { return (uint32_t)a; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLDICL(result, source, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// uint64_t f(uint64_t a) { return (uint16_t)a; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantHalf>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xffff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// uint64_t f(uint64_t a) { return (uint8_t)a; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantByte>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// uint64_t msw(uint64_t a) { return a >> 32; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MostSignificantWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRDI(result, source, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return ((uint32_t)a) >> 31; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MostSignificantBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 1, 31, 31);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return (uint32_t)a == 0; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::IsZero32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZW(result, source);
|
||||
code.SRWI(result, result, 5);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return a == 0; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::IsZero64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZD(result, source);
|
||||
code.SRDI(result, result, 6);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return (a & 1) != 0; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::TestBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (inst->GetArg(1).IsImmediate()) {
|
||||
auto const shift = inst->GetArg(1).GetImmediateAsU64();
|
||||
if (shift > 0) {
|
||||
code.RLDICL(result, source, (64 - shift - 1) & 0x3f, 63);
|
||||
}
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
struct jit {
|
||||
uint64_t t;
|
||||
uint64_t a;
|
||||
};
|
||||
uint64_t f(jit *p, uint64_t a, uint64_t b) {
|
||||
bool n = (p->a & 0b1000) != 0;
|
||||
bool z = (p->a & 0b0100) != 0;
|
||||
bool c = (p->a & 0b0010) != 0;
|
||||
bool v = (p->a & 0b0001) != 0;
|
||||
return (p->a & 0b0001) == 0 ? a : b;
|
||||
}
|
||||
*/
|
||||
static powah::GPR EmitConditionalSelectX(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const nzcv = ctx.reg_alloc.ScratchGpr();
|
||||
auto const then_ = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const else_ = ctx.reg_alloc.UseGpr(inst->GetArg(2));
|
||||
switch (inst->GetArg(0).GetCond()) {
|
||||
case IR::Cond::EQ: // Z == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 4);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::NE: // Z == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 4);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::CS: // C == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 2);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::CC: // C == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 2);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::MI: // N == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 8);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::PL: // N == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 8);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::VS: // V == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::VC: // V == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, else_, then_);
|
||||
break;
|
||||
case IR::Cond::HI: // Z == 0 && C == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.RLWINM(nzcv, nzcv, 0, 29, 30);
|
||||
code.CMPLDI(nzcv, 2);
|
||||
code.ISELEQ(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::LS: // Z == 1 || C == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.RLWINM(nzcv, nzcv, 0, 29, 30);
|
||||
code.CMPLDI(nzcv, 2);
|
||||
code.ISELEQ(nzcv, else_, then_);
|
||||
break;
|
||||
case IR::Cond::GE: { // N == V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LWZ(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, else_, then_);
|
||||
} break;
|
||||
case IR::Cond::LT: { // N != V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LWZ(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
} break;
|
||||
case IR::Cond::GT: { // Z == 0 && N == V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
powah::Label const l_ne = code.DefineLabel();
|
||||
powah::Label const l_cc = code.DefineLabel();
|
||||
powah::Label const l_fi = code.DefineLabel();
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(tmp, nzcv, 4);
|
||||
code.BNE(powah::CR0, l_ne);
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.CMPLDI(nzcv, 0);
|
||||
code.BGT(powah::CR0, l_fi);
|
||||
code.LABEL(l_ne);
|
||||
code.MR(then_, else_);
|
||||
code.LABEL(l_cc);
|
||||
code.MR(nzcv, then_);
|
||||
code.LABEL(l_fi);
|
||||
} break;
|
||||
case IR::Cond::LE: { // Z == 1 || N != V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp2 = ctx.reg_alloc.ScratchGpr();
|
||||
powah::Label const l_ne = code.DefineLabel();
|
||||
code.MR(tmp2, then_);
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(tmp, nzcv, 4);
|
||||
code.BNE(powah::CR0, l_ne);
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(tmp2, then_, else_);
|
||||
code.LABEL(l_ne);
|
||||
code.MR(nzcv, tmp2);
|
||||
} break;
|
||||
default:
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
return nzcv;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelect32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = EmitConditionalSelectX(code, ctx, inst);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelect64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = EmitConditionalSelectX(code, ctx, inst);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelectNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = EmitConditionalSelectX(code, ctx, inst);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeft32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SLW(result, source, shift);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeft64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SLD(result, source, shift);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRW(result, source, shift);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a, uint64_t s) { return a >> s; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRD(result, source, shift);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRAW(result, source, shift);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRAD(result, source, shift);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// __builtin_rotateright32
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLW(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightExtended>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeftMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SLW(result, source, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeftMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SLD(result, source, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRW(result, source, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRD(result, source, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRAW(result, source, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRAD(result, source, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Add32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.ADDC_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
struct jit {
|
||||
uint32_t nzcv;
|
||||
};
|
||||
uint64_t addc(jit *p, uint64_t a, uint64_t b) {
|
||||
long long unsigned int e;
|
||||
uint64_t r = __builtin_addcll(a, b, p->nzcv & 0b0010, &e);
|
||||
p->nzcv = (p->nzcv & 0b1101) | e;
|
||||
return r;
|
||||
}
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Add64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.ADDC_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Sub32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SUBFC_(result, src_b, src_a);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Sub64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SUBFC_(result, src_b, src_a);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Mul32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLWO_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Mul64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLDO_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedMultiplyHigh64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLDO_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedMultiplyHigh64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLDO_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedDiv32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVWU_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedDiv64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVDU_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedDiv32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVW_(result, src_a, src_b);
|
||||
code.EXTSW(result, result);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedDiv64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVD_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::And32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp8 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp9 = PPC64::RNZCV;
|
||||
code.RLDICL(tmp3, src_a, 0, 32); // Truncate
|
||||
code.AND(tmp3, tmp3, src_b);
|
||||
code.CNTLZD(tmp9, tmp3);
|
||||
code.RLWINM(tmp8, tmp3, 0, 0, 0);
|
||||
code.SRDI(tmp9, tmp9, 6);
|
||||
code.SLDI(tmp9, tmp9, 30);
|
||||
code.OR(tmp9, tmp9, tmp8);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::And64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp10 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp9 = PPC64::RNZCV;
|
||||
code.AND(tmp3, src_a, src_b);
|
||||
code.CNTLZD(tmp10, tmp3);
|
||||
code.SRADI(tmp9, tmp3, 32);
|
||||
code.SRDI(tmp10, tmp10, 6);
|
||||
code.RLWINM(tmp9, tmp9, 0, 0, 0);
|
||||
code.SLDI(tmp10, tmp10, 30);
|
||||
code.OR(tmp9, tmp9, tmp10);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AndNot32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.NAND_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AndNot64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.NAND_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Eor32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.XOR_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Eor64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.XOR_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Or32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.OR_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Or64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.OR_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// TODO(lizzie): NZVC support for NOT
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Not32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NOT(result, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Not64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NOT(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendByteToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSB(result, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendHalfToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSH(result, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendByteToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSH(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendHalfToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSB(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendWordToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSW(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendByteToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendHalfToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xffff);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendByteToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendHalfToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xffff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendWordToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLDICL(result, source, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendLongToQuad>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// __builtin_bswap32
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (false) {
|
||||
//code.BRW(result, source);
|
||||
//code.RLDICL(result, result, 0, 32);
|
||||
} else {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.ROTLWI(result, source, 24);
|
||||
code.RLWIMI(result, source, 8, 8, 15);
|
||||
code.RLWIMI(result, source, 8, 24, 31);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
}
|
||||
|
||||
// __builtin_bswap64
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseHalf>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (false) {
|
||||
//code.BRH(result, source);
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
} else {
|
||||
code.ROTLWI(result, source, 24);
|
||||
code.RLWIMI(result, source, 8, 0, 23);
|
||||
code.CLRLDI(result, result, 48);
|
||||
}
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseDual>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (false) {
|
||||
//code.BRD(result, source);
|
||||
} else {
|
||||
auto const tmp10 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp9 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
code.MR(tmp3, source);
|
||||
code.ROTLWI(tmp10, tmp3, 24);
|
||||
code.SRDI(tmp9, tmp3, 32);
|
||||
code.RLWIMI(tmp10, tmp3, 8, 8, 15);
|
||||
code.RLWIMI(tmp10, tmp3, 8, 24, 31);
|
||||
code.ROTLWI(tmp3, tmp9, 24);
|
||||
code.RLWIMI(tmp3, tmp9, 8, 8, 15);
|
||||
code.RLWIMI(tmp3, tmp9, 8, 24, 31);
|
||||
code.RLDIMI(tmp3, tmp10, 32, 0);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
// __builtin_clz
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CountLeadingZeros32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZW(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// __builtin_clz
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CountLeadingZeros64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZD(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxSigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPD(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxSigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPD(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxUnsigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLW(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxUnsigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLD(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinSigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPW(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinSigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPD(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinUnsigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLW(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinUnsigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLD(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,458 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPCompare32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPCompare64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDiv32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDiv64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMax32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMax64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMaxNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMaxNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMin32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMin64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMinNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMinNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMul32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMul64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulX32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulX64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSqrt32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSqrt64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,380 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSubWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Packed
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAbsDiffSumU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSelect>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Crypto
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESDecryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESEncryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESInverseMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SM4AccessSubstitutionBox>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256Hash>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256MessageSchedule0>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256MessageSchedule1>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "dynarmic/interface/exclusive_monitor.h"
|
||||
#include <algorithm>
|
||||
#include "dynarmic/common/assert.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count)
|
||||
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {}
|
||||
|
||||
size_t ExclusiveMonitor::GetProcessorCount() const {
|
||||
return exclusive_addresses.size();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Lock() {
|
||||
lock.Lock();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Unlock() {
|
||||
lock.Unlock();
|
||||
}
|
||||
|
||||
bool ExclusiveMonitor::CheckAndClear(std::size_t processor_id, VAddr address) {
|
||||
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
|
||||
Lock();
|
||||
if (exclusive_addresses[processor_id] != masked_address) {
|
||||
Unlock();
|
||||
return false;
|
||||
}
|
||||
for (VAddr& other_address : exclusive_addresses)
|
||||
if (other_address == masked_address)
|
||||
other_address = INVALID_EXCLUSIVE_ADDRESS;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Clear() {
|
||||
Lock();
|
||||
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::ClearProcessor(size_t processor_id) {
|
||||
Lock();
|
||||
exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic
|
||||
@@ -1,26 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
enum class HostLoc : uint8_t {
|
||||
R0, R1, R2, R3, R4, R5, R6, R7, R8, R9,
|
||||
R10, R11, R12, R13, R14, R15, R16, R17, R18, R19,
|
||||
R20, R21, R22, R23, R24, R25, R26, R27, R28, R29,
|
||||
R30, R31,
|
||||
FR0, FR1, FR2, FR3, FR4, FR5, FR6, FR7, FR8, FR9,
|
||||
FR10, FR11, FR12, FR13, FR14, FR15, FR16, FR17, FR18, FR19,
|
||||
FR20, FR21, FR22, FR23, FR24, FR25, FR26, FR27, FR28, FR29,
|
||||
FR30, FR31,
|
||||
VR0, VR1, VR2, VR3, VR4, VR5, VR6, VR7, VR8, VR9,
|
||||
VR10, VR11, VR12, VR13, VR14, VR15, VR16, VR17, VR18, VR19,
|
||||
VR20, VR21, VR22, VR23, VR24, VR25, VR26, VR27, VR28, VR29,
|
||||
VR30, VR31,
|
||||
FirstSpill,
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::PPC64
|
||||
@@ -1,213 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <ranges>
|
||||
#include <variant>
|
||||
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/common/always_false.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
constexpr size_t spill_offset = offsetof(StackLayout, spill);
|
||||
constexpr size_t spill_slot_size = sizeof(decltype(StackLayout::spill)::value_type);
|
||||
|
||||
static bool IsValuelessType(IR::Type type) {
|
||||
return type == IR::Type::Table;
|
||||
}
|
||||
|
||||
void HostLocInfo::UpdateUses() {
|
||||
accumulated_uses += uses_this_inst;
|
||||
uses_this_inst = 0;
|
||||
if (accumulated_uses == expected_uses) {
|
||||
values.clear();
|
||||
accumulated_uses = 0;
|
||||
expected_uses = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool RegAlloc::IsValueLive(IR::Inst* inst) const {
|
||||
return !!ValueLocation(inst);
|
||||
}
|
||||
|
||||
void RegAlloc::UpdateAllUses() {
|
||||
for (auto& gpr : gprs)
|
||||
gpr.UpdateUses();
|
||||
for (auto& fpr : fprs)
|
||||
fpr.UpdateUses();
|
||||
for (auto& spill : spills)
|
||||
spill.UpdateUses();
|
||||
}
|
||||
|
||||
void RegAlloc::DefineAsExisting(IR::Inst* inst, IR::Value arg) {
|
||||
ASSERT(!ValueLocation(inst));
|
||||
if (arg.IsImmediate()) {
|
||||
inst->ReplaceUsesWith(arg);
|
||||
} else {
|
||||
auto& info = ValueInfo(arg.GetInst());
|
||||
info.values.emplace_back(inst);
|
||||
info.expected_uses += inst->UseCount();
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::AssertNoMoreUses() const {
|
||||
const auto is_empty = [](const auto& i) { return i.IsCompletelyEmpty(); };
|
||||
ASSERT(std::all_of(gprs.begin(), gprs.end(), is_empty));
|
||||
ASSERT(std::all_of(fprs.begin(), fprs.end(), is_empty));
|
||||
ASSERT(std::all_of(spills.begin(), spills.end(), is_empty));
|
||||
}
|
||||
|
||||
std::optional<u32> RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs) const {
|
||||
auto const order = PPC64::GPR_ORDER;
|
||||
if (auto const it = std::find_if(order.begin(), order.end(), [&](u32 i) {
|
||||
return regs[i].values.empty() && !regs[i].locked;
|
||||
}); it != order.end())
|
||||
return *it;
|
||||
// TODO: Actual proper LRU
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void RegAlloc::SpillGpr(u32 index) {
|
||||
ASSERT(!gprs[index].locked && !gprs[index].realized);
|
||||
if (!gprs[index].values.empty()) {
|
||||
const u32 new_location_index = FindFreeSpill();
|
||||
code.STD(powah::GPR{index}, powah::R1, spill_offset + new_location_index * spill_slot_size);
|
||||
spills[new_location_index] = std::exchange(gprs[index], {});
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::SpillFpr(u32 index) {
|
||||
ASSERT(!fprs[index].locked && !fprs[index].realized);
|
||||
if (!fprs[index].values.empty()) {
|
||||
const u32 new_location_index = FindFreeSpill();
|
||||
//code.FSD(powah::FPR{index}, spill_offset + new_location_index * spill_slot_size, powah::sp);
|
||||
spills[new_location_index] = std::exchange(fprs[index], {});
|
||||
}
|
||||
}
|
||||
|
||||
u32 RegAlloc::FindFreeSpill() const {
|
||||
const auto iter = std::find_if(spills.begin(), spills.end(), [](const HostLocInfo& info) { return info.values.empty(); });
|
||||
ASSERT(iter != spills.end() && "All spill locations are full");
|
||||
return u32(iter - spills.begin());
|
||||
}
|
||||
|
||||
std::optional<HostLoc> RegAlloc::ValueLocation(const IR::Inst* value) const {
|
||||
const auto fn = [value](const HostLocInfo& info) {
|
||||
return info.Contains(value);
|
||||
};
|
||||
if (const auto iter = std::ranges::find_if(gprs, fn); iter != gprs.end())
|
||||
return HostLoc(u32(HostLoc::R0) + u32(iter - gprs.begin()));
|
||||
else if (const auto iter = std::ranges::find_if(fprs, fn); iter != fprs.end())
|
||||
return HostLoc(u32(HostLoc::FR0) + u32(iter - fprs.begin()));
|
||||
else if (const auto iter = std::ranges::find_if(vprs, fn); iter != vprs.end())
|
||||
return HostLoc(u32(HostLoc::VR0) + u32(iter - vprs.begin()));
|
||||
else if (const auto iter = std::ranges::find_if(spills, fn); iter != spills.end())
|
||||
return HostLoc(u32(HostLoc::FirstSpill) + u32(iter - spills.begin()));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline bool HostLocIsGpr(HostLoc h) noexcept {
|
||||
return u8(h) >= u8(HostLoc::R0) && u8(h) <= u8(HostLoc::R31);
|
||||
}
|
||||
inline bool HostLocIsFpr(HostLoc h) noexcept {
|
||||
return u8(h) >= u8(HostLoc::FR0) && u8(h) <= u8(HostLoc::FR31);
|
||||
}
|
||||
inline bool HostLocIsVpr(HostLoc h) noexcept {
|
||||
return u8(h) >= u8(HostLoc::VR0) && u8(h) <= u8(HostLoc::VR31);
|
||||
}
|
||||
inline std::variant<powah::GPR, powah::FPR> HostLocToReg(HostLoc h) noexcept {
|
||||
if (HostLocIsGpr(h))
|
||||
return powah::GPR{uint32_t(h)};
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
HostLocInfo& RegAlloc::ValueInfo(HostLoc h) {
|
||||
if (u8(h) >= u8(HostLoc::R0) && u8(h) <= u8(HostLoc::R31))
|
||||
return gprs[size_t(h)];
|
||||
else if (u8(h) >= u8(HostLoc::FR0) && u8(h) <= u8(HostLoc::FR31))
|
||||
return gprs[size_t(h) - size_t(HostLoc::FR0)];
|
||||
else if (u8(h) >= u8(HostLoc::VR0) && u8(h) <= u8(HostLoc::VR31))
|
||||
return vprs[size_t(h) - size_t(HostLoc::VR0)];
|
||||
auto const index = size_t(h) - size_t(HostLoc::FirstSpill);
|
||||
ASSERT(index <= spills.size());
|
||||
return spills[index];
|
||||
}
|
||||
|
||||
HostLocInfo& RegAlloc::ValueInfo(const IR::Inst* value) {
|
||||
const auto fn = [value](const HostLocInfo& info) {
|
||||
return info.Contains(value);
|
||||
};
|
||||
if (const auto iter = std::find_if(gprs.begin(), gprs.end(), fn); iter != gprs.end())
|
||||
return *iter;
|
||||
else if (const auto iter = std::find_if(fprs.begin(), fprs.end(), fn); iter != fprs.end())
|
||||
return *iter;
|
||||
else if (const auto iter = std::find_if(spills.begin(), spills.end(), fn); iter != spills.end())
|
||||
return *iter;
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
/// @brief Defines a register RegLock to use (and locks it)
|
||||
RegLock<powah::GPR> RegAlloc::ScratchGpr() {
|
||||
auto const r = AllocateRegister(gprs);
|
||||
return RegLock(*this, powah::GPR{*r});
|
||||
}
|
||||
|
||||
/// @brief Uses the given GPR of the argument
|
||||
RegLock<powah::GPR> RegAlloc::UseGpr(IR::Value arg) {
|
||||
if (arg.IsImmediate()) {
|
||||
// HOLY SHIT EVIL HAXX
|
||||
auto const reg = ScratchGpr();
|
||||
auto const imm = arg.GetImmediateAsU64();
|
||||
if (imm >= 0xffffffff) {
|
||||
auto const lo = uint32_t(imm >> 0), hi = uint32_t(imm >> 32);
|
||||
if (lo == hi) {
|
||||
code.LIS(reg, imm >> 16);
|
||||
code.ORI(reg, reg, imm & 0xffff);
|
||||
code.RLDIMI(reg, reg, 32, 0);
|
||||
} else {
|
||||
ASSERT(false && "larger >32bit imms");
|
||||
}
|
||||
} else if (imm > 0xffff && imm <= 0xffffffff) {
|
||||
code.LIS(reg, imm >> 16);
|
||||
code.ORI(reg, reg, imm & 0xffff);
|
||||
} else if (imm <= 0xffff) {
|
||||
code.LI(reg, imm);
|
||||
}
|
||||
return reg;
|
||||
} else {
|
||||
auto const loc = ValueLocation(arg.GetInst());
|
||||
ASSERT(loc && HostLocIsGpr(*loc));
|
||||
return RegLock(*this, std::get<powah::GPR>(HostLocToReg(*loc)));
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::DefineValue(IR::Inst* inst, powah::GPR const gpr) noexcept {
|
||||
ASSERT(!ValueLocation(inst) && "inst has already been defined");
|
||||
ValueInfo(HostLoc(gpr.index)).values.push_back(inst);
|
||||
}
|
||||
|
||||
void RegAlloc::DefineValue(IR::Inst* inst, IR::Value arg) noexcept {
|
||||
ASSERT(!ValueLocation(inst) && "inst has already been defined");
|
||||
if (arg.IsImmediate()) {
|
||||
HostLoc const loc{u8(ScratchGpr().value.index)};
|
||||
ValueInfo(loc).values.push_back(inst);
|
||||
auto const value = arg.GetImmediateAsU64();
|
||||
if (value >= 0x7fff) {
|
||||
ASSERT(false && "unimp");
|
||||
} else {
|
||||
//code.LI(HostLocToReg(loc), value);
|
||||
}
|
||||
} else {
|
||||
ASSERT(ValueLocation(arg.GetInst()) && "arg must already be defined");
|
||||
const HostLoc loc = *ValueLocation(arg.GetInst());
|
||||
ValueInfo(loc).values.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,109 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/backend/ppc64/stack_layout.h"
|
||||
#include "dynarmic/backend/ppc64/hostloc.h"
|
||||
#include "dynarmic/ir/cond.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/value.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct HostLocInfo final {
|
||||
std::vector<const IR::Inst*> values;
|
||||
size_t uses_this_inst = 0;
|
||||
size_t accumulated_uses = 0;
|
||||
size_t expected_uses = 0;
|
||||
/// @brief Lock usage of this register UNTIL a DefineValue() is issued
|
||||
bool locked = false;
|
||||
bool realized = false;
|
||||
bool Contains(const IR::Inst* value) const {
|
||||
return std::find(values.begin(), values.end(), value) != values.end();
|
||||
}
|
||||
void SetupScratchLocation() {
|
||||
ASSERT(IsCompletelyEmpty());
|
||||
realized = true;
|
||||
}
|
||||
bool IsCompletelyEmpty() const {
|
||||
return values.empty() && !locked && !realized && !accumulated_uses && !expected_uses && !uses_this_inst;
|
||||
}
|
||||
void UpdateUses();
|
||||
};
|
||||
|
||||
struct RegAlloc;
|
||||
|
||||
/// @brief Allows to use RAII to denote liveness/locking of a given register
|
||||
/// this basically means that we can use temporals and not need to go thru
|
||||
/// any weird deallocation stuffs :)
|
||||
template<typename T> struct RegLock {
|
||||
constexpr RegLock(RegAlloc& reg_alloc, T const value) noexcept
|
||||
: reg_alloc{reg_alloc}
|
||||
, value{value}
|
||||
{
|
||||
SetLock(true);
|
||||
}
|
||||
inline ~RegLock() noexcept { SetLock(false); }
|
||||
constexpr operator T const&() noexcept { return value; }
|
||||
constexpr operator T() const noexcept { return value; }
|
||||
inline void SetLock(bool v) noexcept;
|
||||
RegAlloc& reg_alloc;
|
||||
const T value;
|
||||
};
|
||||
|
||||
struct RegAlloc {
|
||||
explicit RegAlloc(powah::Context& code) : code{code} {}
|
||||
bool IsValueLive(IR::Inst* inst) const;
|
||||
void DefineAsExisting(IR::Inst* inst, IR::Value arg);
|
||||
|
||||
void SpillAll();
|
||||
void UpdateAllUses();
|
||||
void AssertNoMoreUses() const;
|
||||
|
||||
RegLock<powah::GPR> ScratchGpr();
|
||||
RegLock<powah::GPR> UseGpr(IR::Value arg);
|
||||
void DefineValue(IR::Inst* inst, powah::GPR const gpr) noexcept;
|
||||
void DefineValue(IR::Inst* inst, IR::Value arg) noexcept;
|
||||
private:
|
||||
template<typename T>
|
||||
friend struct RegLock;
|
||||
|
||||
std::optional<u32> AllocateRegister(const std::array<HostLocInfo, 32>& regs) const;
|
||||
void SpillGpr(u32 index);
|
||||
void SpillFpr(u32 index);
|
||||
u32 FindFreeSpill() const;
|
||||
|
||||
std::optional<HostLoc> ValueLocation(const IR::Inst* value) const;
|
||||
HostLocInfo& ValueInfo(HostLoc host_loc);
|
||||
HostLocInfo& ValueInfo(const IR::Inst* value);
|
||||
|
||||
powah::Context& code;
|
||||
std::array<HostLocInfo, 32> gprs;
|
||||
std::array<HostLocInfo, 32> fprs;
|
||||
std::array<HostLocInfo, 32> vprs;
|
||||
std::array<HostLocInfo, SpillCount> spills;
|
||||
};
|
||||
|
||||
template<> inline void RegLock<powah::GPR>::SetLock(bool v) noexcept {
|
||||
reg_alloc.gprs[value.index].locked = v;
|
||||
}
|
||||
template<> inline void RegLock<powah::FPR>::SetLock(bool v) noexcept {
|
||||
reg_alloc.fprs[value.index].locked = v;
|
||||
}
|
||||
template<> inline void RegLock<powah::VPR>::SetLock(bool v) noexcept {
|
||||
reg_alloc.vprs[value.index].locked = v;
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,20 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
constexpr size_t SpillCount = 16;
|
||||
|
||||
struct alignas(16) StackLayout {
|
||||
std::array<u64, SpillCount> spill;
|
||||
};
|
||||
|
||||
static_assert(sizeof(StackLayout) % 16 == 0);
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,71 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
#include <sys/mman.h>
|
||||
#include <powah_emit.hpp>
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/hostloc.h"
|
||||
#include "dynarmic/common/spin_lock.h"
|
||||
#include "dynarmic/common/assert.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
/*
|
||||
void acquire(atomic_flag* lock) {
|
||||
while(atomic_flag_test_and_set_explicit( lock, memory_order_acquire))
|
||||
;
|
||||
}
|
||||
*/
|
||||
void EmitSpinLockLock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
void release(atomic_flag* lock) {
|
||||
atomic_flag_clear_explicit(lock, memory_order_release);
|
||||
}
|
||||
*/
|
||||
void EmitSpinLockUnlock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp) {
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct SpinLockImpl {
|
||||
void Initialize();
|
||||
powah::Context code;
|
||||
void* page = nullptr;
|
||||
void (*lock)(volatile int*);
|
||||
void (*unlock)(volatile int*);
|
||||
};
|
||||
|
||||
std::once_flag flag;
|
||||
SpinLockImpl impl;
|
||||
|
||||
void SpinLockImpl::Initialize() {
|
||||
page = mmap(nullptr, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
ASSERT(page != nullptr);
|
||||
code = powah::Context(page, 4096);
|
||||
lock = reinterpret_cast<void (*)(volatile int*)>(code.base);
|
||||
EmitSpinLockLock(code, Backend::PPC64::ABI_PARAM1, Backend::PPC64::ABI_PARAM2);
|
||||
code.BLR();
|
||||
unlock = reinterpret_cast<void (*)(volatile int*)>(code.base);
|
||||
EmitSpinLockUnlock(code, Backend::PPC64::ABI_PARAM1, Backend::PPC64::ABI_PARAM2);
|
||||
code.BLR();
|
||||
// TODO: free the page, rework the stupid spinlock API
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SpinLock::Lock() noexcept {
|
||||
std::call_once(flag, &SpinLockImpl::Initialize, impl);
|
||||
impl.lock(&storage);
|
||||
}
|
||||
|
||||
void SpinLock::Unlock() noexcept {
|
||||
std::call_once(flag, &SpinLockImpl::Initialize, impl);
|
||||
impl.unlock(&storage);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic
|
||||
@@ -1,13 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
void EmitSpinLockLock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp);
|
||||
void EmitSpinLockUnlock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp);
|
||||
|
||||
} // namespace Dynarmic
|
||||
@@ -522,9 +522,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
||||
PAIR(AnisotropyMode, X4, tr("4x")),
|
||||
PAIR(AnisotropyMode, X8, tr("8x")),
|
||||
PAIR(AnisotropyMode, X16, tr("16x")),
|
||||
PAIR(AnisotropyMode, X32, tr("32x")),
|
||||
PAIR(AnisotropyMode, X64, tr("64x")),
|
||||
PAIR(AnisotropyMode, None, tr("None")),
|
||||
}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::Language>::Index(),
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -50,12 +53,6 @@ constexpr std::array RGBA_LUT{
|
||||
R | G | B | A, //
|
||||
};
|
||||
|
||||
void CheckAlignment(IR::Reg reg, size_t alignment) {
|
||||
if (!IR::IsAligned(reg, alignment)) {
|
||||
throw NotImplementedException("Unaligned source register {}", reg);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
IR::Value Composite(TranslatorVisitor& v, Args... regs) {
|
||||
return v.ir.CompositeConstruct(v.F(regs)...);
|
||||
@@ -86,67 +83,53 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info);
|
||||
case 3: // 2D.LL
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {},
|
||||
info);
|
||||
case 4: // 2D.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
|
||||
{}, {}, {}, info);
|
||||
case 5: // 2D.LL.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1),
|
||||
v.F(reg_b + 1), v.F(reg_b), {}, info);
|
||||
case 6: // 2D.LZ.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
|
||||
zero, {}, info);
|
||||
case 7: // ARRAY_2D
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::ColorArray2D);
|
||||
return v.ir.ImageSampleImplicitLod(
|
||||
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
|
||||
{}, {}, {}, info);
|
||||
case 8: // ARRAY_2D.LZ
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::ColorArray2D);
|
||||
return v.ir.ImageSampleExplicitLod(
|
||||
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
|
||||
zero, {}, info);
|
||||
case 9: // ARRAY_2D.LZ.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
info.type.Assign(TextureType::ColorArray2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefExplicitLod(
|
||||
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
|
||||
v.F(reg_b + 1), zero, {}, info);
|
||||
case 10: // 3D
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color3D);
|
||||
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
|
||||
{}, info);
|
||||
case 11: // 3D.LZ
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color3D);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {},
|
||||
info);
|
||||
case 12: // CUBE
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::ColorCube);
|
||||
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
|
||||
{}, info);
|
||||
case 13: // CUBE.LL
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
info.type.Assign(TextureType::ColorCube);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b),
|
||||
v.F(reg_b + 1), {}, info);
|
||||
@@ -187,12 +170,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
|
||||
case 0:
|
||||
return texs.dest_reg_a;
|
||||
case 1:
|
||||
CheckAlignment(texs.dest_reg_a, 2);
|
||||
return texs.dest_reg_a + 1;
|
||||
case 2:
|
||||
return texs.dest_reg_b;
|
||||
case 3:
|
||||
CheckAlignment(texs.dest_reg_b, 2);
|
||||
return texs.dest_reg_b + 1;
|
||||
}
|
||||
throw LogicError("Invalid store index {}", index);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -34,12 +37,6 @@ union Encoding {
|
||||
BitField<36, 13, u64> cbuf_offset;
|
||||
};
|
||||
|
||||
void CheckAlignment(IR::Reg reg, size_t alignment) {
|
||||
if (!IR::IsAligned(reg, alignment)) {
|
||||
throw NotImplementedException("Unaligned source register {}", reg);
|
||||
}
|
||||
}
|
||||
|
||||
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
|
||||
const IR::U32 value{v.X(reg)};
|
||||
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
|
||||
@@ -60,18 +57,15 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0);
|
||||
IR::Value coords;
|
||||
if (tld4s.aoffi != 0) {
|
||||
CheckAlignment(reg_a, 2);
|
||||
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
|
||||
IR::Value offset = MakeOffset(v, reg_b);
|
||||
if (tld4s.dc != 0) {
|
||||
CheckAlignment(reg_b, 2);
|
||||
IR::F32 dref = v.F(reg_b + 1);
|
||||
return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info);
|
||||
}
|
||||
return v.ir.ImageGather(handle, coords, offset, {}, info);
|
||||
}
|
||||
if (tld4s.dc != 0) {
|
||||
CheckAlignment(reg_a, 2);
|
||||
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
|
||||
IR::F32 dref = v.F(reg_b);
|
||||
return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info);
|
||||
@@ -86,12 +80,10 @@ IR::Reg RegStoreComponent32(u64 insn, size_t index) {
|
||||
case 0:
|
||||
return tlds4.dest_reg_a;
|
||||
case 1:
|
||||
CheckAlignment(tlds4.dest_reg_a, 2);
|
||||
return tlds4.dest_reg_a + 1;
|
||||
case 2:
|
||||
return tlds4.dest_reg_b;
|
||||
case 3:
|
||||
CheckAlignment(tlds4.dest_reg_b, 2);
|
||||
return tlds4.dest_reg_b + 1;
|
||||
}
|
||||
throw LogicError("Invalid store index {}", index);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -55,12 +58,6 @@ union Encoding {
|
||||
BitField<53, 4, u64> encoding;
|
||||
};
|
||||
|
||||
void CheckAlignment(IR::Reg reg, size_t alignment) {
|
||||
if (!IR::IsAligned(reg, alignment)) {
|
||||
throw NotImplementedException("Unaligned source register {}", reg);
|
||||
}
|
||||
}
|
||||
|
||||
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
|
||||
const IR::U32 value{v.X(reg)};
|
||||
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
|
||||
@@ -92,38 +89,31 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b));
|
||||
break;
|
||||
case 4:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
offsets = MakeOffset(v, reg_b);
|
||||
break;
|
||||
case 5:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
lod = v.X(reg_b);
|
||||
break;
|
||||
case 6:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
multisample = v.X(reg_b);
|
||||
break;
|
||||
case 7:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color3D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b));
|
||||
break;
|
||||
case 8: {
|
||||
CheckAlignment(reg_b, 2);
|
||||
const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))};
|
||||
texture_type = Shader::TextureType::ColorArray2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array);
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
lod = v.X(reg_b);
|
||||
@@ -166,12 +156,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
|
||||
case 0:
|
||||
return tlds.dest_reg_a;
|
||||
case 1:
|
||||
CheckAlignment(tlds.dest_reg_a, 2);
|
||||
return tlds.dest_reg_a + 1;
|
||||
case 2:
|
||||
return tlds.dest_reg_b;
|
||||
case 3:
|
||||
CheckAlignment(tlds.dest_reg_b, 2);
|
||||
return tlds.dest_reg_b + 1;
|
||||
}
|
||||
throw LogicError("Invalid store index {}", index);
|
||||
|
||||
@@ -169,11 +169,36 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
|
||||
return mapping;
|
||||
}
|
||||
|
||||
struct PassthroughVertices {
|
||||
u32 count;
|
||||
u32 first;
|
||||
u32 stride;
|
||||
};
|
||||
|
||||
PassthroughVertices GetPassthroughVertices(InputTopology input_topology) {
|
||||
switch (input_topology) {
|
||||
case InputTopology::Points:
|
||||
return {1, 0, 1};
|
||||
case InputTopology::Lines:
|
||||
return {2, 0, 1};
|
||||
case InputTopology::LinesAdjacency:
|
||||
return {2, 1, 1};
|
||||
case InputTopology::Triangles:
|
||||
return {3, 0, 1};
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
return {3, 0, 2};
|
||||
}
|
||||
return {3, 0, 1};
|
||||
}
|
||||
|
||||
void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
|
||||
const Shader::VaryingState& passthrough_mask,
|
||||
bool passthrough_position,
|
||||
std::optional<IR::Attribute> passthrough_layer_attr) {
|
||||
for (u32 i = 0; i < program.output_vertices; i++) {
|
||||
std::optional<IR::Attribute> passthrough_layer_attr,
|
||||
InputTopology input_topology) {
|
||||
const PassthroughVertices vertices{GetPassthroughVertices(input_topology)};
|
||||
for (u32 vertex = 0; vertex < vertices.count; vertex++) {
|
||||
const u32 i = vertices.first + vertex * vertices.stride;
|
||||
// Assign generics from input
|
||||
for (u32 j = 0; j < 32; j++) {
|
||||
if (!passthrough_mask.Generic(j)) {
|
||||
@@ -208,25 +233,16 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
|
||||
ir.EndPrimitive(ir.Imm32(0));
|
||||
}
|
||||
|
||||
u32 GetOutputTopologyVertices(OutputTopology output_topology) {
|
||||
switch (output_topology) {
|
||||
case OutputTopology::PointList:
|
||||
return 1;
|
||||
case OutputTopology::LineStrip:
|
||||
return 2;
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
|
||||
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info,
|
||||
InputTopology input_topology) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
if (inst.GetOpcode() == IR::Opcode::Epilogue) {
|
||||
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
EmitGeometryPassthrough(
|
||||
ir, program, program.info.passthrough,
|
||||
program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {});
|
||||
program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {},
|
||||
input_topology);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,7 +251,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info,
|
||||
InputTopology input_topology) {
|
||||
HostTranslateInfo normalized_host_info{host_info};
|
||||
normalized_host_info.ApplyDescriptorLimitPolicy();
|
||||
|
||||
@@ -264,8 +281,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
}
|
||||
|
||||
if (!normalized_host_info.support_geometry_shader_passthrough) {
|
||||
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
|
||||
LowerGeometryPassthrough(program, normalized_host_info);
|
||||
program.output_vertices = GetPassthroughVertices(input_topology).count;
|
||||
LowerGeometryPassthrough(program, normalized_host_info, input_topology);
|
||||
program.is_geometry_passthrough = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -414,11 +432,12 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool,
|
||||
const HostTranslateInfo& host_info,
|
||||
IR::Program& source_program,
|
||||
Shader::OutputTopology output_topology) {
|
||||
Shader::OutputTopology output_topology,
|
||||
InputTopology input_topology) {
|
||||
IR::Program program;
|
||||
program.stage = Stage::Geometry;
|
||||
program.output_topology = output_topology;
|
||||
program.output_vertices = GetOutputTopologyVertices(output_topology);
|
||||
program.output_vertices = GetPassthroughVertices(input_topology).count;
|
||||
|
||||
program.is_geometry_passthrough = false;
|
||||
program.info.loads.mask = source_program.info.stores.mask;
|
||||
@@ -433,7 +452,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||
|
||||
IR::IREmitter ir{*current_block};
|
||||
EmitGeometryPassthrough(ir, program, program.info.stores, true,
|
||||
source_program.info.emulated_layer);
|
||||
source_program.info.emulated_layer, input_topology);
|
||||
|
||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
||||
IR::IREmitter{*return_block}.Epilogue();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,8 @@ namespace Shader::Maxwell {
|
||||
|
||||
[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
||||
Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||
Flow::CFG& cfg, const HostTranslateInfo& host_info,
|
||||
InputTopology input_topology);
|
||||
|
||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
|
||||
Environment& env_vertex_b);
|
||||
@@ -32,6 +36,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf
|
||||
ObjectPool<IR::Block>& block_pool,
|
||||
const HostTranslateInfo& host_info,
|
||||
IR::Program& source_program,
|
||||
Shader::OutputTopology output_topology);
|
||||
Shader::OutputTopology output_topology,
|
||||
InputTopology input_topology);
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -267,6 +267,38 @@ add_library(video_core STATIC
|
||||
video_core.h
|
||||
)
|
||||
|
||||
if (ENABLE_LSFG)
|
||||
target_sources(video_core PRIVATE
|
||||
frame_gen/lossless_dll.cpp
|
||||
frame_gen/lossless_dll.h
|
||||
frame_gen/lsfg_translate.cpp
|
||||
frame_gen/lsfg_translate.h
|
||||
renderer_vulkan/present/frame_gen.cpp
|
||||
renderer_vulkan/present/frame_gen.h
|
||||
renderer_vulkan/present/frame_gen_pacer.cpp
|
||||
renderer_vulkan/present/frame_gen_pacer.h
|
||||
renderer_vulkan/present/lsfg_alpha.cpp
|
||||
renderer_vulkan/present/lsfg_alpha.h
|
||||
renderer_vulkan/present/lsfg_beta.cpp
|
||||
renderer_vulkan/present/lsfg_beta.h
|
||||
renderer_vulkan/present/lsfg_chain.cpp
|
||||
renderer_vulkan/present/lsfg_chain.h
|
||||
renderer_vulkan/present/lsfg_common.cpp
|
||||
renderer_vulkan/present/lsfg_common.h
|
||||
renderer_vulkan/present/lsfg_delta.cpp
|
||||
renderer_vulkan/present/lsfg_delta.h
|
||||
renderer_vulkan/present/lsfg_gamma.cpp
|
||||
renderer_vulkan/present/lsfg_gamma.h
|
||||
renderer_vulkan/present/lsfg_generate.cpp
|
||||
renderer_vulkan/present/lsfg_generate.h
|
||||
renderer_vulkan/present/lsfg_mipmaps.cpp
|
||||
renderer_vulkan/present/lsfg_mipmaps.h
|
||||
renderer_vulkan/present/lsfg_shaders.cpp
|
||||
renderer_vulkan/present/lsfg_shaders.h
|
||||
)
|
||||
target_compile_definitions(video_core PUBLIC HAS_LSFG)
|
||||
endif()
|
||||
|
||||
if (ENABLE_OPENGL)
|
||||
target_sources(video_core PRIVATE
|
||||
renderer_opengl/present/filters.cpp
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
@@ -809,46 +810,46 @@ void BufferCache<P>::BindHostVertexBuffers() {
|
||||
|
||||
if (use_optimized_vertex_buffers) {
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
u32 enabled_mask = enabled_vertex_buffers_mask;
|
||||
HostBindings<Buffer> bindings{};
|
||||
u32 last_index = (std::numeric_limits<u32>::max)();
|
||||
const auto flush_bindings = [&]() {
|
||||
if (bindings.buffers.empty()) {
|
||||
return;
|
||||
}
|
||||
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
|
||||
runtime.BindVertexBuffers(bindings);
|
||||
bindings = HostBindings<Buffer>{};
|
||||
last_index = (std::numeric_limits<u32>::max)();
|
||||
};
|
||||
while (enabled_mask != 0) {
|
||||
const u32 index = std::countr_zero(enabled_mask);
|
||||
enabled_mask &= (enabled_mask - 1);
|
||||
const u32 enabled_mask = enabled_vertex_buffers_mask;
|
||||
bool any_dirty = false;
|
||||
u32 pending_mask = enabled_mask;
|
||||
while (pending_mask != 0) {
|
||||
const u32 index = std::countr_zero(pending_mask);
|
||||
pending_mask &= (pending_mask - 1);
|
||||
const Binding& binding = VertexBufferSlot(index);
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
|
||||
if (!flags[Dirty::VertexBuffer0 + index]) {
|
||||
flush_bindings();
|
||||
continue;
|
||||
}
|
||||
any_dirty |= flags[Dirty::VertexBuffer0 + index];
|
||||
}
|
||||
if (enabled_mask == 0 || !any_dirty) {
|
||||
return;
|
||||
}
|
||||
const u32 min_index = static_cast<u32>(std::countr_zero(enabled_mask));
|
||||
const u32 max_index = 32u - static_cast<u32>(std::countl_zero(enabled_mask));
|
||||
HostBindings<Buffer> bindings{};
|
||||
bindings.min_index = min_index;
|
||||
bindings.max_index = max_index;
|
||||
for (u32 index = min_index; index < max_index; ++index) {
|
||||
flags[Dirty::VertexBuffer0 + index] = false;
|
||||
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
|
||||
if ((enabled_mask & (1u << index)) == 0) {
|
||||
bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]);
|
||||
bindings.offsets.push_back(0);
|
||||
bindings.sizes.push_back(0);
|
||||
bindings.strides.push_back(stride);
|
||||
continue;
|
||||
}
|
||||
const Binding& binding = VertexBufferSlot(index);
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
buffer.MarkUsage(offset, binding.size);
|
||||
if (!bindings.buffers.empty() && index != last_index + 1) {
|
||||
flush_bindings();
|
||||
}
|
||||
if (bindings.buffers.empty()) {
|
||||
bindings.min_index = index;
|
||||
}
|
||||
bindings.buffers.push_back(&buffer);
|
||||
bindings.offsets.push_back(offset);
|
||||
bindings.sizes.push_back(binding.size);
|
||||
bindings.strides.push_back(stride);
|
||||
last_index = index;
|
||||
}
|
||||
flush_bindings();
|
||||
runtime.BindVertexBuffers(bindings);
|
||||
} else {
|
||||
HostBindings<typename P::Buffer> host_bindings;
|
||||
bool any_valid{false};
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "common/cityhash.h"
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/fs_paths.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "video_core/frame_gen/lossless_dll.h"
|
||||
#include "video_core/frame_gen/lsfg_translate.h"
|
||||
|
||||
namespace VideoCore::FrameGen {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u16 DOS_MAGIC = 0x5A4D;
|
||||
constexpr u32 PE_SIGNATURE = 0x00004550;
|
||||
constexpr u16 PE32_MAGIC = 0x010B;
|
||||
constexpr u16 PE32_PLUS_MAGIC = 0x020B;
|
||||
|
||||
constexpr size_t DOS_LFANEW_OFFSET = 0x3C;
|
||||
constexpr size_t COFF_HEADER_SIZE = 20;
|
||||
constexpr size_t OPTIONAL_HEADER_SIZE_OFFSET = 16;
|
||||
constexpr size_t SECTION_HEADER_SIZE = 40;
|
||||
constexpr size_t DATA_DIRECTORY_ENTRY_SIZE = 8;
|
||||
constexpr size_t DATA_DIRECTORY_OFFSET_PE32 = 96;
|
||||
constexpr size_t DATA_DIRECTORY_OFFSET_PE32_PLUS = 112;
|
||||
constexpr size_t RESOURCE_DATA_DIRECTORY_INDEX = 2;
|
||||
|
||||
constexpr size_t RESOURCE_DIRECTORY_SIZE = 16;
|
||||
constexpr size_t RESOURCE_NAMED_COUNT_OFFSET = 12;
|
||||
constexpr size_t RESOURCE_ID_COUNT_OFFSET = 14;
|
||||
constexpr size_t RESOURCE_ENTRY_SIZE = 8;
|
||||
constexpr u32 RESOURCE_SUBDIRECTORY_FLAG = 0x80000000;
|
||||
constexpr u32 RESOURCE_TYPE_RCDATA = 10;
|
||||
|
||||
constexpr u32 MIPMAPS_SHADER_ID = 255;
|
||||
constexpr u32 GENERATE_SHADER_ID = 256;
|
||||
constexpr u32 PERFORMANCE_SHADER_ID_FIRST = 280;
|
||||
constexpr u32 PERFORMANCE_SHADER_ID_LAST = 302;
|
||||
|
||||
constexpr u32 CACHE_MAGIC = 0x4746534C;
|
||||
constexpr u32 CACHE_VERSION = 2;
|
||||
|
||||
struct CacheHeader {
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u64 source_size;
|
||||
u64 source_hash;
|
||||
u32 module_count;
|
||||
u32 variant;
|
||||
};
|
||||
|
||||
struct Section {
|
||||
u32 virtual_address;
|
||||
u32 virtual_size;
|
||||
u32 raw_address;
|
||||
u32 raw_size;
|
||||
};
|
||||
|
||||
struct ResourceEntry {
|
||||
u32 id;
|
||||
u32 offset;
|
||||
bool is_directory;
|
||||
bool is_named;
|
||||
};
|
||||
|
||||
class ImageReader {
|
||||
public:
|
||||
explicit ImageReader(std::span<const u8> image_) : image{image_} {}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] bool Read(size_t offset, T& out_value) const {
|
||||
if (offset > image.size() || image.size() - offset < sizeof(T)) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(&out_value, image.data() + offset, sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Slice(size_t offset, size_t size, std::span<const u8>& out_slice) const {
|
||||
if (offset > image.size() || image.size() - offset < size) {
|
||||
return false;
|
||||
}
|
||||
out_slice = image.subspan(offset, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::span<const u8> image;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::optional<size_t> FindPeHeader(const ImageReader& reader) {
|
||||
u16 dos_magic{};
|
||||
if (!reader.Read(0, dos_magic) || dos_magic != DOS_MAGIC) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u32 pe_offset{};
|
||||
if (!reader.Read(DOS_LFANEW_OFFSET, pe_offset)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u32 pe_signature{};
|
||||
if (!reader.Read(pe_offset, pe_signature) || pe_signature != PE_SIGNATURE) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return static_cast<size_t>(pe_offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<size_t> FindDataDirectory(const ImageReader& reader,
|
||||
size_t optional_header_offset) {
|
||||
u16 optional_magic{};
|
||||
if (!reader.Read(optional_header_offset, optional_magic)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch (optional_magic) {
|
||||
case PE32_MAGIC:
|
||||
return optional_header_offset + DATA_DIRECTORY_OFFSET_PE32;
|
||||
case PE32_PLUS_MAGIC:
|
||||
return optional_header_offset + DATA_DIRECTORY_OFFSET_PE32_PLUS;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ReadSections(const ImageReader& reader, size_t pe_offset,
|
||||
std::vector<Section>& out_sections) {
|
||||
u16 section_count{};
|
||||
u16 optional_header_size{};
|
||||
if (!reader.Read(pe_offset + 4 + 2, section_count) ||
|
||||
!reader.Read(pe_offset + 4 + OPTIONAL_HEADER_SIZE_OFFSET, optional_header_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t table_offset = pe_offset + 4 + COFF_HEADER_SIZE + optional_header_size;
|
||||
out_sections.reserve(section_count);
|
||||
for (size_t i = 0; i < section_count; ++i) {
|
||||
const size_t offset = table_offset + i * SECTION_HEADER_SIZE;
|
||||
Section section{};
|
||||
if (!reader.Read(offset + 8, section.virtual_size) ||
|
||||
!reader.Read(offset + 12, section.virtual_address) ||
|
||||
!reader.Read(offset + 16, section.raw_size) ||
|
||||
!reader.Read(offset + 20, section.raw_address)) {
|
||||
return false;
|
||||
}
|
||||
out_sections.push_back(section);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<size_t> RvaToFileOffset(std::span<const Section> sections, u32 rva) {
|
||||
for (const Section& section : sections) {
|
||||
const u32 span = std::max(section.virtual_size, section.raw_size);
|
||||
if (span == 0 || rva < section.virtual_address) {
|
||||
continue;
|
||||
}
|
||||
const u32 relative = rva - section.virtual_address;
|
||||
if (relative < span) {
|
||||
return static_cast<size_t>(section.raw_address) + relative;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ReadResourceEntries(const ImageReader& reader, size_t directory_offset,
|
||||
std::vector<ResourceEntry>& out_entries) {
|
||||
u16 named_count{};
|
||||
u16 id_count{};
|
||||
if (!reader.Read(directory_offset + RESOURCE_NAMED_COUNT_OFFSET, named_count) ||
|
||||
!reader.Read(directory_offset + RESOURCE_ID_COUNT_OFFSET, id_count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t total = size_t{named_count} + size_t{id_count};
|
||||
out_entries.clear();
|
||||
out_entries.reserve(total);
|
||||
for (size_t i = 0; i < total; ++i) {
|
||||
const size_t offset = directory_offset + RESOURCE_DIRECTORY_SIZE + i * RESOURCE_ENTRY_SIZE;
|
||||
u32 name{};
|
||||
u32 data{};
|
||||
if (!reader.Read(offset, name) || !reader.Read(offset + 4, data)) {
|
||||
return false;
|
||||
}
|
||||
out_entries.push_back(ResourceEntry{
|
||||
.id = name & ~RESOURCE_SUBDIRECTORY_FLAG,
|
||||
.offset = data & ~RESOURCE_SUBDIRECTORY_FLAG,
|
||||
.is_directory = (data & RESOURCE_SUBDIRECTORY_FLAG) != 0,
|
||||
.is_named = (name & RESOURCE_SUBDIRECTORY_FLAG) != 0,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ReadResourceLeaf(const ImageReader& reader, std::span<const Section> sections,
|
||||
size_t leaf_offset, std::span<const u8>& out_data) {
|
||||
u32 data_rva{};
|
||||
u32 data_size{};
|
||||
if (!reader.Read(leaf_offset, data_rva) || !reader.Read(leaf_offset + 4, data_size) ||
|
||||
data_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::optional<size_t> data_offset = RvaToFileOffset(sections, data_rva);
|
||||
if (!data_offset) {
|
||||
return false;
|
||||
}
|
||||
return reader.Slice(*data_offset, data_size, out_data);
|
||||
}
|
||||
|
||||
using ResourceSpans = std::map<u32, std::span<const u8>>;
|
||||
|
||||
[[nodiscard]] bool CollectRcData(const ImageReader& reader, std::span<const Section> sections,
|
||||
size_t resource_base, ResourceSpans& out_resources) {
|
||||
std::vector<ResourceEntry> type_entries;
|
||||
if (!ReadResourceEntries(reader, resource_base, type_entries)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const ResourceEntry& type_entry : type_entries) {
|
||||
if (type_entry.is_named || type_entry.id != RESOURCE_TYPE_RCDATA ||
|
||||
!type_entry.is_directory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<ResourceEntry> name_entries;
|
||||
if (!ReadResourceEntries(reader, resource_base + type_entry.offset, name_entries)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const ResourceEntry& name_entry : name_entries) {
|
||||
if (name_entry.is_named || !name_entry.is_directory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<ResourceEntry> language_entries;
|
||||
if (!ReadResourceEntries(reader, resource_base + name_entry.offset, language_entries)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const ResourceEntry& language_entry : language_entries) {
|
||||
if (language_entry.is_directory) {
|
||||
continue;
|
||||
}
|
||||
std::span<const u8> data;
|
||||
if (!ReadResourceLeaf(reader, sections, resource_base + language_entry.offset,
|
||||
data)) {
|
||||
continue;
|
||||
}
|
||||
out_resources.insert_or_assign(name_entry.id, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<u32> PerformanceShaderIds() {
|
||||
std::vector<u32> ids{MIPMAPS_SHADER_ID, GENERATE_SHADER_ID};
|
||||
for (u32 id = PERFORMANCE_SHADER_ID_FIRST; id <= PERFORMANCE_SHADER_ID_LAST; ++id) {
|
||||
ids.push_back(id);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
[[nodiscard]] bool HasPerformanceShaders(const Map& resources) {
|
||||
const std::vector<u32> ids = PerformanceShaderIds();
|
||||
return std::ranges::all_of(ids, [&](u32 id) { return resources.contains(id); });
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 VariantOffset(ShaderVariant variant) {
|
||||
return variant == ShaderVariant::NativeFp16 ? PerformanceShader::NATIVE_FP16_OFFSET
|
||||
: PerformanceShader::NATIVE_FP32_OFFSET;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
[[nodiscard]] bool HasNativeVariant(const Map& resources, ShaderVariant variant) {
|
||||
const u32 offset = VariantOffset(variant);
|
||||
return std::ranges::all_of(PerformanceShaderIds(), [&](u32 id) {
|
||||
const auto hit = resources.find(id + offset);
|
||||
return hit != resources.end() && IsSpirvModule(hit->second);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<ShaderVariant> SelectVariant(const ResourceSpans& resources,
|
||||
bool allow_fp16, bool prefer_fp16) {
|
||||
if (prefer_fp16 && HasNativeVariant(resources, ShaderVariant::NativeFp16)) {
|
||||
return ShaderVariant::NativeFp16;
|
||||
}
|
||||
if (HasNativeVariant(resources, ShaderVariant::NativeFp32)) {
|
||||
return ShaderVariant::NativeFp32;
|
||||
}
|
||||
if (allow_fp16 && HasNativeVariant(resources, ShaderVariant::NativeFp16)) {
|
||||
return ShaderVariant::NativeFp16;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] LosslessStatus TranslateAll(const ResourceSpans& resources,
|
||||
ShaderModules& out_modules,
|
||||
ShaderVariant variant) {
|
||||
const u32 offset = VariantOffset(variant);
|
||||
out_modules.clear();
|
||||
for (const u32 id : PerformanceShaderIds()) {
|
||||
const auto hit = resources.find(id + offset);
|
||||
if (hit == resources.end()) {
|
||||
return LosslessStatus::MissingShaders;
|
||||
}
|
||||
std::vector<u32> adopted = AdoptSpirvModule(hit->second);
|
||||
if (adopted.empty()) {
|
||||
return LosslessStatus::TranslationFailed;
|
||||
}
|
||||
out_modules.emplace(id, std::move(adopted));
|
||||
}
|
||||
return LosslessStatus::Ok;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool WriteShaderCache(const std::filesystem::path& path, const CacheHeader& header,
|
||||
const ShaderModules& modules) {
|
||||
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Write,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
if (!file.IsOpen() || file.Write(header) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& [id, words] : modules) {
|
||||
const u32 word_count = static_cast<u32>(words.size());
|
||||
if (file.Write(id) != 1 || file.Write(word_count) != 1 ||
|
||||
file.Write(words) != words.size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return file.Flush();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ReadShaderCache(const std::filesystem::path& path, u64 source_size,
|
||||
u64 source_hash, u32 variant, ShaderModules& out_modules) {
|
||||
if (!Common::FS::Exists(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
CacheHeader header{};
|
||||
if (!file.IsOpen() || file.Read(header) != 1) {
|
||||
return false;
|
||||
}
|
||||
if (header.magic != CACHE_MAGIC || header.version != CACHE_VERSION ||
|
||||
header.source_size != source_size || header.source_hash != source_hash ||
|
||||
header.variant != variant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out_modules.clear();
|
||||
for (u32 i = 0; i < header.module_count; ++i) {
|
||||
u32 id{};
|
||||
u32 word_count{};
|
||||
if (file.Read(id) != 1 || file.Read(word_count) != 1 || word_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u32> words(word_count);
|
||||
if (file.Read(words) != words.size()) {
|
||||
return false;
|
||||
}
|
||||
out_modules.emplace(id, std::move(words));
|
||||
}
|
||||
|
||||
return HasPerformanceShaders(out_modules);
|
||||
}
|
||||
|
||||
[[nodiscard]] LosslessStatus ReadImageFile(const std::filesystem::path& path,
|
||||
std::vector<u8>& out_image) {
|
||||
if (!Common::FS::Exists(path)) {
|
||||
return LosslessStatus::NotInstalled;
|
||||
}
|
||||
|
||||
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
if (!file.IsOpen()) {
|
||||
return LosslessStatus::UnreadableFile;
|
||||
}
|
||||
|
||||
out_image.resize(static_cast<size_t>(file.GetSize()));
|
||||
if (out_image.empty() || file.Read(out_image) != out_image.size()) {
|
||||
return LosslessStatus::UnreadableFile;
|
||||
}
|
||||
return LosslessStatus::Ok;
|
||||
}
|
||||
|
||||
[[nodiscard]] LosslessStatus ParseShaderSpans(std::span<const u8> image,
|
||||
ResourceSpans& out_resources) {
|
||||
const ImageReader reader{image};
|
||||
const std::optional<size_t> pe_offset = FindPeHeader(reader);
|
||||
if (!pe_offset) {
|
||||
return LosslessStatus::NotPortableExecutable;
|
||||
}
|
||||
|
||||
const std::optional<size_t> data_directory =
|
||||
FindDataDirectory(reader, *pe_offset + 4 + COFF_HEADER_SIZE);
|
||||
if (!data_directory) {
|
||||
return LosslessStatus::NotPortableExecutable;
|
||||
}
|
||||
|
||||
std::vector<Section> sections;
|
||||
if (!ReadSections(reader, *pe_offset, sections)) {
|
||||
return LosslessStatus::NotPortableExecutable;
|
||||
}
|
||||
|
||||
u32 resource_rva{};
|
||||
if (!reader.Read(*data_directory + RESOURCE_DATA_DIRECTORY_INDEX * DATA_DIRECTORY_ENTRY_SIZE,
|
||||
resource_rva) ||
|
||||
resource_rva == 0) {
|
||||
return LosslessStatus::MissingShaders;
|
||||
}
|
||||
|
||||
const std::optional<size_t> resource_base = RvaToFileOffset(sections, resource_rva);
|
||||
if (!resource_base) {
|
||||
return LosslessStatus::NotPortableExecutable;
|
||||
}
|
||||
|
||||
out_resources.clear();
|
||||
if (!CollectRcData(reader, sections, *resource_base, out_resources)) {
|
||||
return LosslessStatus::MissingShaders;
|
||||
}
|
||||
|
||||
return HasPerformanceShaders(out_resources) ? LosslessStatus::Ok
|
||||
: LosslessStatus::MissingShaders;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::filesystem::path GetLosslessDllPath() {
|
||||
return Common::FS::GetEdenPath(Common::FS::EdenPath::LosslessDir) / LOSSLESS_DLL_FILE;
|
||||
}
|
||||
|
||||
std::filesystem::path GetShaderCachePath() {
|
||||
return Common::FS::GetEdenPath(Common::FS::EdenPath::LosslessDir) / LOSSLESS_CACHE_FILE;
|
||||
}
|
||||
|
||||
LosslessStatus ReadShaderResources(const std::filesystem::path& path,
|
||||
ShaderResources& out_resources) {
|
||||
std::vector<u8> image;
|
||||
const LosslessStatus read_status = ReadImageFile(path, image);
|
||||
if (read_status != LosslessStatus::Ok) {
|
||||
return read_status;
|
||||
}
|
||||
|
||||
ResourceSpans spans;
|
||||
const LosslessStatus parse_status = ParseShaderSpans(image, spans);
|
||||
if (parse_status != LosslessStatus::Ok) {
|
||||
return parse_status;
|
||||
}
|
||||
|
||||
out_resources.clear();
|
||||
for (const auto& [id, data] : spans) {
|
||||
out_resources.emplace(id, std::vector<u8>{data.begin(), data.end()});
|
||||
}
|
||||
return LosslessStatus::Ok;
|
||||
}
|
||||
|
||||
LosslessStatus ValidateLosslessDll(const std::filesystem::path& path) {
|
||||
std::vector<u8> image;
|
||||
const LosslessStatus read_status = ReadImageFile(path, image);
|
||||
if (read_status != LosslessStatus::Ok) {
|
||||
return read_status;
|
||||
}
|
||||
|
||||
ResourceSpans spans;
|
||||
return ParseShaderSpans(image, spans);
|
||||
}
|
||||
|
||||
LosslessStatus GetInstalledLosslessStatus() {
|
||||
return ValidateLosslessDll(GetLosslessDllPath());
|
||||
}
|
||||
|
||||
LosslessStatus LoadShaderModules(ShaderModules& out_modules, bool allow_fp16, bool prefer_fp16) {
|
||||
std::vector<u8> image;
|
||||
const LosslessStatus read_status = ReadImageFile(GetLosslessDllPath(), image);
|
||||
if (read_status != LosslessStatus::Ok) {
|
||||
return read_status;
|
||||
}
|
||||
|
||||
const u64 source_size = image.size();
|
||||
const u64 source_hash =
|
||||
Common::CityHash64(reinterpret_cast<const char*>(image.data()), image.size());
|
||||
const std::filesystem::path cache_path = GetShaderCachePath();
|
||||
|
||||
ResourceSpans spans;
|
||||
const LosslessStatus parse_status = ParseShaderSpans(image, spans);
|
||||
if (parse_status != LosslessStatus::Ok) {
|
||||
return parse_status;
|
||||
}
|
||||
|
||||
const std::optional<ShaderVariant> variant = SelectVariant(spans, allow_fp16, prefer_fp16);
|
||||
if (!variant) {
|
||||
return LosslessStatus::MissingShaders;
|
||||
}
|
||||
|
||||
if (ReadShaderCache(cache_path, source_size, source_hash, static_cast<u32>(*variant),
|
||||
out_modules)) {
|
||||
return LosslessStatus::Ok;
|
||||
}
|
||||
|
||||
const LosslessStatus translate_status = TranslateAll(spans, out_modules, *variant);
|
||||
if (translate_status != LosslessStatus::Ok) {
|
||||
return translate_status;
|
||||
}
|
||||
|
||||
const CacheHeader header{
|
||||
.magic = CACHE_MAGIC,
|
||||
.version = CACHE_VERSION,
|
||||
.source_size = source_size,
|
||||
.source_hash = source_hash,
|
||||
.module_count = static_cast<u32>(out_modules.size()),
|
||||
.variant = static_cast<u32>(*variant),
|
||||
};
|
||||
if (!WriteShaderCache(cache_path, header, out_modules)) {
|
||||
void(Common::FS::RemoveFile(cache_path));
|
||||
return LosslessStatus::CacheUnusable;
|
||||
}
|
||||
|
||||
return LosslessStatus::Ok;
|
||||
}
|
||||
|
||||
LosslessStatus BuildShaderCache() {
|
||||
ShaderModules modules;
|
||||
return LoadShaderModules(modules, true);
|
||||
}
|
||||
|
||||
bool RemoveInstalledLosslessDll() {
|
||||
const std::filesystem::path cache_path = GetShaderCachePath();
|
||||
if (Common::FS::Exists(cache_path)) {
|
||||
void(Common::FS::RemoveFile(cache_path));
|
||||
}
|
||||
|
||||
const std::filesystem::path path = GetLosslessDllPath();
|
||||
if (!Common::FS::Exists(path)) {
|
||||
return true;
|
||||
}
|
||||
return Common::FS::RemoveFile(path);
|
||||
}
|
||||
|
||||
} // namespace VideoCore::FrameGen
|
||||
@@ -0,0 +1,67 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2025 lsfg-vk
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCore::FrameGen {
|
||||
|
||||
enum class LosslessStatus : u32 {
|
||||
Ok,
|
||||
NotInstalled,
|
||||
UnreadableFile,
|
||||
NotPortableExecutable,
|
||||
MissingShaders,
|
||||
TranslationFailed,
|
||||
CacheUnusable,
|
||||
};
|
||||
|
||||
using ShaderResources = std::map<u32, std::vector<u8>>;
|
||||
using ShaderModules = std::map<u32, std::vector<u32>>;
|
||||
|
||||
enum class ShaderVariant : u32 {
|
||||
NativeFp32 = 1,
|
||||
NativeFp16 = 2,
|
||||
};
|
||||
|
||||
namespace PerformanceShader {
|
||||
constexpr u32 MIPMAPS = 255;
|
||||
constexpr u32 GENERATE = 256;
|
||||
constexpr std::array<u32, 4> ALPHA{290, 291, 292, 293};
|
||||
constexpr std::array<u32, 5> BETA{298, 299, 300, 301, 302};
|
||||
constexpr std::array<u32, 5> GAMMA{280, 282, 283, 284, 285};
|
||||
constexpr std::array<u32, 10> DELTA{280, 286, 287, 288, 289, 281, 294, 295, 296, 297};
|
||||
|
||||
constexpr u32 NATIVE_FP16_OFFSET = 49;
|
||||
constexpr u32 NATIVE_FP32_OFFSET = 98;
|
||||
} // namespace PerformanceShader
|
||||
|
||||
[[nodiscard]] std::filesystem::path GetLosslessDllPath();
|
||||
|
||||
[[nodiscard]] std::filesystem::path GetShaderCachePath();
|
||||
|
||||
[[nodiscard]] LosslessStatus ReadShaderResources(const std::filesystem::path& path,
|
||||
ShaderResources& out_resources);
|
||||
|
||||
[[nodiscard]] LosslessStatus ValidateLosslessDll(const std::filesystem::path& path);
|
||||
|
||||
[[nodiscard]] LosslessStatus GetInstalledLosslessStatus();
|
||||
|
||||
[[nodiscard]] LosslessStatus BuildShaderCache();
|
||||
|
||||
[[nodiscard]] LosslessStatus LoadShaderModules(ShaderModules& out_modules,
|
||||
bool allow_fp16 = false,
|
||||
bool prefer_fp16 = false);
|
||||
|
||||
bool RemoveInstalledLosslessDll();
|
||||
|
||||
} // namespace VideoCore::FrameGen
|
||||
@@ -0,0 +1,93 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
|
||||
#include "video_core/frame_gen/lsfg_translate.h"
|
||||
|
||||
namespace VideoCore::FrameGen {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u32 SPIRV_MAGIC = 0x07230203;
|
||||
constexpr u32 SPIRV_WORD_COUNT_SHIFT = 16;
|
||||
constexpr u32 SPIRV_OPCODE_MASK = 0xffff;
|
||||
constexpr u32 SPIRV_OP_FUNCTION = 54;
|
||||
constexpr u32 SPIRV_OP_DECORATE = 71;
|
||||
constexpr u32 SPIRV_DECORATION_BINDING = 33;
|
||||
constexpr u32 SPIRV_DECORATION_DESCRIPTOR_SET = 34;
|
||||
|
||||
constexpr u32 DECORATION_LITERAL_WORD = 3;
|
||||
constexpr size_t SPIRV_HEADER_WORDS = 5;
|
||||
|
||||
void RenumberBindingsInOrder(std::vector<u32>& words) {
|
||||
struct Slot {
|
||||
u32 set;
|
||||
u32 binding;
|
||||
size_t literal_offset;
|
||||
};
|
||||
|
||||
std::map<u32, u32> sets;
|
||||
std::vector<Slot> slots;
|
||||
|
||||
size_t offset = SPIRV_HEADER_WORDS;
|
||||
while (offset + 1 <= words.size()) {
|
||||
const u32 length = words[offset] >> SPIRV_WORD_COUNT_SHIFT;
|
||||
const u32 opcode = words[offset] & SPIRV_OPCODE_MASK;
|
||||
if (length == 0 || offset + length > words.size()) {
|
||||
return;
|
||||
}
|
||||
if (opcode == SPIRV_OP_FUNCTION) {
|
||||
break;
|
||||
}
|
||||
if (opcode == SPIRV_OP_DECORATE && length >= 4) {
|
||||
if (words[offset + 2] == SPIRV_DECORATION_DESCRIPTOR_SET) {
|
||||
sets[words[offset + 1]] = words[offset + 3];
|
||||
} else if (words[offset + 2] == SPIRV_DECORATION_BINDING) {
|
||||
slots.push_back(Slot{0, words[offset + 3], offset + DECORATION_LITERAL_WORD});
|
||||
}
|
||||
}
|
||||
offset += length;
|
||||
}
|
||||
|
||||
for (Slot& slot : slots) {
|
||||
const auto hit = sets.find(words[slot.literal_offset - 2]);
|
||||
slot.set = hit == sets.end() ? 0 : hit->second;
|
||||
}
|
||||
|
||||
std::ranges::stable_sort(slots, [](const Slot& lhs, const Slot& rhs) {
|
||||
return std::tie(lhs.set, lhs.binding) < std::tie(rhs.set, rhs.binding);
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < slots.size(); ++i) {
|
||||
words[slots[i].literal_offset] = static_cast<u32>(i);
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
bool IsSpirvModule(std::span<const u8> blob) {
|
||||
if (blob.size() < SPIRV_HEADER_WORDS * sizeof(u32) || blob.size() % sizeof(u32) != 0) {
|
||||
return false;
|
||||
}
|
||||
u32 magic{};
|
||||
std::memcpy(&magic, blob.data(), sizeof(magic));
|
||||
return magic == SPIRV_MAGIC;
|
||||
}
|
||||
|
||||
std::vector<u32> AdoptSpirvModule(std::span<const u8> blob) {
|
||||
if (!IsSpirvModule(blob)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u32> words(blob.size() / sizeof(u32));
|
||||
std::memcpy(words.data(), blob.data(), blob.size());
|
||||
|
||||
RenumberBindingsInOrder(words);
|
||||
return words;
|
||||
}
|
||||
|
||||
} // namespace VideoCore::FrameGen
|
||||
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCore::FrameGen {
|
||||
|
||||
[[nodiscard]] bool IsSpirvModule(std::span<const u8> blob);
|
||||
|
||||
[[nodiscard]] std::vector<u32> AdoptSpirvModule(std::span<const u8> blob);
|
||||
|
||||
} // namespace VideoCore::FrameGen
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user