Compare commits

..

39 Commits

Author SHA1 Message Date
lizzie
328f496ef9 the abi sucks
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 10:41:45 +00:00
lizzie
ba0e074ab1 fix stdu/ldu
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 09:58:20 +00:00
lizzie
9fa9cc203e save more regs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 09:31:57 +00:00
lizzie
2faa4947e9 Use ctr like gcc does
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 09:13:26 +00:00
lizzie
f3a8742535 +add docs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
38fb0c261a more ppc fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
d1ecdb034f chained calls for ppc64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
d54e53b768 inline a32core/a64core
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
crueter
e965b885c4 build fixes + gentoo cross docs
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
791f353ad0 fix invalidations, use stack for checkbit
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
12e0ecb440 A64checkbit
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
3e66f9bd8e A64: addition fixes extra
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
108988f1b3 even more stupid fixes i feel angry
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
ace670d381 fix xs stuffs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
27537b52bd more bclr fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
9d2e74f0c7 fixes for cmpld(i) encodings
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
0fc37f3b10 C -> O for ppc64; impl NZCV?
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
cbdd78df8f more fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
e597b1eb95 i forgot jit pointer, again
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
d56c540dea just use STD() + offset, reorder stuff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
1f42af9da3 let type deduction do its thing
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
81773cb0e6 reglock draft 1
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
e09257eccc A64: ADD now passes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
c1acac33e6 terminal draft1
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
1cf0e71fdb "A64: ADD" passes (except on PC check)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
2dbfb96827 polish up A64 to be ready to accept stuff (but NO-op)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
0ce520af70 remove args, directly ref inst stuff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
e508931b42 cmake exclusion guard dynarmic
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
7802690e04 fix license
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
00858b679c exclude powah from non-ppc64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
17ad4cfbec generation of add and extraction of flags
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
b68db08e6a first emitted block
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
41adc735af backwards relocs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
36c60fab0e relocations
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
27e274a9f3 cross compile instructions; ignore toolchain files
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:49 +00:00
lizzie
172ff8e76c spinlocks, annoyances with organistaion
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:48 +00:00
lizzie
3c5d7212ca cross compile instructions
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:48 +00:00
lizzie
762e8d010b a64+a32 stubs (+some impls)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:48 +00:00
lizzie
81cb00c4ec skeleton for ppc64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-10 08:25:46 +00:00
101 changed files with 8659 additions and 2410 deletions

1
.gitignore vendored
View File

@@ -20,6 +20,7 @@ log.txt
# Generated source files
src/common/scm_rev.cpp
dist/english_plurals/generated_en.ts
*-toolchain.cmake
# Project/editor files
*.swp

View File

@@ -13,6 +13,8 @@ Copyright: yuzu Emulator Project
License: GPL-2.0-or-later
Files: dist/qt_themes/default/icons/256x256/eden.png
dist/qt_themes/default/icons/256x256/eden_named.png
dist/Assets.car
dist/yuzu.bmp
dist/eden.icns
dist/eden.ico

View File

@@ -26,9 +26,8 @@ endif()
# Needed for FFmpeg w/ VAAPI and DRM
if (PLATFORM_OPENBSD)
# OpenBSD 7.8 broke libcxx when upgrading, so we must define the PSTL backend manually
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R6/lib")
elseif (PLATFORM_NETBSD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include")

View File

@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: Copyright 2025 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)

81
dist/eden_named.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -77,8 +77,6 @@ For this reason this patch is NOT applied to default on all platforms (for obvio
Still will not run flawlessly until `mesa-24` is available. Modify CMakeCache.txt with the `.so` of libGL and libGLESv2 by doing the incredibly difficult task of copy pasting them (`cp /boot/system/lib/libGL.so .`)
If you have `quazip1_qt6_devel`, uninstall it. It may call `Core5Compat` on CMake which is wrongly packaged.
## OpenBSD
After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`.
@@ -101,17 +99,11 @@ ip6addrctl=YES
ip6addrctl_policy=ipv4_prefer
```
System provides a default `g++-10` which doesn't support the current C++ codebase; install `clang-19` with `pkgin install clang-19`. Or install `gcc14` (or `gcc15` with current pkgsrc). Provided that, the following CMake commands may work:
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -Bbuild`
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc14/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -Bbuild`
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc15/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc15/bin/g++ -Bbuild`
System provides a default `g++-10` which doesn't support the current C++ codebase; install `clang-19` with `pkgin install clang-19`. Then build with `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -B build`.
Make may error out when generating C++ headers of SPIRV shaders, hence it's recommended to use `gmake` over the default system one.
[parallel/spirv-tools](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/parallel/spirv-tools/index.html) isn't available in binary form and must be build from source.
Such that glslang is not available on NetBSD, to circumvent this simply build glslang by yourself:
glslang is not available on NetBSD, to circumvent this simply build glslang by yourself:
```sh
pkgin python313
git clone --depth=1 https://github.com/KhronosGroup/glslang.git
@@ -122,8 +114,6 @@ cmake --build build -- -j`nproc`
cmake --install build
```
However, pkgsrc is highly recommended, see [getting pkgsrc](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/doc/pkgsrc.html#getting). You must get `current` not the `2025Q2` version.
# DragonFlyBSD
If `libstdc++.so.6` is not found (`GLIBCXX_3.4.30`) then attempt:

View File

@@ -1,5 +1,118 @@
# Cross Compile
## 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
A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main.
@@ -8,3 +121,22 @@ A painless guide for cross compilation (or to test NCE) from a x86_64 system wit
- Download Debian 13: `wget https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/debian-13.0.0-arm64-netinst.iso`
- Create a system disk: `qemu-img create -f qcow2 debian-13-arm64-ci.qcow2 30G`
- Run the VM: `qemu-system-aarch64 -M virt -m 2G -cpu max -bios /usr/local/share/qemu/edk2-aarch64-code.fd -drive if=none,file=debian-13.0.0-arm64-netinst.iso,format=raw,id=cdrom -device scsi-cd,drive=cdrom -drive if=none,file=debian-13-arm64-ci.qcow2,id=hd0,format=qcow2 -device virtio-blk-device,drive=hd0 -device virtio-gpu-pci -device usb-ehci -device usb-kbd -device intel-hda -device hda-output -nic user,model=virtio-net-pci`
## 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`
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
```

View File

@@ -183,8 +183,6 @@ sudo dnf config-manager --enable crb
sudo dnf install qt6-qtbase-private-devel
```
For systems like OpenEuler or derivates, don't forget to also install: `SDL2-devel pkg-config fmt-dev nlohmann-json-dev`.
* [RPM Fusion](https://rpmfusion.org/Configuration) is required for `ffmpeg-devel`
* Fedora 32 or later is required.
* Fedora 36+ users with GCC 12 need Clang and should configure CMake with: `cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -B build`
@@ -243,7 +241,7 @@ brew install molten-vk vulkan-loader
<details>
<summary>FreeBSD</summary>
As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/unordered-dense mbedtls3 vulkan-headers quazip-qt6`
As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/jwt-cpp devel/unordered-dense mbedtls3 vulkan-headers quazip-qt6`
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
@@ -253,7 +251,7 @@ If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
<details>
<summary>NetBSD</summary>
For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers ffmpeg7 libva nlohmann-json jq libopus qt6 mbedtls3 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx`.
For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers ffmpeg7 libva nlohmann-json jq libopus qt6 mbedtls3 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1`.
[Caveats](./Caveats.md#netbsd).
@@ -322,7 +320,7 @@ pacman -Syuu --needed --noconfirm $packages
<summary>HaikuOS</summary>
```sh
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel mbedtls3_devel glslang qt6_devel
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt6_devel qt6_5compat_devel libusb1_devel libz_devel mbedtls3_devel glslang
```
[Caveats](./Caveats.md#haikuos).

View File

@@ -1,874 +0,0 @@
# The NVIDIA SM86 (Maxwell) GPU - Instruction set
<!-- TOC -->
[AL2P](#AL2P)
[ALD](#ALD)
[AST](#AST)
[ATOM](#ATOM)
[ATOMS](#ATOMS)
[B2R](#B2R)
[BAR](#BAR)
[BFE](#BFE)
[BFI](#BFI)
[BPT](#BPT)
[BRA](#BRA)
[BRK](#BRK)
[BRX](#BRX)
[CAL](#CAL)
[CCTL](#CCTL)
[CCTLL](#CCTLL)
[CONT](#CONT)
[CS2R](#CS2R)
[CSET](#CSET)
[CSETP](#CSETP)
[DADD](#DADD)
[DEPBAR](#DEPBAR)
[DFMA](#DFMA)
[DMNMX](#DMNMX)
[DMUL](#DMUL)
[DSET](#DSET)
[DSETP](#DSETP)
[EXIT](#EXIT)
[F2F](#F2F)
[F2I](#F2I)
[FADD](#FADD)
[FCHK](#FCHK)
[FCMP](#FCMP)
[FFMA](#FFMA)
[FLO](#FLO)
[FMNMX](#FMNMX)
[FMUL](#FMUL)
[FSET](#FSET)
[FSETP](#FSETP)
[FSWZADD](#FSWZADD)
[GETCRSPTR](#GETCRSPTR)
[GETLMEMBASE](#GETLMEMBASE)
[HADD2](#HADD2)
[HFMA2](#HFMA2)
[HMUL2](#HMUL2)
[HSET2](#HSET2)
[HSETP2](#HSETP2)
[I2F](#I2F)
[I2I](#I2I)
[IADD](#IADD)
[IADD3](#IADD3)
[ICMP](#ICMP)
[IDE](#IDE)
[IDP](#IDP)
[IMAD](#IMAD)
[IMADSP](#IMADSP)
[IMNMX](#IMNMX)
[IMUL](#IMUL)
[IPA](#IPA)
[ISBERD](#ISBERD)
[ISCADD](#ISCADD)
[ISET](#ISET)
[ISETP](#ISETP)
[JCAL](#JCAL)
[JMP](#JMP)
[JMX](#JMX)
[KIL](#KIL)
[LD](#LD)
[LDC](#LDC)
[LDG](#LDG)
[LDL](#LDL)
[LDS](#LDS)
[LEA](#LEA)
[LEPC](#LEPC)
[LONGJMP](#LONGJMP)
[LOP](#LOP)
[LOP3](#LOP3)
[MEMBAR](#MEMBAR)
[MOV](#MOV)
[MUFU](#MUFU)
[NOP](#NOP)
[OUT](#OUT)
[P2R](#P2R)
[PBK](#PBK)
[PCNT](#PCNT)
[PEXIT](#PEXIT)
[PIXLD](#PIXLD)
[PLONGJMP](#PLONGJMP)
[POPC](#POPC)
[PRET](#PRET)
[PRMT](#PRMT)
[PSET](#PSET)
[PSETP](#PSETP)
[R2B](#R2B)
[R2P](#R2P)
[RAM](#RAM)
[RED](#RED)
[RET](#RET)
[RRO](#RRO)
[RTT](#RTT)
[S2R](#S2R)
[SAM](#SAM)
[SEL](#SEL)
[SETCRSPTR](#SETCRSPTR)
[SETLMEMBASE](#SETLMEMBASE)
[SHF](#SHF)
[SHFL](#SHFL)
[SHL](#SHL)
[SHR](#SHR)
[SSY](#SSY)
[ST](#ST)
[STG](#STG)
[STL](#STL)
[STP](#STP)
[STS](#STS)
[SUATOM](#SUATOM)
[SULD](#SULD)
[SURED](#SURED)
[SUST](#SUST)
[SYNC](#SYNC)
[TEX](#TEX)
[TLD](#TLD)
[TLD4](#TLD4)
[TMML](#TMML)
[TXA](#TXA)
[TXD](#TXD)
[TXQ](#TXQ)
[VABSDIFF](#VABSDIFF)
[VABSDIFF4](#VABSDIFF4)
[VADD](#VADD)
[VMAD](#VMAD)
[VMNMX](#VMNMX)
[VOTE](#VOTE)
[VSET](#VSET)
[VSETP](#VSETP)
[VSHL](#VSHL)
[VSHR](#VSHR)
[XMAD](#XMAD)
<!-- /TOC -->
NOTE: Regenerate TOC with `cat docs/gpu/README.md | grep '#' | cut -d '#' -f 2 | tr -d ' ' | awk '{print "["$1"](#"$1")"}'`.
The numbers (in binary) represent the opcodes; `-` signifies "don't care".
# AL2P
`1110 1111 1010 0---`
# ALD
`1110 1111 1101 1---`
# AST
`1110 1111 1111 0---`
# ATOM
- **ATOM_cas**: `1110 1110 1111 ----`
- **ATOM**: `1110 1101 ---- ----`
Atomic operation.
- INC, DEC for U32/S32/U64 does nothing.
- ADD, INC, DEC for S64 does nothing.
- Only ADD does something for F32.
- Only ADD, MIN and MAX does something for F16x2.
# ATOMS
- **ATOMS_cas**: `1110 1110 ---- ----`
- **ATOMS**: `1110 1100 ---- ----`
# B2R
`1111 0000 1011 1---`
# BAR
`1111 0000 1010 1---`
# BFE
- **BFE_reg**: `0101 1100 0000 0---`
- **BFE_cbuf**: `0100 1100 0000 0---`
- **BFE_imm**: `0011 100- 0000 0---`
Bit Field Extract.
# BFI
- **BFI_reg**: `0101 1011 1111 0---`
- **BFI_rc**: `0101 0011 1111 0---`
- **BFI_cr**: `0100 1011 1111 0---`
- **BFI_imm**: `0011 011- 1111 0---`
Bit Field Insert.
# BPT
`1110 0011 1010 ----`
Breakpoint trap.
# BRA
`1110 0010 0100 ----`
Relative branch.
# BRK
`1110 0011 0100 ----`
Break.
# BRX
`1110 0010 0101 ----`
# CAL
`1110 0010 0110 ----`
# CCTL
`1110 1111 011- ----`
Cache Control.
# CCTLL
`1110 1111 100- ----`
Texture Cache Control.
# CONT
`1110 0011 0101 ----`
Continue.
# CS2R
`0101 0000 1100 1---`
Move Special Register to Register.
# CSET
`0101 0000 1001 1---`
Test Condition Code And Set.
# CSETP
`0101 0000 1010 0---`
Test Condition Code and Set Predicate.
# DADD
- **DADD_reg**: `0101 1100 0111 0---`
- **DADD_cbuf**: `0100 1100 0111 0---`
- **DADD_imm**: `0011 100- 0111 0---`
# DEPBAR
`1111 0000 1111 0---`
# DFMA
- **DFMA_reg**: `0101 1011 0111 ----`
- **DFMA_rc**: `0101 0011 0111 ----`
- **DFMA_cr**: `0100 1011 0111 ----`
- **DFMA_imm**: `0011 011- 0111 ----`
FP64 Fused Mutiply Add.
# DMNMX
- **DMNMX_reg**: `0101 1100 0101 0---`
- **DMNMX_cbuf**: `0100 1100 0101 0---`
- **DMNMX_imm**: `0011 100- 0101 0---`
FP64 Minimum/Maximum.
# DMUL
- **DMUL_reg**: `0101 1100 1000 0---`
- **DMUL_cbuf**: `0100 1100 1000 0---`
- **DMUL_imm**: `0011 100- 1000 0---`
FP64 Multiply.
# DSET
- **DSET_reg**: `0101 1001 0--- ----`
- **DSET_cbuf**: `0100 1001 0--- ----`
- **DSET_imm**: `0011 001- 0--- ----`
FP64 Compare And Set.
# DSETP
- **DSETP_reg**: `0101 1011 1000 ----`
- **DSETP_cbuf**: `0100 1011 1000 ----`
- **DSETP_imm**: `0011 011- 1000 ----`
FP64 Compare And Set Predicate.
# EXIT
`1110 0011 0000 ----`
# F2F
- **F2F_reg**: `0101 1100 1010 1---`
- **F2F_cbuf**: `0100 1100 1010 1---`
- **F2F_imm**: `0011 100- 1010 1---`
# F2I
- **F2I_reg**: `0101 1100 1011 0---`
- **F2I_cbuf**: `0100 1100 1011 0---`
- **F2I_imm**: `0011 100- 1011 0---`
# FADD
- **FADD_reg**: `0101 1100 0101 1---`
- **FADD_cbuf**: `0100 1100 0101 1---`
- **FADD_imm**: `0011 100- 0101 1---`
- **FADD32I**: `0000 10-- ---- ----`
FP32 Add.
# FCHK
- **FCHK_reg**: `0101 1100 1000 1---`
- **FCHK_cbuf**: `0100 1100 1000 1---`
- **FCHK_imm**: `0011 100- 1000 1---`
Single Precision FP Divide Range Check.
# FCMP
- **FCMP_reg**: `0101 1011 1010 ----`
- **FCMP_rc**: `0101 0011 1010 ----`
- **FCMP_cr**: `0100 1011 1010 ----`
- **FCMP_imm**: `0011 011- 1010 ----`
FP32 Compare to Zero and Select Source.
# FFMA
- **FFMA_reg**: `0101 1001 1--- ----`
- **FFMA_rc**: `0101 0001 1--- ----`
- **FFMA_cr**: `0100 1001 1--- ----`
- **FFMA_imm**: `0011 001- 1--- ----`
- **FFMA32I**: `0000 11-- ---- ----`
FP32 Fused Multiply and Add.
# FLO
- **FLO_reg**: `0101 1100 0011 0---`
- **FLO_cbuf**: `0100 1100 0011 0---`
- **FLO_imm**: `0011 100- 0011 0---`
# FMNMX
- **FMNMX_reg**: `0101 1100 0110 0---`
- **FMNMX_cbuf**: `0100 1100 0110 0---`
- **FMNMX_imm**: `0011 100- 0110 0---`
FP32 Minimum/Maximum.
# FMUL
- **FMUL_reg**: `0101 1100 0110 1---`
- **FMUL_cbuf**: `0100 1100 0110 1---`
- **FMUL_imm**: `0011 100- 0110 1---`
- **FMUL32I**: `0001 1110 ---- ----`
FP32 Multiply.
# FSET
- **FSET_reg**: `0101 1000 ---- ----`
- **FSET_cbuf**: `0100 1000 ---- ----`
- **FSET_imm**: `0011 000- ---- ----`
FP32 Compare And Set.
# FSETP
- **FSETP_reg**: `0101 1011 1011 ----`
- **FSETP_cbuf**: `0100 1011 1011 ----`
- **FSETP_imm**: `0011 011- 1011 ----`
FP32 Compare And Set Predicate.
# FSWZADD
`0101 0000 1111 1---`
FP32 Add used for FSWZ emulation.
# GETCRSPTR
`1110 0010 1100 ----`
# GETLMEMBASE
`1110 0010 1101 ----`
# HADD2
- **HADD2_reg**: `0101 1101 0001 0---`
- **HADD2_cbuf**: `0111 101- 1--- ----`
- **HADD2_imm**: `0111 101- 0--- ----`
- **HADD2_32I**: `0010 110- ---- ----`
FP16 Add.
# HFMA2
- **HFMA2_reg**: `0101 1101 0000 0---`
- **HFMA2_rc**: `0110 0--- 1--- ----`
- **HFMA2_cr**: `0111 0--- 1--- ----`
- **HFMA2_imm**: `0111 0--- 0--- ----`
- **HFMA2_32I**: `0010 100- ---- ----`
FP16 Fused Mutiply Add.
# HMUL2
- **HMUL2_reg**: `0101 1101 0000 1---`
- **HMUL2_cbuf**: `0111 100- 1--- ----`
- **HMUL2_imm**: `0111 100- 0--- ----`
- **HMUL2_32I**: `0010 101- ---- ----`
FP16 Multiply.
# HSET2
- **HSET2_reg**: `0101 1101 0001 1---`
- **HSET2_cbuf**: `0111 110- 1--- ----`
- **HSET2_imm**: `0111 110- 0--- ----`
FP16 Compare And Set.
# HSETP2
- **HSETP2_reg**: `0101 1101 0010 0---`
- **HSETP2_cbuf**: `0111 111- 1--- ----`
- **HSETP2_imm**: `0111 111- 0--- ----`
FP16 Compare And Set Predicate.
# I2F
- **I2F_reg**: `0101 1100 1011 1---`
- **I2F_cbuf**: `0100 1100 1011 1---`
- **I2F_imm**: `0011 100- 1011 1---`
# I2I
- **I2I_reg**: `0101 1100 1110 0---`
- **I2I_cbuf**: `0100 1100 1110 0---`
- **I2I_imm**: `0011 100- 1110 0---`
# IADD
- **IADD_reg**: `0101 1100 0001 0---`
- **IADD_cbuf**: `0100 1100 0001 0---`
- **IADD_imm**: `0011 100- 0001 0---`
Integer Addition.
# IADD3
- **IADD3_reg**: `0101 1100 1100 ----`
- **IADD3_cbuf**: `0100 1100 1100 ----`
- **IADD3_imm**: `0011 100- 1100 ----`
- **IADD32I**: `0001 110- ---- ----`
3-input Integer Addition.
# ICMP
- **ICMP_reg**: `0101 1011 0100 ----`
- **ICMP_rc**: `0101 0011 0100 ----`
- **ICMP_cr**: `0100 1011 0100 ----`
- **ICMP_imm**: `0011 011- 0100 ----`
Integer Compare to Zero and Select Source.
# IDE
`1110 0011 1001 ----`
# IDP
- **IDP_reg**: `0101 0011 1111 1---`
- **IDP_imm**: `0101 0011 1101 1---`
# IMAD
- **IMAD_reg**: `0101 1010 0--- ----`
- **IMAD_rc**: `0101 0010 0--- ----`
- **IMAD_cr**: `0100 1010 0--- ----`
- **IMAD_imm**: `0011 010- 0--- ----`
- **IMAD32I**: `1000 00-- ---- ----`
Integer Multiply And Add.
# IMADSP
- **IMADSP_reg**: `0101 1010 1--- ----`
- **IMADSP_rc**: `0101 0010 1--- ----`
- **IMADSP_cr**: `0100 1010 1--- ----`
- **IMADSP_imm**: `0011 010- 1--- ----`
Extracted Integer Multiply And Add..
# IMNMX
- **IMNMX_reg**: `0101 1100 0010 0---`
- **IMNMX_cbuf**: `0100 1100 0010 0---`
- **IMNMX_imm**: `0011 100- 0010 0---`
Integer Minimum/Maximum.
# IMUL
- **IMUL_reg**: `0101 1100 0011 1---`
- **IMUL_cbuf**: `0100 1100 0011 1---`
- **IMUL_imm**: `0011 100- 0011 1---`
- **IMUL32I**: `0001 1111 ---- ----`
Integer Multiply.
# IPA
`1110 0000 ---- ----`
# ISBERD
`1110 1111 1101 0---`
In-Stage-Buffer Entry Read.
# ISCADD
- **ISCADD_reg**: `0101 1100 0001 1---`
- **ISCADD_cbuf**: `0100 1100 0001 1---`
- **ISCADD_imm**: `0011 100- 0001 1---`
- **ISCADD32I**: `0001 01-- ---- ----`
Scaled Integer Addition.
# ISET
- **ISET_reg**: `0101 1011 0101 ----`
- **ISET_cbuf**: `0100 1011 0101 ----`
- **ISET_imm**: `0011 011- 0101 ----`
Integer Compare And Set.
# ISETP
- **ISETP_reg**: `0101 1011 0110 ----`
- **ISETP_cbuf**: `0100 1011 0110 ----`
- **ISETP_imm**: `0011 011- 0110 ----`
Integer Compare And Set Predicate.
# JCAL
`1110 0010 0010 ----`
Absolute Call.
# JMP
`1110 0010 0001 ----`
Absolute Jump.
# JMX
`1110 0010 0000 ----`
Absolute Jump Indirect.
# KIL
`1110 0011 0011 ----`
# LD
`100- ---- ---- ----`
Load from generic Memory.
# LDC
`1110 1111 1001 0---`
Load Constant.
# LDG
`1110 1110 1101 0---`
Load from Global Memory.
# LDL
`1110 1111 0100 0---`
Load within Local Memory Window.
# LDS
`1110 1111 0100 1---`
Load within Shared Memory Window.
# LEA
- **LEA_hi_reg**: `0101 1011 1101 1---`
- **LEA_hi_cbuf**: `0001 10-- ---- ----`
- **LEA_lo_reg**: `0101 1011 1101 0---`
- **LEA_lo_cbuf**: `0100 1011 1101 ----`
- **LEA_lo_imm**: `0011 011- 1101 0---`
# LEPC
`0101 0000 1101 0---`
# LONGJMP
`1110 0011 0001 ----`
# LOP
- **LOP_reg**: `0101 1100 0100 0---`
- **LOP_cbuf**: `0100 1100 0100 0---`
- **LOP_imm**: `0011 100- 0100 0---`
# LOP3
- **LOP3_reg**: `0101 1011 1110 0---`
- **LOP3_cbuf**: `0000 001- ---- ----`
- **LOP3_imm**: `0011 11-- ---- ----`
- **LOP32I**: `0000 01-- ---- ----`
# MEMBAR
`1110 1111 1001 1---`
Memory Barrier.
# MOV
- **MOV_reg**: `0101 1100 1001 1---`
- **MOV_cbuf**: `0100 1100 1001 1---`
- **MOV_imm**: `0011 100- 1001 1---`
- **MOV32I**: `0000 0001 0000 ----`
# MUFU
`0101 0000 1000 0---`
Multi Function Operation.
# NOP
`0101 0000 1011 0---`
No operation.
# OUT
- **OUT_reg**: `1111 1011 1110 0---`
- **OUT_cbuf**: `1110 1011 1110 0---`
- **OUT_imm**: `1111 011- 1110 0---`
# P2R
- **P2R_reg**: `0101 1100 1110 1---`
- **P2R_cbuf**: `0100 1100 1110 1---`
- **P2R_imm**: `0011 1000 1110 1---`
Move Predicate Register To Register.
# PBK
`1110 0010 1010 ----`
Pre-break.
# PCNT
`1110 0010 1011 ----`
Pre-continue.
# PEXIT
`1110 0010 0011 ----`
Pre-exit.
# PIXLD
`1110 1111 1110 1---`
# PLONGJMP
`1110 0010 1000 ----`
Pre-long jump.
# POPC
- **POPC_reg**: `0101 1100 0000 1---`
- **POPC_cbuf**: `0100 1100 0000 1---`
- **POPC_imm**: `0011 100- 0000 1---`
Population/Bit count.
# PRET
`1110 0010 0111 ----`
Pre-return from subroutine. Pushes the return address to the CRS stack.
# PRMT
- **PRMT_reg**: `0101 1011 1100 ----`
- **PRMT_rc**: `0101 0011 1100 ----`
- **PRMT_cr**: `0100 1011 1100 ----`
- **PRMT_imm**: `0011 011- 1100 ----`
# PSET
`0101 0000 1000 1---`
Combine Predicates and Set.
# PSETP
`0101 0000 1001 0---`
Combine Predicates and Set Predicate.
# R2B
`1111 0000 1100 0---`
Move Register to Barrier.
# R2P
- **R2P_reg**: `0101 1100 1111 0---`
- **R2P_cbuf**: `0100 1100 1111 0---`
- **R2P_imm**: `0011 100- 1111 0---`
Move Register To Predicate/CC Register.
# RAM
`1110 0011 1000 ----`
# RED
`1110 1011 1111 1---`
Reduction Operation on Generic Memory.
# RET
`1110 0011 0010 ----`
Return.
# RRO
- **RRO_reg**: `0101 1100 1001 0---`
- **RRO_cbuf**: `0100 1100 1001 0---`
- **RRO_imm**: `0011 100- 1001 0---`
# RTT
`1110 0011 0110 ----`
# S2R
`1111 0000 1100 1---`
# SAM
`1110 0011 0111 ----`
# SEL
- **SEL_reg**: `0101 1100 1010 0---`
- **SEL_cbuf**: `0100 1100 1010 0---`
- **SEL_imm**: `0011 100- 1010 0---`
# SETCRSPTR
`1110 0010 1110 ----`
# SETLMEMBASE
`1110 0010 1111 ----`
# SHF
- **SHF_l_reg**: `0101 1011 1111 1---`
- **SHF_l_imm**: `0011 011- 1111 1---`
- **SHF_r_reg**: `0101 1100 1111 1---`
- **SHF_r_imm**: `0011 100- 1111 1---`
# SHFL
`1110 1111 0001 0---`
# SHL
- **SHL_reg**: `0101 1100 0100 1---`
- **SHL_cbuf**: `0100 1100 0100 1---`
- **SHL_imm**: `0011 100- 0100 1---`
# SHR
- **SHR_reg**: `0101 1100 0010 1---`
- **SHR_cbuf**: `0100 1100 0010 1---`
- **SHR_imm**: `0011 100- 0010 1---`
# SSY
`1110 0010 1001 ----`
Set Synchronization Point.
# ST
`101- ---- ---- ----`
Store to generic Memory.
# STG
`1110 1110 1101 1---`
Store to global Memory.
# STL
`1110 1111 0101 0---`
Store within Local or Shared Window.
# STP
`1110 1110 1010 0---`
Store to generic Memory and Predicate.
# STS
`1110 1111 0101 1---`
Store within Local or Shared Window.
# SUATOM
- **SUATOM**: `1110 1010 0--- ----`
- **SUATOM_cas**: `1110 1010 1--- ----`
Atomic Op on Surface Memory.
# SULD
`1110 1011 000- ----`
Surface Load.
# SURED
`1110 1011 010- ----`
Reduction Op on Surface Memory.
# SUST
`1110 1011 001- ----`
Surface Store.
# SYNC
`1111 0000 1111 1---`
# TEX
- **TEX**: `1100 0--- ---- ----`
- **TEX_b**: `1101 1110 10-- ----`
- **TEXS**: `1101 -00- ---- ----`
Texture Fetch with scalar/non-vec4 source/destinations.
# TLD
- **TLD**: `1101 1100 ---- ----`
- **TLD_b**: `1101 1101 ---- ----`
- **TLDS**: `1101 -01- ---- ----`
Texture Load with scalar/non-vec4 source/destinations.
# TLD4
- **TLD4**: `1100 10-- ---- ----`
- **TLD4_b**: `1101 1110 11-- ----`
- **TLD4S**: `1101 1111 -0-- ----`
Texture Load 4 with scalar/non-vec4 source/destinations.
# TMML
- **TMML**: `1101 1111 0101 1---`
- **TMML_b**: `1101 1111 0110 0---`
Texture MipMap Level.
# TXA
`1101 1111 0100 0---`
# TXD
- **TXD**: `1101 1110 00-- ----`
- **TXD_b**: `1101 1110 01-- ----`
Texture Fetch With Derivatives.
# TXQ
- **TXQ**: `1101 1111 0100 1---`
- **TXQ_b**: `1101 1111 0101 0---`
Texture Query.
# VABSDIFF
`0101 0100 ---- ----`
# VABSDIFF4
`0101 0000 0--- ----`
# VADD
`0010 00-- ---- ----`
# VMAD
`0101 1111 ---- ----`
# VMNMX
`0011 101- ---- ----`
# VOTE
- **VOTE**: `0101 0000 1101 1---`
- **VOTE_vtg**: `0101 0000 1110 0---`
Vote Across SIMD Thread Group
# VSET
`0100 000- ---- ----`
# VSETP
`0101 0000 1111 0---`
# VSHL
`0101 0111 ---- ----`
# VSHR
`0101 0110 ---- ----`
# XMAD
- **XMAD_reg**: `0101 1011 00-- ----`
- **XMAD_rc**: `0101 0001 0--- ----`
- **XMAD_cr**: `0100 111- ---- ----`
- **XMAD_imm**: `0011 011- 00-- ----`
Integer Short Multiply Add.

View File

@@ -10,6 +10,4 @@ This contains documentation created by developers. This contains build instructi
- **[Debug Guidelines](./Debug.md)**
- **[CPM - CMake Package Manager](CPMUtil.md)**
- **[Platform-Specific Caveats](Caveats.md)**
- **[The NVIDIA SM86 (Maxwell) GPU](./NvidiaGpu.md)**
- **[User Handbook](./user)**
- **[Release Policy](./ReleasePolicy.md)**

View File

@@ -1,25 +0,0 @@
# Release Policy
While releases are usually made at the discretion of the team, we feel that establishing a clearer guideline on how those come to be will help expectations when it comes to features and fixes per version.
## Release candidates
Every full release is *preceded* by at least, 3 release candidates. The reasoning is that each week of the month, there will be a release candidate, with the "4th one" being the final full release.
The main expectation is that the release candidates bring both fixes and, sometimes, new features. But not guarantee a regression-free experience.
The criteria for choosing a date for a release candidate is at discretion, or "perceived necesity" at any given time.
## Full release
A full release means there are *no major* leftover regressions, importantly this means that a grand portion of regressions found between release candidates are swept out before declaring a full release. This doesn't mean a full release is regression-free; but we do a best-effort approach to reduce them for end-users.
The main expectation is that users can safely upgrade from a stable build to another, with no major regressions.
## Snapshot/rolling release
While we don't publish rolling releases, we are aware users may compile from source and/or provide binaries to master builds of the project.
This is mostly fine since we keep master very stable from major hiccups. However sometimes bugs do slip between tests or reviews - so users are advised to keep that in mind.
We advise that users also read git logs (`git log --oneline`) before recompiling to get a clearer picture of the changes given into the emulator.

View File

@@ -0,0 +1,28 @@
# Setting Controller Profiles By Game
Use this guide when you want to set up specific controller profiles for specific games. This can be useful for certain games like *Captain Toad Treasure Tracker* where a blue dot appears in the middle of the screen when you have docked mode enabled, but not handheld mode.
**Click [Here](https://evilperson1337.notion.site/Setting-Controller-Profiles-By-Game-2b057c2edaf681658a57f0c199cb6083) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Eden Emulator set up and fully configured
- Controller Profile Created
- See [*Configuring Controller Profiles*](./ControllerProfiles.md) for instructions on how to do this if needed.
---
## Steps
1. *Right-Click* the game you want to apply the profile to in the main window and select **Properties.**
2. Navigate to the **Input Profiles** tab in the window that appears. Drop down on *Player 1 profile* (or whatever player profile you want to apply it to) and select the profile you want.
<aside>
***NOTE***: You may have to resize the window to see all tabs, or press the arrows by the tabs to see **Input Profiles**.
</aside>
1. Click **OK** to apply the profile mapping.
2. Launch the game and confirm that the profile is applied, regardless of what the global configuration is.

View File

@@ -12,38 +12,9 @@ Use this guide for when you want to configure specific controller settings to be
---
### Steps
## Steps
1. Launch Eden and wait for it to load.
2. Navigate to *Emulation > Configure…*
3. Select **Controls** from the left-hand menu and configure your controller for the way you want it to be in game.
4. Select **New** and enter a name for the profile in the box that appears. Press **OK** to save the profile settings.
5. Select **OK** to close the settings menu.
## Setting Controller Profiles By Game
Use this guide when you want to set up specific controller profiles for specific games. This can be useful for certain games like *Captain Toad Treasure Tracker* where a blue dot appears in the middle of the screen when you have docked mode enabled, but not handheld mode.
**Click [Here](https://evilperson1337.notion.site/Setting-Controller-Profiles-By-Game-2b057c2edaf681658a57f0c199cb6083) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Eden Emulator set up and fully configured
- Controller Profile Created
- See [*Configuring Controller Profiles*](./ControllerProfiles.md) for instructions on how to do this if needed.
---
### Steps
1. *Right-Click* the game you want to apply the profile to in the main window and select **Properties.**
2. Navigate to the **Input Profiles** tab in the window that appears. Drop down on *Player 1 profile* (or whatever player profile you want to apply it to) and select the profile you want.
<aside>
***NOTE***: You may have to resize the window to see all tabs, or press the arrows by the tabs to see **Input Profiles**.
</aside>
1. Click **OK** to apply the profile mapping.
2. Launch the game and confirm that the profile is applied, regardless of what the global configuration is.
5. Select **OK** to close the settings menu.

View File

@@ -1,22 +1,10 @@
# User Handbook - Troubleshooting
## Vulkan initialization error
- Ensure you have the latest drivers
- Uninstall old drivers, for Windows you can use [Display Driver Uninstaller](https://www.guru3d.com/download/display-driver-uninstaller-download/)
- Change backend manually in the settings file (set it from `0` to `1` or viceversa).
- Disconnect your second monitor, if any
## This mod only works on an Emulator
- Enable RNG seed
- Set RNG seed to 0
## Eden Fails to Launch and Does Not Leave Any Logs
# Eden Fails to Launch and Does Not Leave Any Logs
**Click [Here](https://evilperson1337.notion.site/Windows-Eden-Fails-to-Launch-and-Does-Not-Leave-Any-Logs-2b057c2edaf68156b640cf1ac549870a) for a version of this guide with images & visual elements.**
### Error Details
---
## Error Details
*Behavior*: Program appears not to launch or exits immediately without leaving any log entries.
*Platform(s) Affected*:
@@ -44,11 +32,13 @@ Faulting package-relative application ID:
---
### Causes
## Causes
<aside>
#### Issue 1: Missing C++ Redistributable
### Issue 1: Missing C++ Redistributable
---
*Eden requires the latest C++ redistributable from Microsoft in order to run. Like many other programs, it relies on aspects and libraries included in this runtime, without it - the program cannot run.*
@@ -60,7 +50,9 @@ Faulting package-relative application ID:
<aside>
#### Issue 2: Corrupted System Files
### Issue 2: Corrupted System Files
---
*A corruption of necessary system files can cause odd behaviors when Eden tries to access them. It is a very rare case and you would likely see other programs misbehaving if this is what your issue is, but you can try if you have no other options.*
@@ -96,4 +88,4 @@ Faulting package-relative application ID:
8. Reboot your computer.
9. Launch Eden and verify it is now working.
</aside>
</aside>

View File

@@ -90,22 +90,3 @@ The OpenGL backend would invoke behaviour that would result in swarst/LLVMpipe w
### HaikuOS compatibility
HaikuOS bundles a Mesa library that doesn't support full core OpenGL 4.6 (required by the emulator). This leads to HaikuOS being one of the few computer platforms where Vulkan is the only available option for users. If OpenGL is desired, Mesa has to be built manually from source. For debugging purpouses `lavapipe` is recommended over the GPU driver; there is in-kernel support for NVIDIA cards through.
### Fixes for Windows 10 and above having "Device loss"
Run the following batch script *inside* the Eden folder:
```cmd
@echo off
pushd "%~dp0"
if exist "%temp%\FixFullScreen.reg" (
del %temp%\FixFullScreen.reg
)
set str_path="%cd:\=\\%\\eden.exe"
echo Windows Registry Editor Version 5.00 >> %temp%\FixFullScreen.reg
echo. >> %temp%\FixFullScreen.reg
echo [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers] >> %temp%\FixFullScreen.reg
echo %str_path%="~ DISABLEDXMAXIMIZEDWINDOWEDMODE HIGHDPIAWARE" >> %temp%\FixFullScreen.reg
regedit /s %temp%\FixFullScreen.reg
del %temp%\FixFullScreen.reg
exit /b
```

View File

@@ -3,7 +3,7 @@ Use this guide when you want to use the Steam Deck's native gyro functionality f
**Click [Here](https://evilperson1337.notion.site/Getting-Gyro-Motion-Controls-Working-on-Steam-Deck-2b057c2edaf681a1aaade35db6e0fd1b) for a version of this guide with images & visual elements.**
## Steamdeck
---
### Pre-Requisites
@@ -13,7 +13,7 @@ Use this guide when you want to use the Steam Deck's native gyro functionality f
---
### Steps
## Steps
1. Go into Steam Deck's Desktop Mode, and use the shortcut to launch EmuDeck.
2. Install [SteamDeckGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU/releases) by going to *3rd Party Tools > Gyroscope* and clicking **Install.**

View File

@@ -4,13 +4,15 @@ Use this guide when you want to manually import save files for use in the Eden e
**Click [Here](https://evilperson1337.notion.site/Importing-Saves-Into-Eden-2b057c2edaf681fe968df8d63821ccae) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Eden emulator already set up and configured.
- The save file(s) you want to import
## Desktop
---
### Steps
## Steps
1. Open Eden and wait for it to load.
2. Start the game and create a save file to establish the directories.
3. *Right-Click* the game for which you want to load a save in.
@@ -23,8 +25,4 @@ Use this guide when you want to manually import save files for use in the Eden e
</aside>
7. Close the file explorer as it is no longer needed.
8. Launch the game in Eden and verify that the save data appears through whatever method the game implements.
## Android
TBD
8. Launch the game in Eden and verify that the save data appears through whatever method the game implements.

View File

@@ -1,4 +1,4 @@
# User Handbook - Installing Atmosphere Mods
# Installing Atmosphere Mods
Use this guide for when you want to install an Atmosphere-based mod for use in Eden.

View File

@@ -1,4 +1,4 @@
# User Handbook - Working with Updates/DLC in Eden
# Working with Updates/DLC in Eden
Use this guide when you want to install Updates or DLC for your games in Eden.

View File

@@ -1,296 +0,0 @@
# Multiplayer
Use this guide to answer questions regarding and to start using the multiplayer functionality of Eden.
## Multiplayer FAQ
This FAQ will serve as a general quick question and answer simple questions.
**Click [Here](https://evilperson1337.notion.site/Multiplayer-FAQ-2c357c2edaf680fca2e9ce59969a220f) for a version of this guide with images & visual elements.**
### Can Eden Play Games with a Switch Console?
No - The only emulator that has this kind of functionality is *Ryujinx* and it's forks. This solution requires loading a custom module on a modded switch console to work.
### Can I Play Online Games?
No - This would require hijacking requests to Nintendo's official servers to a custom server infrastructure built to emulate that functionality. This is how services like [*Pretendo*](https://pretendo.network/) operate. As such, you would not be able to play “Online”, you can however play multiplayer games.
### What's the Difference Between Online and Multiplayer?
I have chosen the wording carefully here for a reason.
- Online: Games that connect to Nintendo's Official servers and allow games/functionality using that communication method are unsupported on any emulator currently (for obvious reasons).
- Multiplayer: The ability to play with multiple players on separate systems. This is supported for games that support LDN Local Wireless.
The rule of thumb here is simple: If a game supports the ability to communicate without a server (Local Wireless, LAN, etc.) you will be able to play with other users. If it requires a server to function - it will not. You will need to look up if your title support Local Wireless/LAN play as an option.
### How Does Multiplayer Work on Eden Exactly?
Eden's multiplayer works by emulating the Switch's local wireless (LDN) system, then tunneling that traffic over the internet through “rooms” that act like lobbies. Each player runs their own instance of the emulator, and as long as everyone joins the same room and the game supports local wireless multiplayer, the emulated consoles see each other as if they were on the same local network. This design avoids typical one-save netplay issues because every user keeps an independent save and console state while only the in-game wireless packets are forwarded through the room server. In practice, you pick or host a room, configure your network interface/port forwarding if needed, then launch any LDN-capable game; from the game's perspective it is just doing standard local wireless, while the emulator handles discovery and communication over the internet or LAN.
### What Do I Need to Do?
That depends entirely on what your goal is and your level of technical ability, you have a 2 options on how to proceed.
1. Join a Public Lobby.
1. If you just want to play *Mario Kart 8 Deluxe* with other people and don't care how it works or about latency - this is the option for you. See the *Joining a Multiplayer Room* section for instructions on how to do this.
2. Host Your Own Room.
1. This option will require you to be comfortable with accessing your router's configuration, altering firewall rules, and troubleshooting when things (inevitably) don't work out perfectly on the first try. Use this option if you want to control the room entirely, are concerned about latency issues, or just want to run something for your friends. See the *Hosting a Multiplayer Room* section for next steps*.*
### Can Other Platforms Play Together?
Yes - the platform you choose to run the emulator on does not matter. Steam Deck users can play with Windows users, Android users can play with MacOS users, etc. Furthermore different emulators can play together as well (Eden/Citron/Ryubing, etc.) - but be you may want to all go to the same one if you are having issues.
### What Pitfalls Should I Look Out For?
While it would be nice if everything always worked perfectly - that is not reality. Here are some things you should watch out for when attempting to play multiplayer.
1. Emulator Version Mismatches
1. Occasionally updates to the emulator of choice alter how the LDN functionality is handled. In these situations, unexpected behavior can occur when trying to establish LDN connections. This is a good first step to check if you are having issues playing a game together, but can join the same lobby without issue.
2. Game Version Mismatches
1. It is best practice to have the game version be identical to each other in order to ensure that there is no difference in how the programs are handling the LDN logic. Games are black boxes that the dev team cannot see into to ensure the logic handling operates the same way. For this reason, it is highly advised that the game versions match across all the players. This would be a good 2nd step to check if you are having issues playing a game together, but can join the same lobby without issue.
3. Latency
1. Because this implementation is emulating a LAN/Local Wireless connection - it is extremely sensitive to network latency and drops. Eden has done a good job of trying to account for this and not immediately drop users out - but it is not infallible. If latency is a concern or becomes an issue - consider hosting a room.
---
## Joining a Multiplayer Room
Use this when you need to connect to a multiplayer room for LDN functionality inside of Eden. This does not cover how to host a room, only joining existing ones.
**Click [Here](https://evilperson1337.notion.site/Access-Your-Multiplayer-Room-Externally-2c357c2edaf681c0ab2ce2ee624d809d) for a version of this guide with images & visual elements.**
### Pre-Requisites
- Eden set up and functioning
- Multiplayer Options Configured in Eden Settings
- Network Access
## Steps
There are 2 primary methods that you can use to connect to an existing room, depending on how the room is hosted.
- Joining a Public Lobby
- This option allows you to view publicly hosted lobbies and join them easily. Use this option if you just want to join a room and play quickly.
- Directly Connecting to a Room
- Use this option if the hosted room is not on the public lobby list (private, internal network only, etc.)
<aside>
***NOTE:*** Just because a lobby appears on the public lobby list, does not mean that the hoster has properly configured the necessary port forwarding/firewall rules to allow a connection. If you cannot connect to a lobby, move onto another entry as the issue is probably not on your end. Start looking at your environment if you are unable to connect to multiple/any lobbies.
</aside>
### Joining a Public Lobby
1. Open Eden and navigate to *Multiplayer → Browse Public Game Lobby*.
2. The **Public Room Browser** will now open and display a list of publicly accessible rooms. Find one you want to connect to and double click it.
<aside>
***NOTE***: Just because a room is set for a specific game, does not mean that you ***have*** to play that game in that lobby. It is generally good practice to do so, but there is no enforcement of that.
</aside>
3. You will now see a window showing everyone on the lobby, or an error message.
### Direct Connecting to a Room
If the hoster has not made the lobby public, or you don't want to find it in the public game browser - use this option to connect.
1. Open Eden and navigate to *Multiplayer → Direct Connect*.
2. Enter the *Server Address, Port*, *Nickname* (what your user will be called in the room), and a *Password* (if the hoster set one, otherwise leave it blank) and hit **Connect.**
3. You will now see a window showing everyone on the lobby, or an error message.
---
# Hosting a Multiplayer Room
Use this guide for when you want to host a multiplayer lobby to play with others in Eden. In order to have someone access the room from outside your local network, see the *Access Your Multiplayer Room Externally* section for next steps.
**Click [Here](https://evilperson1337.notion.site/Hosting-a-Multiplayer-Room-2c357c2edaf6819481dbe8a99926cea2) for a version of this guide with images & visual elements.**
### Pre-Requisites
- Eden set up and Functioning
- Network Access
- Ability to allow programs through the firewall on your device.
## Steps
1. Open Eden and navigate to *Emulation → Multiplayer → Create Room.*
2. Fill out the following information in the popup dialog box.
| **Option** | **Acceptable Values** | **Default** | **Description** |
| --- | --- | --- | --- |
| Room Name | *Any string between 4 - 20 characters.* | *None* | Controls the name of the room and how it would appear in the Public Game Lobby/Room screen. |
| Username | *Any string between 4 - 20 characters.* | *None* | Controls the name your user will appear as to the other users in the room. |
| Preferred Game | *Any Game from your Game List.* | The first game on your game list | What game will the lobby be playing? You are not forced to play the game you choose and can switch games without needing to recreate the room. |
| Password | *None or any string* | *None* | What password do you want to secure the room with, if any. |
| Max Players | 2 - 16 | 8 | How many players do you want to allow in the room at a time? |
| Port | 1024 - 65535 | 24872 | What port do you want to run the lobby on? Could technically be any port number, but it's best to choose an uncommon port to avoid potential conflicts. See [*Well-Known Ports*](https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports) for more information on ports commonly used. |
| Room Description | *None or any string* | *None* | An optional message that elaborates on what the room is for, or for a makeshift message of the day presented to users in the lobby. |
| Load Previous Ban List | [Checked, Unchecked] | Checked | Tells Eden to load the list containing users you have banned before. |
| Room Type | [Public, Unlisted] | Public | Specifies whether you want the server to appear in the public game lobby browser |
3. Click **Host Room** to start the room server. You may get a notice to allow the program through the firewall from your operating system. Allow it and then users can attempt to connect to your room.
---
# Access Your Multiplayer Room Externally
Quite often the person with whom you want to play is located off of your internal network (LAN). If you want to host a room and play with them you will need to get your devices to communicate with each other. This guide will go over your options on how to do this so that you can play together.
**Click [Here](https://evilperson1337.notion.site/Access-Your-Multiplayer-Room-Externally-2c357c2edaf681c0ab2ce2ee624d809d) for a version of this guide with images & visual elements.**
### Pre-Requisites
- Eden set up and Functioning
- Network Access
## Options
### Port Forwarding
- **Difficulty Level**: High
<aside>
Use this option if you want the greatest performance/lowest latency, don't want to install any software, and have the ability to modify your networking equipment's configuration (most notably - your router). Avoid this option if you cannot modify your router's configuration or are uncomfortable with looking up things on your own.
</aside>
Port forwarding is a networking technique that directs incoming traffic arriving at a specific port on a router or firewall to a designated device and port inside a private local network. When an external client contacts the public IP address of the router on that port, the router rewrites the packet's destination information (IP address and sometimes port number) and forwards it to the internal host that is listening on the corresponding service. This allows services such as web servers, game servers, or remote desktop sessions hosted behind NAT (Network Address Translation) to be reachable from the wider Internet despite the devices themselves having non-routable private addresses.
The process works by creating a static mapping—often called a “port-forward rule”—in the router's configuration. The rule specifies three pieces of data: the external (public) port, the internal (private) IP address of the target machine, and the internal port on which that machine expects the traffic. When a packet arrives, the router checks its NAT table, matches the external port to a rule, and then translates the packet's destination to the internal address before sending it onward. Responses from the internal host are similarly rewritten so they appear to come from the router's public IP, completing the bidirectional communication loop. This mechanism enables seamless access to services inside a protected LAN without exposing the entire network.
For our purposes we would pick the port we want to expose (*e.g. 24872*) and we would access our router's configuration and create a port-forward rule to send the traffic from an external connection to your local machine over our specified port (*24872)*. The exact way to do so, varies greatly by router manufacturer - and sometimes require contacting your ISP to do so depending on your agreement. You can look up your router on [*portforward.com*](https://portforward.com/router.htm) which may have instructions on how to do so for your specific equipment. If it is not there, you will have to use Google/ChatGPT to determine the steps for your equipment.
### Use a Tunnelling Service
- **Difficulty Level**: Easy
<aside>
Use this option if you don't want to have to worry about other users machine/configuration settings, but also cannot do port forwarding. This will still require that you as the hoster install a program and sign up for an account - but will prevent you from having to deal with port forwards or networking equipment. Avoid this option if there is not a close relay and you are getting issues with latency.
</aside>
Using a Tunnelling service may be the solution to avoid port forward, but also avoid worrying about your users setup. A tunnelling service works by having a lightweight client run on the machine that hosts the game server. That client immediately opens an **outbound** encrypted connection (typically over TLS/QUIC) to a relay node operated by the tunnel provider's cloud infrastructure. Because outbound traffic is almost always allowed through NAT routers and ISP firewalls, the tunnel can be established even when the host sits behind carrier-grade NAT or a strict firewall. The tunnel provider then assigns a public address (e.g.,`mygame.playit.gg:12345`). When a remote player connects to that address, the traffic reaches the the tunnel provider relay, which forwards it through the already-established tunnel back to the client on the private network, and finally onto the local game server's port. In effect, the server appears to the Internet as if it were listening on the public address, while the host never needs to configure port-forwarding rules or expose its own IP directly.
For our purposes we would spawn the listener for the port that way chose when hosting our room. The user would connect to our assigned public address/port combination, and it would be routed to our machine. The tunnel must remain active for as long as you want the connection to remain open. Closing the terminal will kill the tunnel and disconnect the users.
**Recommended Services:**
- [*Playit.GG*](https://playit.gg/)
### Use a VPN Service
- **Difficulty**: Easy
<aside>
Use this option if you don't want to use a tunnelling service, or don't want to manage the overhead of the playit gg solution, don't want to send data through the relay, or any other reason. Avoid this method if you do not want to have to manage VPN installation/configuration for your users.
</aside>
The VPN solution is a good compromise between the tunnelling solution and port forwarding. You do not have to port forward or touch your networking equipment at all - but also don't need to send all your data connections through a 3rd party relay. The big downside is that you will have to ensure all of your users have your VPN solution installed *and* that they have a valid configuration. When looking for a solution, it is advised to find one that uses the WireGuard protocol for speed, and does not require communication with a server beyond the initial handshake.
**Recommended Services:**
- [*Tailscale*](https://tailscale.com/)
- [*ZeroTier*](https://www.zerotier.com/)
- *Self-hosted VPN Solution*
- This is so far out of the scope of this document it has a different postal code.
*Check with the provider you select on the sign up and installation process specific to that provider.*
---
# Finding the Server Information for a Multiplayer Room
Use this guide when you need to determine the connection information for the Public Multiplayer Lobby you are connected to.
**Click [Here](https://evilperson1337.notion.site/Finding-the-Server-Information-for-a-Multiplayer-Room-2c557c2edaf6809e94e8ed3429b9eb26) for a version of this guide with images & visual elements.**
### Pre-Requisites
- Eden set up and configured
- Internet Access
## Steps
### Method 1: Grabbing the Address from the Log File
1. Open Eden and Connect to the room you want to identify.
1. See the *Joining a Multiplayer Room* section for instructions on how to do so if you need them.
2. Go to *File → Open Eden Folder*, then open the **config** folder.
3. Open the the **qt-config.ini** file in a text editor.
4. Search for the following keys:
1. `Multiplayer\ip=`
2. `Multiplayer\port=`
5. Copy the Server Address and Port.
### Method 2: Using a Web Browser
1. Obtain the name of the room you want the information for.
2. Open a Web Browser.
3. Navigate to [`https://api.ynet-fun.xyz/lobby`](https://api.ynet-fun.xyz/lobby)
4. Press *Ctrl + F* and search for the name of your room.
5. Look for and copy the Server Address and Port.
### Method 3: Using a Terminal (PowerShell or CURL)
1. Obtain the name of the room you want the information for.
2. Open the terminal supported by your operating system.
3. Run one of the following commands, replacing *<Name>* with the name of the server from step 1.
### PowerShell Command [Windows Users]
```powershell
# Calls the API to get the address and port information
(Invoke-RestMethod -Method Get -Uri "https://api.ynet-fun.xyz/lobby").rooms | Where-Object {$_.Name -eq '<NAME>'} | Select address,port | ConvertTo-Json
# Example Output
#{
# "address": "118.208.233.90",
# "port": 5001
#}
```
### CURL Command [MacOS/Linux Users] **Requires jq*
```bash
# Calls the API to get the address and port information
curl -s "https://api.ynet-fun.xyz/lobby" | jq '.rooms[] | select(.name == "<NAME>") | {address, port}'
# Example Output
#{
# "address": "118.208.233.90",
# "port": 5001
#}
```
4. Copy the Server Address and Port.
---
# Multiplayer for Local Co-Op Games
Use this guide when you want to play with a friend on a different system for games that only support local co-op.
**Click [Here](https://evilperson1337.notion.site/Multiplayer-for-Local-Co-Op-Games-2c657c2edaf680c59975ec6b52022a2d) for a version of this guide with images & visual elements.**
Occasionally you will want to play a game with a friend on a game that does not support LDN multiplayer, and only offer local co-op (multiple controllers connected to a single console), such as with *New Super Mario Bros. U Deluxe.* Emulation solutions have developed 2 primary methods for handling these cases.
1. Netplay: Netplay lets two or more players run the same game on their own computers while sharing each other's controller inputs over the internet, so everyone sees the same game world in sync. One player hosts the session, and the others join as guests, sending their button presses back and forth to keep the gameplay coordinated.
1. This is a huge over-simplification of how it works, but gives you an idea
2. Low-Latency remote desktop solutions: Using a service like *Parsec*, the host shares his screen to a remote party with an input device connected. This device sends inputs to the host machine.
In either situation at its core, we are emulating an input device on the host machine, so the game believes 2 controllers are connected. No current Switch emulator has a Netplay offering, so we use Parsec to accomplish this for us.
### Pre-Requisites
- Eden Set Up and Fully Configured
- A [*Parsec*](https://parsec.app/) Account
- Parsec is free to use for personal, non-commercial use. For instructions on how to set up an account and install the client you should refer to the Parsec documentation on it's site.
- Parsec client installed on your machine and remote (friend's) machine
## Steps
<aside>
This guide will assume you are the one hosting the game and go over things *Parsec* specific at a high level, as their system is subject to change. Follow *Parsec's* documentation where needed.
</aside>
1. Launch Parsec on the host machine.
2. Connect to the other player in Parsec. You will know it is successful when the other player can see the host's screen.
1. If you are the one hosting the game, you will have your friend initiate the remote connection you will accept.
2. If you are joining a game, you will have to send a connection request the host will have to accept.
3. Verify that the remote player can see the screen and that there is no issues with the connection.
4. Launch Eden.
5. Navigate to *Emulation → Configure*.
6. Select the **Controls** tab.
7. Set up your controller, if necessary.
8. Select the **Player 2** tab and select the **Connect Controller** checkbox. This enables inputs from another device to be seen as a second controller.
9. Dropdown the **Input Device** and select the controller.
1. What exactly it shows up as depends on the Parsec settings.
10. Set up the remote player's controller.
11. Hit **OK** to apply the changes.
12. Launch the game you want to play and enter the co-op mode. How this works depends on the game, so you will have to look in the menus or online to find out.

View File

@@ -0,0 +1,42 @@
# Eden Quick Start
Use this guide to get starting using the Eden emulator on Steam Deck.
**Click [Here](https://evilperson1337.notion.site/Eden-Quick-Start-2b757c2edaf680d49ffdcda291a32840) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Firmware dumped from your console
- Keys extracted from your console
- Games dumped from your console
- Internet Connection
---
## Steps
1. Access Steam Desktop Mode.
2. Download either the *Stable* or *Nightly* Eden AppImage onto your Steam Deck and save it somewhere accessible.
<aside>
***TIP***: If you have questions about the requirements, architectures, or general information surrounding what release you need - see the [*Basics Guide*](./Basics.md) and [*Architectures Guide*](./Architectures.md).
</aside>
3. Double-Click the Eden executable to launch the program.
<aside>
***NOTE***: The first time you run the AppImage you will get a notification asking you to confirm you want to launch the program. Hit **Continue**.
</aside>
4. If you have had a different Switch emulator installed, it will detect and ask if you want to import those settings. Make your selection to close the screen.
5. Eden will now launch and notify you about missing Encryption keys. Close the dialog box by hitting **OK**.
6. Navigate to **Tools → Install Decryption Keys**, navigate to the folder containing your ***prod.keys*** file and select the file and hit **Open**.
7. Navigate to **Tools → Install Firmware →** *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
8. Double-Click the main window to add the folder containing your games.
9. Go to *Emulation > Configure > Input* and set up your controller. Click **OK** to close the dialog window.
10. Double-Click a game to run it.

View File

@@ -4,18 +4,21 @@ Use this guide to get starting using the Eden emulator.
**Click [Here](https://evilperson1337.notion.site/Eden-Quick-Start-2b057c2edaf6817b9859d8bcdb474017) for a version of this guide with images & visual elements.**
## Windows
---
### Pre-Requisites
- The [*latest C++ Redistributable*](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-supported-redistributable-version) from Microsoft.
- Eden will not even launch without it see [*Eden Fails to Launch*](./Troubleshoot.md) for more information.
- Eden will not even launch without it see [*Eden Fails to Launch*](./EdenFailsToLaunch.md) for more information.
- Firmware dumped from your console
- Keys extracted from your console
- Games dumped from your console
- Internet Connection
### Steps
---
## Steps
1. Download either the *Stable* or *Nightly* Eden application.
<aside>
@@ -41,39 +44,4 @@ Use this guide to get starting using the Eden emulator.
6. Navigate to **Tools → Install Firmware**, *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
7. Double-Click the main window to add the folder containing your games.
8. Go to *Emulation > Configure > Input* and set up your controller of choice. Click **OK** to close the dialog window.
9. Double-Click a game to run it.
## Steamdeck
### Pre-Requisites
- Firmware dumped from your console
- Keys extracted from your console
- Games dumped from your console
- Internet Connection
### Steps
1. Access Steam Desktop Mode.
2. Download either the *Stable* or *Nightly* Eden AppImage onto your Steam Deck and save it somewhere accessible.
<aside>
***TIP***: If you have questions about the requirements, architectures, or general information surrounding what release you need - see the [*Basics Guide*](./Basics.md) and [*Architectures Guide*](./Architectures.md).
</aside>
3. Double-Click the Eden executable to launch the program.
<aside>
***NOTE***: The first time you run the AppImage you will get a notification asking you to confirm you want to launch the program. Hit **Continue**.
</aside>
4. If you have had a different Switch emulator installed, it will detect and ask if you want to import those settings. Make your selection to close the screen.
5. Eden will now launch and notify you about missing Encryption keys. Close the dialog box by hitting **OK**.
6. Navigate to **Tools → Install Decryption Keys**, navigate to the folder containing your ***prod.keys*** file and select the file and hit **Open**.
7. Navigate to **Tools → Install Firmware →** *Select **From Folder*** or ***From ZIP*** - depending on how your firmware is stored, navigate to where it is stored and select it.
8. Double-Click the main window to add the folder containing your games.
9. Go to *Emulation > Configure > Input* and set up your controller. Click **OK** to close the dialog window.
10. Double-Click a game to run it.
9. Double-Click a game to run it.

View File

@@ -4,35 +4,14 @@ The "FAQ".
This handbook is primarily aimed at the end-user - baking useful knowledge for enhancing their emulation experience.
## Basics
- **[The Basics](Basics.md)**
- **[Quickstart](./QuickStart.md)**
- **[Run On macOS](./RunOnMacOS.md)**
- **[Audio](Audio.md)**
- **[Graphics](Graphics.md)**
- **[Data, Savefiles and Storage](Storage.md)**
- **[Orphaned Profiles](Orphaned.md)**
- **[Troubleshooting](./Troubleshoot.md)**
- **[Using Amiibo](./UsingAmiibo.md)**
- **[Using Cheats](./UsingCheats.md)**
- **[Importing Saves](./ImportingSaves.md)**
- **[Add Eden to Steam ROM Manager](./AddEdenToSRM.md)**
- **[Add Games to Steam ROM Manager](./AddGamesToSRM.md)**
- **[Installing Atmosphere Mods](./InstallingAtmosphereMods.md)**
- **[Installing Updates & DLCs](./InstallingUpdatesDLC.md)**
- **[Controller Profiles](./ControllerProfiles.md)**
- **[Alter Date & Time](./AlterDateTime.md)**
## Advanced
- **[How To Access Logs](./HowToAccessLogs.md)**
- **[Gyro Controls](./GyroControls.md)**
- **[Platforms and Architectures](Architectures.md)**
- **[Server hosting](ServerHosting.md)**
- **[Graphics](Graphics.md)**
- **[Platforms and Architectures](Architectures.md)**
- **[Testing](Testing.md)**
- **[Data, savefiles and storage](Storage.md)**
- **[Orphaned Profiles](Orphaned.md)**
- **[Command Line](CommandLine.md)**
- **[Native Application Development](Native.md)**
- **[Adding Boolean Settings Toggles](AddingBooleanToggles.md)**
- **[Adding Debug Knobs](./AddingDebugKnobs.md)**
- **[Syncthing Guide](./SyncthingGuide.md)**
- **[Testing](Testing.md)**

View File

@@ -1,211 +0,0 @@
# User Handbook - Backing Up/Syncing Eden Game Saves
Use this guide for when you want to configure automated backup/syncing of your Eden save files using [*Syncthing*](https://syncthing.net/).
**Click [Here](https://evilperson1337.notion.site/Backing-Up-Syncing-Eden-Game-Saves-2b357c2edaf68000b40cfab2c2c3dc0a) for a version of this guide with images & visual elements.**
### Pre-Requisites
- Eden already installed, configured, and functioning.
- Devices to run Syncthing on.
- Ability to allow a program to communicate through the firewall of your device.
## Introduction
<aside>
***WARNING***: You should manually back up your save files before proceeding with this guide. If you incorrectly perform the steps, you risk losing them!
</aside>
- While this is a de-centralized model without the concepts of a Server/Client, Parent/Child, etc. - For the purposes of these guides, we will borrow from this models terminology to avoid sync conflicts and potential data loss. After the initial setup, all the devices in the sync network are equals and can push & pull files from any other device.
- In order for this to work, you should get all of the save files in Eden in the save folder on the Parent.
- If you need help doing that, see the ***Importing Saves into Eden*** guide for the platform you elect to act as the Parent, and delete the save files on the "Child" devices.
### Terminology
- **Sync Network**: All the devices configured in *Syncthing* to push/pull files.
- **Parent**: This will be the device that you elect to push files to the other devices. There can only be one here initially in order to avoid sync conflicts.
- **Child**: All the other devices added to the Sync Network. These devices will pull files from the Parent.
## Overview
Rather than giving a breakdown of all the platforms and configurations, those will be in the platforms specific guides - this will serve as a general overview of Syncthing.
### What is Syncthing Anyway?
Syncthing is a continuous file synchronization program (in the laymans - make sure 2 or more systems with the same files are always up to date). This is perfect for game saves where we would want to play on 1 device, save our game, and then continue playing it on another device. This technology is what Epic/Steam/etc. use to allow you to do this on games run through their respective services. Syncthing is an open source implementation of this technology that you control, rather than relying on a 3rd party. This has a few key benefits, most notably - better security, privacy, and speed (when on your LAN).
### What are some common issues?
Syncthing is fairly robust and doesnt have many issues luckily, but there are some things you should watch out for (almost all of them a user issue).
- Sync conflicts
- If for whatever reason you update the same file on 2 different machines, the system does not know which updated file is considered the one to sync across. This results in a ***sync conflict*** where it may not sync the files as you would expect. Worst case scenario, this can result in your save progress being lost if you are not careful. When one of these occurs, it will create a copy of the file and store it with a specific name, like this example, *Paper Mario.sync-conflict-20251102-072925-TZBBN6S.srm.* To resolve this, you must remove the other files and remove the *.sync-conflict-<TIMESTAMP>-<Syncthing Device ID>* from the file name of the file you want to keep.
- Accidental Deletions
- If you delete a file from one of the devices, it will also remove the file on the other devices when they perform a sync so be careful when doing this.
## Windows
### Pre-Requisites
- Eden already installed, configured, and functioning.
- Ability to allow a program to communicate through the firewall in Windows.
- Ability to extract archive (.zip/.7z/.rar) files.
### Steps
<aside>
***WARNING***: You should manually back up your save files before proceeding with this guide. If you incorrectly perform the steps, you risk losing them!
</aside>
#### Downloading and Installing *Syncthing*
1. Download [*Syncthing Tray*](https://martchus.github.io/syncthingtray/#downloads-section).
1. While it is available as a command line interface, for most people I would recommend *Syncthing Tray* on Windows. For most people here, you would download the **64-bit (Intel/AMD)** version.
2. Open the downloaded archive and extract the **syncthingtray.exe** to wherever you want to store the executable.
3. Double-Click the application to run it, select the **Start guided setup** on the splash screen that appears and press **Next**.
<aside>
***NOTE***: You may get a Windows Defender Smart Screen pop up, this is a known thing, just accept and run anyway.
</aside>
4. It will then look for an existing Syncthing instance to pull settings from, but will likely fail to do so if you are here. Regardless, select the **Yes, continue configuration** option.
5. Select ***Start Syncthing application that is built into Syncthing Tray***, this means it will use a built in Syncthing executable rather than relying on an externally provided one. Press **Next** to continue.
6. Check the box to start Syncthing Tray on login - as the name implies, this means the program will run automatically whenever you log onto the computer. Press Next to continue.
7. You will now be presented with a confirmation window with your selections, confirm they are what you want and hit **Apply** to continue.
8. You will now be prompted with a confirmation window and a message to allow it through the firewall. Allow the access through the firewall to close that pop up. The confirmation screen has a QR code and the devices identifier - you will need one of these to add other devices to the sync system.
9. *Syncthing/Syncthing Tray* are now installed.
#### Configuring this Machine as a Parent
Use this when you want to set this machine as the initial source of truth (push files out to all the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
3. Well start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
5. Enter the Full folder path to where your save files are stored on this machine.
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
6. Ignore the other tabs for now and hit **Save**.
7. The folder is now ready to be shared with other devices.
#### Configuring this Machine as a Child
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Install Syncthing Tray on the client device following the section above. Copy the childs ID and store it so it is accessible to the Parent.
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
5. Add the ID and Name the device, the device may appear as a **nearby device**, in which case you can just click it to pre-populate the Device ID.
6. Click the **Sharing** Tab, and check the box next to the folder you set up on the Parent (Switch Saves in my case). Hit **Save.**
7. We are done with the parent, now **SWITCH OVER TO THE CHILD.**
8. ***ON THE CHILD***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
9. You should now see a connection request from the parent. Hit **+ Add Device** to add the device.
10. Hit **Save** to finish adding the device.
11. That pop-up will close and you will get notification that the device wants to share a folder now. Hit **Add.**
12. Enter the path to the save folder in Eden and hit **Save.**
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered “*Parents*” for other devices you want to add. Repeat these steps for as many devices you want.
## Linux
### Pre-Requisites
- Eden already installed, configured, and functioning.
### Step 1: Downloading and Installing Syncthing
<aside>
***WARNING***: You should manually back up your save files before proceeding with this guide. If you incorrectly perform the steps, you risk losing them!
</aside>
<aside>
***NOTE***: I am using Linux Mint for my guides, but the steps should translate pretty easily to your distro. I ***hope*** that if you are running Linux you know the basic operations. Steam Deck users should follow the guide specific to that platform.
</aside>
1. Download [*Syncthing Tray*](https://flathub.org/en/apps/io.github.martchus.syncthingtray) from the Flatpak store.
2. Launch *Syncthing Tray* to run it, select the **Start guided setup** on the splash screen that appears and press **Next**.
3. It will then look for an existing *Syncthing* instance to pull settings from, but will likely fail to do so if you are here. Regardless, select the **Yes, continue configuration** option.
4. Select ***Start installed Syncthing application via Syncthing Tray***, this means it will use a built in Syncthing executable rather than relying on an externally provided one. Press **Next** to continue.
5. You will now be presented with a confirmation window with your selections, confirm they are what you want and hit **Apply** to continue.
6. You will now be prompted with a confirmation window that has a QR code and the devices identifier - you will need one of these to add other devices to the sync system.
7. *Syncthing/Syncthing Tray* are now installed. Press Finish to close the pop up.
<aside>
***NOTE***: By default due to flatpak sandboxing limitations, Syncthing Tray will not run automatically on login. You can get around this by following the [*instructions here*](https://github.com/flathub/io.github.martchus.syncthingtray).
</aside>
### Step 2: Configuring this Machine as a Parent
Use this when you want to set this machine as the initial source of truth (push files out to all the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
1. If you dont have a taskbar in your distro, you can also reach it directly by opening a web browser to: *http://127.0.0.1:8384/.*
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
3. Well start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
5. Enter the Full folder path to where your save files are stored on this machine.
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
6. Ignore the other tabs for now and hit **Save**.
7. The folder is now ready to be shared with other devices.
### Step 3: Configuring this Machine as a Child
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Install Syncthing Tray on the client device following the section above. Copy the childs ID and store it so it is accessible to the Parent.
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
5. Add the ID and Name the device, the device may appear as a **nearby device**, in which case you can just click it to pre-populate the Device ID.
6. Click the **Sharing** Tab, and check the box next to the folder you set up on the Parent (Switch Saves in my case). Hit **Save.**
7. We are done with the parent, now **SWITCH OVER TO THE CHILD.**
8. ***ON THE CHILD***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already.
9. You should now see a connection request pop-up from the parent. Hit **+ Add Device** to add the device.
10. Hit **Save** to finish adding the device.
11. That pop-up will close and you will get notification that the device wants to share a folder now. Hit **Add.**
12. Enter the path to the save folder in Eden and hit **Save.**
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered “*Parents*” for other devices you want to add. Repeat these steps for as many devices you want.

View File

@@ -0,0 +1,66 @@
# Backing Up/Syncing Eden Game Saves
Use this guide for when you want to configure automated backup/syncing of your Eden save files using [*Syncthing*](https://syncthing.net/).
**Click [Here](https://evilperson1337.notion.site/Backing-Up-Syncing-Eden-Game-Saves-2b357c2edaf68000b40cfab2c2c3dc0a) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Eden already installed, configured, and functioning.
- Devices to run Syncthing on.
- Ability to allow a program to communicate through the firewall of your device.
---
## Platform Specific Setup Guides
- [*Windows*](./SyncthingGuide_Windows.md)
- *MacOS (Coming Soon)*
- *Steam Deck (Coming Soon)*
- *Android (Coming Soon)*
- [*Linux*](./SyncthingGuide_Linux.md)
---
## A Few Notes Before You Proceed
<aside>
***WARNING***: You should manually back up your save files before proceeding with this guide. If you incorrectly perform the steps, you risk losing them!
</aside>
- While this is a de-centralized model without the concepts of a Server/Client, Parent/Child, etc. - For the purposes of these guides, we will borrow from this models terminology to avoid sync conflicts and potential data loss. After the initial setup, all the devices in the sync network are equals and can push & pull files from any other device.
- In order for this to work, you should get all of the save files in Eden in the save folder on the Parent.
- If you need help doing that, see the ***Importing Saves into Eden*** guide for the platform you elect to act as the Parent, and delete the save files on the "Child" devices.
### Terminology
- **Sync Network**: All the devices configured in *Syncthing* to push/pull files.
- **Parent**: This will be the device that you elect to push files to the other devices. There can only be one here initially in order to avoid sync conflicts.
- **Child**: All the other devices added to the Sync Network. These devices will pull files from the Parent.
---
## Overview
Rather than giving a breakdown of all the platforms and configurations, those will be in the platforms specific guides - this will serve as a general overview of Syncthing.
---
### What is Syncthing Anyway?
Syncthing is a continuous file synchronization program (in the laymans - make sure 2 or more systems with the same files are always up to date). This is perfect for game saves where we would want to play on 1 device, save our game, and then continue playing it on another device. This technology is what Epic/Steam/etc. use to allow you to do this on games run through their respective services. Syncthing is an open source implementation of this technology that you control, rather than relying on a 3rd party. This has a few key benefits, most notably - better security, privacy, and speed (when on your LAN).
---
### What are some common issues?
Syncthing is fairly robust and doesnt have many issues luckily, but there are some things you should watch out for (almost all of them a user issue).
- Sync conflicts
- If for whatever reason you update the same file on 2 different machines, the system does not know which updated file is considered the one to sync across. This results in a ***sync conflict*** where it may not sync the files as you would expect. Worst case scenario, this can result in your save progress being lost if you are not careful. When one of these occurs, it will create a copy of the file and store it with a specific name, like this example, *Paper Mario.sync-conflict-20251102-072925-TZBBN6S.srm.* To resolve this, you must remove the other files and remove the *.sync-conflict-<TIMESTAMP>-<Syncthing Device ID>* from the file name of the file you want to keep.
- Accidental Deletions
- If you delete a file from one of the devices, it will also remove the file on the other devices when they perform a sync so be careful when doing this.

View File

@@ -0,0 +1,96 @@
# Backing Up/Syncing Eden Game Saves
Use this guide for when you want to configure automated backup/syncing of your Eden save files using [*Syncthing*](https://syncthing.net/) on Linux.
**Click [Here](https://evilperson1337.notion.site/Backing-Up-Syncing-Eden-Game-Saves-2b057c2edaf680fc8a28eba5a05fd7a3) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Read the [*Syncthing General Guide*](./SyncthingGuide_General.md).
- Eden already installed, configured, and functioning.
---
## Steps
<aside>
***WARNING***: You should manually back up your save files before proceeding with this guide. If you incorrectly perform the steps, you risk losing them!
</aside>
<aside>
***NOTE***: I am using Linux Mint for my guides, but the steps should translate pretty easily to your distro. I ***hope*** that if you are running Linux you know the basic operations. Steam Deck users should follow the guide specific to that platform.
</aside>
### Downloading and Installing Syncthing
1. Download [*Syncthing Tray*](https://flathub.org/en/apps/io.github.martchus.syncthingtray) from the Flatpak store.
2. Launch *Syncthing Tray* to run it, select the **Start guided setup** on the splash screen that appears and press **Next**.
3. It will then look for an existing *Syncthing* instance to pull settings from, but will likely fail to do so if you are here. Regardless, select the **Yes, continue configuration** option.
4. Select ***Start installed Syncthing application via Syncthing Tray***, this means it will use a built in Syncthing executable rather than relying on an externally provided one. Press **Next** to continue.
5. You will now be presented with a confirmation window with your selections, confirm they are what you want and hit **Apply** to continue.
6. You will now be prompted with a confirmation window that has a QR code and the devices identifier - you will need one of these to add other devices to the sync system.
7. *Syncthing/Syncthing Tray* are now installed. Press Finish to close the pop up.
<aside>
***NOTE***: By default due to flatpak sandboxing limitations, Syncthing Tray will not run automatically on login. You can get around this by following the [*instructions here*](https://github.com/flathub/io.github.martchus.syncthingtray).
</aside>
---
### Configuring this Machine as a Parent
Use this when you want to set this machine as the initial source of truth (push files out to all the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
1. If you dont have a taskbar in your distro, you can also reach it directly by opening a web browser to: *http://127.0.0.1:8384/.*
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
3. Well start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
5. Enter the Full folder path to where your save files are stored on this machine.
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
6. Ignore the other tabs for now and hit **Save**.
7. The folder is now ready to be shared with other devices.
---
### Configuring this Machine as a Child
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Install Syncthing Tray on the client device following the section above. Copy the childs ID and store it so it is accessible to the Parent.
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
5. Add the ID and Name the device, the device may appear as a **nearby device**, in which case you can just click it to pre-populate the Device ID.
6. Click the **Sharing** Tab, and check the box next to the folder you set up on the Parent (Switch Saves in my case). Hit **Save.**
7. We are done with the parent, now **SWITCH OVER TO THE CHILD.**
8. ***ON THE CHILD***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already.
9. You should now see a connection request pop-up from the parent. Hit **+ Add Device** to add the device.
10. Hit **Save** to finish adding the device.
11. That pop-up will close and you will get notification that the device wants to share a folder now. Hit **Add.**
12. Enter the path to the save folder in Eden and hit **Save.**
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered “*Parents*” for other devices you want to add. Repeat these steps for as many devices you want.

View File

@@ -0,0 +1,95 @@
# Backing Up/Syncing Eden Game Saves
Use this guide for when you want to configure automated backup/syncing of your Eden save files using [*Syncthing](https://syncthing.net/)* on Windows.
**Click [Here](https://evilperson1337.notion.site/Backing-Up-Syncing-Eden-Game-Saves-2b057c2edaf680f5aa9cd1c4f97121ce) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Read the [*Syncthing General Guide*](./SyncthingGuide_General.md).
- Eden already installed, configured, and functioning.
- Ability to allow a program to communicate through the firewall in Windows.
- Ability to extract archive (.zip/.7z/.rar) files.
---
## Steps
<aside>
***WARNING***: You should manually back up your save files before proceeding with this guide. If you incorrectly perform the steps, you risk losing them!
</aside>
### Downloading and Installing *Syncthing*
1. Download [*Syncthing Tray*](https://martchus.github.io/syncthingtray/#downloads-section).
1. While it is available as a command line interface, for most people I would recommend *Syncthing Tray* on Windows. For most people here, you would download the **64-bit (Intel/AMD)** version.
2. Open the downloaded archive and extract the **syncthingtray.exe** to wherever you want to store the executable.
3. Double-Click the application to run it, select the **Start guided setup** on the splash screen that appears and press **Next**.
<aside>
***NOTE***: You may get a Windows Defender Smart Screen pop up, this is a known thing, just accept and run anyway.
</aside>
4. It will then look for an existing Syncthing instance to pull settings from, but will likely fail to do so if you are here. Regardless, select the **Yes, continue configuration** option.
5. Select ***Start Syncthing application that is built into Syncthing Tray***, this means it will use a built in Syncthing executable rather than relying on an externally provided one. Press **Next** to continue.
6. Check the box to start Syncthing Tray on login - as the name implies, this means the program will run automatically whenever you log onto the computer. Press Next to continue.
7. You will now be presented with a confirmation window with your selections, confirm they are what you want and hit **Apply** to continue.
8. You will now be prompted with a confirmation window and a message to allow it through the firewall. Allow the access through the firewall to close that pop up. The confirmation screen has a QR code and the devices identifier - you will need one of these to add other devices to the sync system.
9. *Syncthing/Syncthing Tray* are now installed.
---
### Configuring this Machine as a Parent
Use this when you want to set this machine as the initial source of truth (push files out to all the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing.**
2. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
3. Well start by adding the folder with our save files that we want to sync by Pressing **+ Add Folder**.
4. A pop-up window will appear, fill in the Folder label field with whatever you want to call it, like Switch Saves.
5. Enter the Full folder path to where your save files are stored on this machine.
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
6. Ignore the other tabs for now and hit **Save**.
7. The folder is now ready to be shared with other devices.
---
### Configuring this Machine as a Child
Use this when you want to set this machine up as a child (pull files from the other devices). Afterwards they will all be equal partners, not a parent/child relationship, this just helps with initial setup.
1. Install Syncthing Tray on the client device following the section above. Copy the childs ID and store it so it is accessible to the Parent.
2. ***ON THE PARENT***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
3. You will now have a browser window open up to a web GUI to configure *Syncthing*. You will get a pop up about allowing anonymous usage and setting a password, make your selections to close them.
4. Navigate down to **+ Add Remote Device**, we are going to add our Child device, so I hope you have its ID handy. If not, go back and get it.
5. Add the ID and Name the device, the device may appear as a **nearby device**, in which case you can just click it to pre-populate the Device ID.
6. Click the **Sharing** Tab, and check the box next to the folder you set up on the Parent (Switch Saves in my case). Hit **Save.**
7. We are done with the parent, now **SWITCH OVER TO THE CHILD.**
8. ***ON THE CHILD***: Right-Click the *Syncthing* Tray icon in your taskbar and select **Open Syncthing** if it is not open already**.**
9. You should now see a connection request from the parent. Hit **+ Add Device** to add the device.
10. Hit **Save** to finish adding the device.
11. That pop-up will close and you will get notification that the device wants to share a folder now. Hit **Add.**
12. Enter the path to the save folder in Eden and hit **Save.**
<aside>
***TIP***: The easiest way to do this would be to open Eden, right-click a game that has a save, hit ***Open Save Data Location,*** and then go up 1 directory. It should contain folders with the TitleID of your games.
It should look similar to this: ..*\nand\user\save\0000000000000000\EC573727F509799675F6E5112C581D7E*
</aside>
13. *Syncthing* will now pull all the files from the Parent and store them in your local save directory. At this point the files are in sync and alterations to one will affect the other and both can be considered “*Parents*” for other devices you want to add. Repeat these steps for as many devices you want.

View File

@@ -2,11 +2,6 @@
While this is mainly aimed for testers - normal users can benefit from these guidelines to make their life easier when trying to outline and/or report an issue.
## Getting logs
In order to get more information, you can find logs in the following location:
## How to Test a PR Against the Based Master When Issues Arise
When you're testing a pull request (PR) and encounter unexpected behavior, it's important to determine whether the issue was introduced by the PR or if it already exists in the base code. To do this, compare the behavior against the based master branch.

View File

@@ -6,4 +6,3 @@ While most of the links mentioned in this guide are relatively "safe"; we urge u
- [Nightly Eden builds](https://github.com/pflyly/eden-nightly)
- [NixOS Eden Flake](https://github.com/Grantimatter/eden-flake)
- [ES-DE Frontend Support](https://github.com/GlazedBelmont/es-de-android-custom-systems)

View File

@@ -1,14 +1,10 @@
# User Handbook - Using Amiibo
# Using Amiibo with Eden
Use this guide when you want to load Amiibo into your games for use with the Eden emulator.
**Click [Here](https://evilperson1337.notion.site/Using-Amiibo-with-Eden-2b057c2edaf681b1b28ec6be600c6d3e) for a version of this guide with images & visual elements.**
## Android
TBD
## Desktop
---
### Pre-Requisites
@@ -21,7 +17,9 @@ TBD
</aside>
### Steps
## Steps
---
1. Launch Eden and launch the game you want to load Amiibo for.
<aside>
@@ -42,4 +40,4 @@ TBD
</aside>
5. Upon loading a valid file, you will get a confirmation screen and your bonus content will be unlocked/functionality activated.
6. Repeat with any other Amiibo you want to use.
6. Repeat with any other Amiibo you want to use.

View File

@@ -1,10 +1,10 @@
# User Handbook - Using Cheats
# Using Cheats with Eden
Use this guide when you want to add cheats into a game to alter gameplay for use with the Eden emulator.
**Click [Here](https://evilperson1337.notion.site/Using-Cheats-with-Eden-2b057c2edaf6818fab66c276e2304bb4) for a version of this guide with images & visual elements.**
## Android
---
### Pre-Requisites
@@ -21,7 +21,11 @@ Another option would be to launch the game in Eden and close it - then go into t
</aside>
### Step 1: Configuring a Cheat
---
## Steps
### Configuring a Cheat
1. Create a directory somewhere accessible on your phone with the name of the cheat. The name you choose only affects how it is displayed in Eden.
2. Create a directory inside of this folder called **cheats.**
@@ -48,7 +52,9 @@ Another option would be to launch the game in Eden and close it - then go into t
2. You should now see the cheat appear in the **Add-ons** screen.
3. Launch the game and confirm that the cheat is applied.
### Step 2: Multiple Cheats
---
### Multiple Cheats
In order to install multiple cheats, you must repeat the steps above with the new cheat, creating a new directory with the name of the cheat and cheats directory. You **cannot** install multiple cheats with a single file.
@@ -73,7 +79,9 @@ Community Member [Ninjistix](https://github.com/Ninjistix) created a utility (Wi
040E0000 0048A818 52800028
```
### Step 3: Enabling/Disabling Cheats
---
### Enabling/Disabling Cheats
Cheats are enabled by default, but can be disabled so they dont affect gameplay fairly easily using the game properties.
@@ -81,76 +89,4 @@ Cheats are enabled by default, but can be disabled so they dont affect gamepl
2. Scroll down on the properties until you see **Add-ons**, select this option.
3. *Select/Deselect* the name of the cheat you wish to enable/disable.
4. Click **OK** to close the window.
5. Launch the game to confirm the cheat is/is not active.
## Desktop
### Pre-Requisites
- Eden Emulator fully set up and configured
- The cheat(s) you want to apply
- The **Build ID** of the game.
<aside>
***TIP***: The easiest way I have found to find the Build ID is by Right-Clicking the game **IN RYUJINX** and hitting **Manage Cheats**. Your Build ID will be displayed at the top.
Another option would be to launch the game in Eden and close it - then go into the log and look for a line like this - the first 16 characters if your Build ID. **Make sure that it is the MAIN line**.
`[ 27.098382] Loader <Info> core/file_sys/patch_manager.cpp:HasNSOPatch:304: Querying NSO patch existence for build_id=AEE6DCCC06D9C05B42061E2019123A61, name=main`
</aside>
### Step 1: Configuring a Cheat
1. Copy the Hex Code of the cheat into a text file, optionally with the cheat name at the beginning like the example. Here this code will set the timer to 999 in *New Super Mario Bros. U Deluxe.*
```bash
[Time = 999]
58000000 00C88A70
78001000 00000090
64000000 00000000 003E6F00
```
1. Save the file as a **txt** file with the Build ID of the game. For my example, my Build ID is **AEE6DCCC06D9C05B** so my file would be `AEE6DCCC06D9C05B.txt`.
2. Launch Eden and wait for the program to load.
3. *Right-Click* the game in Eden and select **Open Mod Data Location**. A file explorer window should appear.
4. Create a folder inside of the file explorer window with the name of the cheat. This name does not matter and only affects how it appears in the game properties inside of Eden.
5. Navigate inside of this folder and create another folder called **cheats.**
6. Move the txt file you created earlier into this **cheats** folder. (e.g. `<mod_location>/Time 999/cheats/AEE6DCCC06D9C05B.txt` )
7. Go back to Eden and *right-click* the game. Select *Configure Game* and you should now see the cheat you created appear in the **Add-Ons** section with the name of the folder from step 6.
8. Launch the game to verify that the cheat is enabled.
### Step 2: Multiple Cheats
In order to install multiple cheats, you must repeat the steps above with the new cheat, creating a new directory with the name of the cheat and cheats directory. You **cannot** install multiple cheats with a single file.
Community Member [Ninjistix](https://github.com/Ninjistix) created a utility (Windows or anything that can run Python) that can take a file with multiple cheats and create the files/structure for you with a provided Build ID. To download and run it, see the [GitHub Project](https://github.com/Ninjistix/nxCheat_Splitter) page.
**Example cheat TXT file with multiple cheats. It must be in this format to work:**
```
[Super Mario Bros. Wonder - Various] <- Optional
[♯ 1. Always Star Power]
040E0000 00880580 52800035
[♯ 2. Star Power + Bubble Mode (Invincible)]
040E0000 00880580 52800075
[♯ 3. Can Fast Travel to Any Course and World]
040E0000 00935E10 52800036
040E0000 0048A528 52800028
040E0000 005D9F58 52800028
[♯ 4. Got All Top of Flag Poles]
040E0000 0048A818 52800028
```
### Step 3: Enabling/Disabling Cheats
Cheats are enabled by default, but can be disabled so they dont affect gameplay fairly easily using the game properties.
1. *Right-Click* the game and select *Configure Game*.
2. In the **Add-Ons** section, locate the cheat you wish to enable.
3. *Select/Deselect* the name of the cheat you wish to enable/disable.
4. Click **OK** to close the window.
5. Launch the game to confirm the cheat is/is not active.
5. Launch the game to confirm the cheat is/is not active.

View File

@@ -0,0 +1,83 @@
# Using Cheats with Eden
Use this guide when you want to add cheats into a game to alter gameplay for use with the Eden emulator.
**Click [Here](https://evilperson1337.notion.site/Using-Cheats-with-Eden-2b057c2edaf6818fab66c276e2304bb4) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Eden Emulator fully set up and configured
- The cheat(s) you want to apply
- The **Build ID** of the game.
<aside>
***TIP***: The easiest way I have found to find the Build ID is by Right-Clicking the game **IN RYUJINX** and hitting **Manage Cheats**. Your Build ID will be displayed at the top.
Another option would be to launch the game in Eden and close it - then go into the log and look for a line like this - the first 16 characters if your Build ID. **Make sure that it is the MAIN line**.
`[ 27.098382] Loader <Info> core/file_sys/patch_manager.cpp:HasNSOPatch:304: Querying NSO patch existence for build_id=AEE6DCCC06D9C05B42061E2019123A61, name=main`
</aside>
## Steps
---
### Configuring a Cheat
1. Copy the Hex Code of the cheat into a text file, optionally with the cheat name at the beginning like the example. Here this code will set the timer to 999 in *New Super Mario Bros. U Deluxe.*
```bash
[Time = 999]
58000000 00C88A70
78001000 00000090
64000000 00000000 003E6F00
```
1. Save the file as a **txt** file with the Build ID of the game. For my example, my Build ID is **AEE6DCCC06D9C05B** so my file would be `AEE6DCCC06D9C05B.txt`.
2. Launch Eden and wait for the program to load.
3. *Right-Click* the game in Eden and select **Open Mod Data Location**. A file explorer window should appear.
4. Create a folder inside of the file explorer window with the name of the cheat. This name does not matter and only affects how it appears in the game properties inside of Eden.
5. Navigate inside of this folder and create another folder called **cheats.**
6. Move the txt file you created earlier into this **cheats** folder. (e.g. `<mod_location>/Time 999/cheats/AEE6DCCC06D9C05B.txt` )
7. Go back to Eden and *right-click* the game. Select *Configure Game* and you should now see the cheat you created appear in the **Add-Ons** section with the name of the folder from step 6.
8. Launch the game to verify that the cheat is enabled.
### Multiple Cheats
In order to install multiple cheats, you must repeat the steps above with the new cheat, creating a new directory with the name of the cheat and cheats directory. You **cannot** install multiple cheats with a single file.
Community Member [Ninjistix](https://github.com/Ninjistix) created a utility (Windows or anything that can run Python) that can take a file with multiple cheats and create the files/structure for you with a provided Build ID. To download and run it, see the [GitHub Project](https://github.com/Ninjistix/nxCheat_Splitter) page.
**Example cheat TXT file with multiple cheats. It must be in this format to work:**
```
[Super Mario Bros. Wonder - Various] <- Optional
[♯ 1. Always Star Power]
040E0000 00880580 52800035
[♯ 2. Star Power + Bubble Mode (Invincible)]
040E0000 00880580 52800075
[♯ 3. Can Fast Travel to Any Course and World]
040E0000 00935E10 52800036
040E0000 0048A528 52800028
040E0000 005D9F58 52800028
[♯ 4. Got All Top of Flag Poles]
040E0000 0048A818 52800028
```
---
### Enabling/Disabling Cheats
Cheats are enabled by default, but can be disabled so they dont affect gameplay fairly easily using the game properties.
1. *Right-Click* the game and select *Configure Game*.
2. In the **Add-Ons** section, locate the cheat you wish to enable.
3. *Select/Deselect* the name of the cheat you wish to enable/disable.
4. Click **OK** to close the window.
5. Launch the game to confirm the cheat is/is not active.

View File

@@ -235,6 +235,11 @@ endif()
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)
# PowerPC emitter
if (ARCHITECTURE_ppc64)
add_subdirectory(powah)
endif()
if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)

View File

@@ -1,8 +1,8 @@
{
"ffmpeg": {
"repo": "FFmpeg/FFmpeg",
"sha": "5e56937b74",
"hash": "9ab0457dcd6ce6359b5053c1662f57910d332f68ca0cca9d4134d858464840917027374de3d97e0863c3a7daaea2fe4f4cd17d1c6d8e7f740f4ad91e71c2932b",
"sha": "ddf443f1e9",
"hash": "ded1c313843f23805102565bd3ca92602fb9c2951e059ca5e1a486ab3ef7d589acccf3cde05c5ff0cfc5199c3a261dccb4d2a93254e585824850696fb41a292e",
"bundled": true
},
"ffmpeg-ci": {
@@ -10,7 +10,7 @@
"package": "FFmpeg",
"name": "ffmpeg",
"repo": "crueter-ci/FFmpeg",
"version": "8.0.1-5e56937b74",
"version": "8.0-ddf443f1e9",
"min_version": "4.1"
}
}

10
externals/powah/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Copyright 2025 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)

340
externals/powah/data2code.c vendored Normal file
View File

@@ -0,0 +1,340 @@
#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;
}

397
externals/powah/powah_emit.hpp vendored Normal file
View File

@@ -0,0 +1,397 @@
#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);
// 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;
};
}

484
externals/powah/powah_gen_base.hpp vendored Normal file
View File

@@ -0,0 +1,484 @@
// 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); }

340
externals/powah/tests.cpp vendored Normal file
View File

@@ -0,0 +1,340 @@
// SPDX-FileCopyrightText: Copyright 2025 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_ppc64)
/*
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

View File

@@ -1,5 +1,5 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-25.11";
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShellNoCC {

View File

@@ -7,7 +7,7 @@
include_directories(.)
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64))
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_ppc64))
set(DYNARMIC_IGNORE_ASSERTS ON)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)

View File

@@ -21,15 +21,6 @@
namespace AudioCore::Renderer {
namespace {
constexpr f32 BiquadParameterFixedScaleQ14 = 16384.0f; // 1 << 14
[[nodiscard]] inline s16 ToQ14Clamped(f32 v) {
const f32 scaled = std::clamp(v * BiquadParameterFixedScaleQ14, -32768.0f, 32767.0f);
return static_cast<s16>(scaled);
}
} // namespace
template <typename T, CommandId Id>
T& CommandBuffer::GenerateStart(const s32 node_id) {
if (size + sizeof(T) >= command_list.size_bytes()) {
@@ -268,42 +259,18 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
const bool use_float_processing) {
auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
const auto& parameter{
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
const auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(
effect_info.GetStateBuffer() + channel * sizeof(VoiceState::BiquadFilterState))};
if (behavior->IsEffectInfoVersion2Supported()) {
const auto& p{
*reinterpret_cast<BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter())};
cmd.input = buffer_offset + parameter.inputs[channel];
cmd.output = buffer_offset + parameter.outputs[channel];
if (!IsChannelCountValid(p.channel_count) || channel < 0 || channel >= p.channel_count) {
return;
}
cmd.biquad.b = parameter.b;
cmd.biquad.a = parameter.a;
cmd.input = buffer_offset + p.inputs[channel];
cmd.output = buffer_offset + p.outputs[channel];
// Convert float coefficients to Q2.14 fixed-point as expected by the legacy DSP path.
cmd.biquad.b[0] = ToQ14Clamped(p.b[0]);
cmd.biquad.b[1] = ToQ14Clamped(p.b[1]);
cmd.biquad.b[2] = ToQ14Clamped(p.b[2]);
cmd.biquad.a[0] = ToQ14Clamped(p.a[0]);
cmd.biquad.a[1] = ToQ14Clamped(p.a[1]);
} else {
const auto& p{
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
if (!IsChannelCountValid(p.channel_count) || channel < 0 || channel >= p.channel_count) {
return;
}
cmd.input = buffer_offset + p.inputs[channel];
cmd.output = buffer_offset + p.outputs[channel];
cmd.biquad.b = p.b;
cmd.biquad.a = p.a;
}
// Effects always use the fixed-point coefficient path on the DSP.
// Effects use legacy fixed-point format
cmd.use_float_coefficients = false;
cmd.state = memory_pool->Translate(CpuAddr(state),
@@ -628,15 +595,6 @@ void CommandBuffer::GenerateCopyMixBufferCommand(const s32 node_id, EffectInfoBa
const s16 buffer_offset, const s8 channel) {
auto& cmd{GenerateStart<CopyMixBufferCommand, CommandId::CopyMixBuffer>(node_id)};
if (behavior->IsEffectInfoVersion2Supported()) {
const auto& parameter_v2{
*reinterpret_cast<BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter())};
cmd.input_index = buffer_offset + parameter_v2.inputs[channel];
cmd.output_index = buffer_offset + parameter_v2.outputs[channel];
GenerateEnd<CopyMixBufferCommand>(cmd);
return;
}
const auto& parameter{
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
cmd.input_index = buffer_offset + parameter.inputs[channel];

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -364,37 +361,12 @@ void CommandGenerator::GenerateAuxCommand(const s16 buffer_offset, EffectInfoBas
void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset,
EffectInfoBase& effect_info,
const s32 node_id) {
EffectInfoBase::ParameterState param_state{};
s8 channel_count = 0;
if (render_context.behavior->IsEffectInfoVersion2Supported()) {
const auto* parameter =
reinterpret_cast<const BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter());
if (!parameter) {
LOG_ERROR(Service_Audio, "Biquad filter parameter is null");
return;
}
param_state = parameter->state;
channel_count = parameter->channel_count;
} else {
const auto* parameter =
reinterpret_cast<const BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter());
if (!parameter) {
LOG_ERROR(Service_Audio, "Biquad filter parameter is null");
return;
}
param_state = parameter->state;
channel_count = parameter->channel_count;
}
if (channel_count <= 0) {
return;
}
const auto& parameter{
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
if (effect_info.IsEnabled()) {
bool needs_init{false};
switch (param_state) {
switch (parameter.state) {
case EffectInfoBase::ParameterState::Initialized:
needs_init = true;
break;
@@ -403,26 +375,22 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset
if (render_context.behavior->IsBiquadFilterEffectStateClearBugFixed()) {
needs_init = false;
} else {
needs_init = param_state == EffectInfoBase::ParameterState::Updating;
needs_init = parameter.state == EffectInfoBase::ParameterState::Updating;
}
break;
default:
LOG_ERROR(Service_Audio,
"Invalid biquad parameter state {}, treating as uninitialized",
static_cast<u32>(param_state));
needs_init = true;
LOG_ERROR(Service_Audio, "Invalid biquad parameter state {}",
static_cast<u32>(parameter.state));
break;
}
const bool use_float_processing =
render_context.behavior->UseBiquadFilterFloatProcessing();
for (s8 channel = 0; channel < channel_count; channel++) {
command_buffer.GenerateBiquadFilterCommand(node_id, effect_info, buffer_offset, channel,
needs_init, use_float_processing);
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
command_buffer.GenerateBiquadFilterCommand(
node_id, effect_info, buffer_offset, channel, needs_init,
render_context.behavior->UseBiquadFilterFloatProcessing());
}
} else {
for (s8 channel = 0; channel < channel_count; channel++) {
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset,
channel);
}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -37,22 +34,14 @@ void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
}
void BiquadFilterInfo::UpdateForCommandGeneration() {
usage_state = enabled ? UsageState::Enabled : UsageState::Disabled;
auto* params_v1 = reinterpret_cast<ParameterVersion1*>(parameter.data());
auto* params_v2 = reinterpret_cast<ParameterVersion2*>(parameter.data());
const auto raw_state_v1 = static_cast<u8>(params_v1->state);
const auto raw_state_v2 = static_cast<u8>(params_v2->state);
if (raw_state_v1 <= static_cast<u8>(ParameterState::Updated)) {
params_v1->state = ParameterState::Updated;
} else if (raw_state_v2 <= static_cast<u8>(ParameterState::Updated)) {
params_v2->state = ParameterState::Updated;
if (enabled) {
usage_state = UsageState::Enabled;
} else {
params_v1->state = ParameterState::Updated;
params_v2->state = ParameterState::Updated;
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,12 +27,10 @@ public:
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u32 padding;
/* 0x10 */ std::array<f32, 3> b;
/* 0x1C */ std::array<f32, 2> a;
/* 0x24 */ s8 channel_count;
/* 0x25 */ ParameterState state;
/* 0x26 */ u16 reserved;
/* 0x0C */ std::array<s16, 3> b;
/* 0x12 */ std::array<s16, 2> a;
/* 0x16 */ s8 channel_count;
/* 0x17 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"BiquadFilterInfo::ParameterVersion2 has the wrong size!");

View File

@@ -1233,7 +1233,7 @@ if (HAS_NCE)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
if (TARGET dynarmic::dynarmic)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp

View File

@@ -59,14 +59,10 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
_mm_mfence();
_mm_lfence();
#elif defined(ARCHITECTURE_x86_64)
asm volatile("mfence\n\tlfence\n\t" : : : "memory");
#elif defined(_MSC_VER) && defined(ARCHITECTURE_arm64)
_Memory_barrier();
#elif defined(ARCHITECTURE_arm64)
asm volatile("dsb sy\n\t" : : : "memory");
#else
#error Unsupported architecture
__sync_synchronize();
#endif
return 0;
},
@@ -78,14 +74,10 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
_mm_mfence();
#elif defined(ARCHITECTURE_x86_64)
asm volatile("mfence\n\t" : : : "memory");
#elif defined(_MSC_VER) && defined(ARCHITECTURE_arm64)
_Memory_barrier();
#elif defined(ARCHITECTURE_arm64)
asm volatile("dmb sy\n\t" : : : "memory");
#else
#error Unsupported architecture
__sync_synchronize();
#endif
return 0;
},

View File

@@ -88,8 +88,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
{181, nullptr, "UpgradeLaunchRequiredVersion"},
{190, nullptr, "SendServerMaintenanceOverlayNotification"},
{200, nullptr, "GetLastApplicationExitReason"},
{210, D<&IApplicationFunctions::GetLaunchRequiredVersionUpgrade>, "GetLaunchRequiredVersionUpgrade"}, // [20.0.0+]
{211, nullptr, "GetLaunchRequiredVersionUpgradeStatus"}, // [20.0.0+]
{210, D<&IApplicationFunctions::GetUnknownEvent210>, "Unknown210"},
{220, nullptr, "Unknown220"}, // [20.0.0+]
{300, nullptr, "Unknown300"}, // [20.0.0+]
{310, nullptr, "Unknown310"}, // [20.0.0+]
@@ -497,10 +496,10 @@ Result IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(
R_SUCCEED();
}
Result IApplicationFunctions::GetLaunchRequiredVersionUpgrade(
Result IApplicationFunctions::GetUnknownEvent210(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_event = m_applet->state_changed_event.GetHandle();
LOG_DEBUG(Service_AM, "called");
*out_event = m_applet->unknown_event.GetHandle();
R_SUCCEED();
}

View File

@@ -79,7 +79,7 @@ private:
Result TryPopFromFriendInvitationStorageChannel(Out<SharedPointer<IStorage>> out_storage);
Result GetNotificationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetLaunchRequiredVersionUpgrade(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetUnknownEvent210(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result PrepareForJit();
const std::shared_ptr<Applet> m_applet;

View File

@@ -13,7 +13,6 @@
#include "core/hle/service/ns/content_management_interface.h"
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
#include "core/file_sys/patch_manager.h"
#include "frontend_common/firmware_manager.h"
namespace Service::NS {
@@ -430,13 +429,13 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
}
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
const auto size = (std::min)(out_application_views.size(), application_ids.size());
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
for (size_t i = 0; i < size; i++) {
ApplicationViewV19 view{};
ApplicationView view{};
view.application_id = application_ids[i];
view.version = 0x70000;
view.flags = 0x401f17;
@@ -448,53 +447,34 @@ Result IApplicationManagerInterface::GetApplicationViewDeprecated(
}
Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
Out<u32> out_count,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
const auto requested = application_ids.size();
LOG_WARNING(Service_NS, "called, size={}", requested);
const auto fw_pair = FirmwareManager::GetFirmwareVersion(system);
const bool is_fw20 = fw_pair.first.major >= 20;
const size_t per_entry_size = is_fw20 ? (sizeof(ApplicationViewV20) + sizeof(PromotionInfo))
: (sizeof(ApplicationViewV19) + sizeof(PromotionInfo));
const size_t capacity_entries = out_buffer.size() / per_entry_size;
const size_t to_write_entries = (std::min)(requested, capacity_entries);
u8* dst = out_buffer.data();
for (size_t i = 0; i < to_write_entries; ++i) {
ApplicationViewWithPromotionData data{};
data.view.application_id = application_ids[i];
data.view.version = 0x70000;
data.view.unk = 0;
data.view.flags = 0x401f17;
data.view.download_state = {};
data.view.download_progress = {};
data.promotion = {};
const size_t written = WriteApplicationViewWithPromotion(dst, out_buffer.size() - (dst - out_buffer.data()), data, is_fw20);
if (written == 0) {
break;
}
dst += written;
}
*out_count = static_cast<u32>(dst - out_buffer.data()) / static_cast<u32>(per_entry_size);
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationView(
OutArray<ApplicationViewV20, BufferAttr_HipcMapAlias> out_application_views,
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
const auto size = (std::min)(out_application_views.size(), application_ids.size());
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
for (size_t i = 0; i < size; i++) {
ApplicationViewV20 view{};
ApplicationViewWithPromotionInfo view{};
view.view.application_id = application_ids[i];
view.view.version = 0x70000;
view.view.flags = 0x401f17;
view.promotion = {};
out_application_views[i] = view;
}
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationView(
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
const auto size = (std::min)(out_application_views.size(), application_ids.size());
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
for (size_t i = 0; i < size; i++) {
ApplicationView view{};
view.application_id = application_ids[i];
view.version = 0x70000;
view.unk = 0;
view.flags = 0x401f17;
out_application_views[i] = view;

View File

@@ -36,14 +36,13 @@ public:
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
Result GetApplicationViewDeprecated(
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result GetApplicationViewWithPromotionInfo(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
Out<u32> out_count,
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result GetApplicationView(
OutArray<ApplicationViewV20, BufferAttr_HipcMapAlias> out_application_views,
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result GetApplicationRightsOnClient(
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,

View File

@@ -55,6 +55,16 @@ struct ApplicationDownloadState {
static_assert(sizeof(ApplicationDownloadState) == 0x20,
"ApplicationDownloadState has incorrect size.");
/// ApplicationView
struct ApplicationView {
u64 application_id; ///< ApplicationId.
u32 version; ///< Application Version(?)
u32 flags; ///< Flags.
ApplicationDownloadState download_state; ///< \ref ApplicationDownloadState
ApplicationDownloadState download_progress; ///< \ref ApplicationDownloadState
};
static_assert(sizeof(ApplicationView) == 0x50, "ApplicationView has incorrect size.");
struct ApplicationRightsOnClient {
u64 application_id;
Common::UUID uid;
@@ -78,74 +88,14 @@ struct PromotionInfo {
};
static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size.");
struct ApplicationViewV19 {
u64 application_id;
u32 version;
u32 flags;
ApplicationDownloadState download_state;
ApplicationDownloadState download_progress;
// TODO(Maufeat): NsApplicationViewWithPromotionInfo is on SDK20+ 0x78 bytes
/// NsApplicationViewWithPromotionInfo
struct ApplicationViewWithPromotionInfo {
ApplicationView view; ///< \ref NsApplicationView
PromotionInfo promotion; ///< \ref NsPromotionInfo
};
static_assert(sizeof(ApplicationViewV19) == 0x50, "ApplicationViewV19 has incorrect size.");
struct ApplicationViewV20 {
u64 application_id;
u32 version;
u32 flags;
u32 unk;
ApplicationDownloadState download_state;
ApplicationDownloadState download_progress;
};
static_assert(sizeof(ApplicationViewV20) == 0x58, "ApplicationViewV20 has incorrect size.");
struct ApplicationViewData {
u64 application_id{};
u32 version{};
u32 flags{};
u32 unk{};
ApplicationDownloadState download_state{};
ApplicationDownloadState download_progress{};
};
inline size_t WriteApplicationView(void* dst, size_t dst_size, const ApplicationViewData& data,
bool is_fw20) {
if (is_fw20) {
if (dst_size < sizeof(ApplicationViewV20)) return 0;
auto* out = reinterpret_cast<ApplicationViewV20*>(dst);
out->application_id = data.application_id;
out->version = data.version;
out->flags = data.flags;
out->unk = data.unk;
out->download_state = data.download_state;
out->download_progress = data.download_progress;
return sizeof(ApplicationViewV20);
} else {
if (dst_size < sizeof(ApplicationViewV19)) return 0;
auto* out = reinterpret_cast<ApplicationViewV19*>(dst);
out->application_id = data.application_id;
out->version = data.version;
out->flags = data.flags;
out->download_state = data.download_state;
out->download_progress = data.download_progress;
return sizeof(ApplicationViewV19);
}
}
struct ApplicationViewWithPromotionData {
ApplicationViewData view;
PromotionInfo promotion;
};
inline size_t WriteApplicationViewWithPromotion(void* dst, size_t dst_size,
const ApplicationViewWithPromotionData& data,
bool sdk20_plus) {
const size_t view_written = WriteApplicationView(dst, dst_size, data.view, sdk20_plus);
if (view_written == 0) return 0;
const size_t remaining = dst_size - view_written;
if (remaining < sizeof(PromotionInfo)) return 0;
auto* promo_dst = reinterpret_cast<u8*>(dst) + view_written;
std::memcpy(promo_dst, &data.promotion, sizeof(PromotionInfo));
return view_written + sizeof(PromotionInfo);
}
static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70,
"ApplicationViewWithPromotionInfo has incorrect size.");
struct ApplicationOccupiedSizeEntity {
FileSys::StorageId storage_id;

View File

@@ -4,12 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <vector>
#include <stb_image.h>
#include <stb_image_resize.h>
#include <stb_image_write.h>
#include "common/settings.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -23,49 +17,6 @@
namespace Service::NS {
namespace {
void JPGToMemory(void* context, void* data, int size) {
auto* buffer = static_cast<std::vector<u8>*>(context);
const auto* char_data = static_cast<const u8*>(data);
buffer->insert(buffer->end(), char_data, char_data + size);
}
void SanitizeJPEGImageSize(std::vector<u8>& image) {
constexpr std::size_t max_jpeg_image_size = 0x20000;
constexpr int profile_dimensions = 174; // for grid view thingy
int original_width, original_height, color_channels;
auto* plain_image =
stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width,
&original_height, &color_channels, STBI_rgb);
if (plain_image == nullptr) {
LOG_ERROR(Service_NS, "Failed to load JPEG for sanitization.");
return;
}
if (original_width != profile_dimensions || original_height != profile_dimensions) {
std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb);
stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(),
profile_dimensions, profile_dimensions, 0, STBI_rgb, 0,
STBIR_FILTER_BOX);
image.clear();
if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions,
STBI_rgb, out_image.data(), 90)) {
LOG_ERROR(Service_NS, "Failed to resize the user provided image.");
}
}
stbi_image_free(plain_image);
if (image.size() > max_jpeg_image_size) {
image.resize(max_jpeg_image_size);
}
}
} // namespace
IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
Core::System& system_)
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
@@ -76,8 +27,8 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
{4, nullptr, "SelectApplicationDesiredLanguage"},
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlDataWithoutIcon"},
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlDataWithoutIcon3"},
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"},
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"},
};
// clang-format on
@@ -174,77 +125,11 @@ Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLan
R_SUCCEED();
}
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u64> out_total_size,
ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
application_control_source, flag1, flag2, application_id);
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto size = out_buffer.size();
const auto nacp_size = sizeof(FileSys::RawNACP);
if (size < nacp_size) {
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})",
size, nacp_size);
R_THROW(ResultUnknown);
}
if (control.first != nullptr) {
const auto bytes = control.first->GetRawBytes();
const auto copy_len = (std::min)(static_cast<size_t>(bytes.size()), static_cast<size_t>(nacp_size));
std::memcpy(out_buffer.data(), bytes.data(), copy_len);
if (copy_len < nacp_size) {
std::memset(out_buffer.data() + copy_len, 0, nacp_size - copy_len);
}
} else {
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}", application_id);
std::memset(out_buffer.data(), 0, nacp_size);
}
const auto icon_area_size = size - nacp_size;
std::vector<u8> final_icon_data;
if (control.second != nullptr) {
size_t full_size = control.second->GetSize();
if (full_size > 0) {
final_icon_data.resize(full_size);
control.second->Read(final_icon_data.data(), full_size);
if (flag1 == 1) {
SanitizeJPEGImageSize(final_icon_data);
}
}
}
size_t available_icon_bytes = final_icon_data.size();
if (icon_area_size > 0) {
const size_t to_copy = (std::min)(available_icon_bytes, icon_area_size);
if (to_copy > 0) {
std::memcpy(out_buffer.data() + nacp_size, final_icon_data.data(), to_copy);
}
if (to_copy < icon_area_size) {
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
}
}
const u32 total_available = static_cast<u32>(nacp_size + available_icon_bytes);
*out_total_size = (static_cast<u64>(total_available) << 32) | static_cast<u64>(flag1);
R_SUCCEED();
}
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b,
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
application_control_source, flag1, flag2, application_id);
ApplicationControlSource application_control_source, u64 application_id) {
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
application_control_source, application_id);
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
system.GetContentProvider()};
@@ -273,43 +158,22 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
}
const auto icon_area_size = size - nacp_size;
std::vector<u8> final_icon_data;
if (control.second != nullptr) {
size_t full_size = control.second->GetSize();
if (full_size > 0) {
final_icon_data.resize(full_size);
control.second->Read(final_icon_data.data(), full_size);
if (flag1 == 1) {
SanitizeJPEGImageSize(final_icon_data);
}
}
}
size_t available_icon_bytes = final_icon_data.size();
if (icon_area_size > 0) {
const size_t to_copy = (std::min)(available_icon_bytes, icon_area_size);
if (to_copy > 0) {
std::memcpy(out_buffer.data() + nacp_size, final_icon_data.data(), to_copy);
if (control.second != nullptr) {
const auto icon_size = control.second->GetSize();
const auto to_copy = static_cast<size_t>((std::min)(icon_size, icon_area_size));
control.second->Read(out_buffer.data() + nacp_size, to_copy);
if (to_copy < icon_area_size) {
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
}
} else {
std::memset(out_buffer.data() + nacp_size, 0, icon_area_size);
LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}, zero-filling icon area",
application_id);
}
if (to_copy < icon_area_size) {
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
}
} else {
std::memset(out_buffer.data() + nacp_size, 0, icon_area_size);
}
const u32 actual_total_size = static_cast<u32>(nacp_size + available_icon_bytes);
// Out 1: always 0x10001 (likely presents flags: Bit0=Icon, Bit16=NACP)
// Out 2: reflects flag1 application (0 if flag1=0, 0x10001 if flag1=1)
// Out 3: The actual size of data
*out_flags_a = 0x10001;
*out_flags_b = (flag1 == 1) ? 0x10001 : 0;
*out_actual_size = actual_total_size;
*out_total_size = static_cast<u64>(size);
R_SUCCEED();
}

View File

@@ -27,21 +27,10 @@ public:
u32 supported_languages);
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
ApplicationLanguage application_language);
Result GetApplicationControlData2(
Result GetApplicationControlDataWithoutIcon(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
Out<u64> out_total_size,
ApplicationControlSource application_control_source,
u8 flag1,
u8 flag2,
u64 application_id);
Result GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
Out<u32> out_flags_a,
Out<u32> out_flags_b,
Out<u32> out_actual_size,
ApplicationControlSource application_control_source,
u8 flag1,
u8 flag2,
u64 application_id);
};

View File

@@ -312,7 +312,7 @@ void BSD::Listen(HLERequestContext& ctx) {
void BSD::Fcntl(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const u32 cmd = rp.Pop<u32>();
const s32 cmd = rp.Pop<s32>();
const s32 arg = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
@@ -627,41 +627,23 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
LOG_ERROR(Service, "Bind failed: Invalid fd={}", fd);
return Errno::BADF;
}
ASSERT(addr.size() == sizeof(SockAddrIn));
auto addr_in = GetValue<SockAddrIn>(addr);
LOG_INFO(Service, "Bind fd={} to {}:{}", fd, Network::IPv4AddressToString(addr_in.ip),
addr_in.portno);
const auto result = Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
if (result != Errno::SUCCESS) {
LOG_ERROR(Service, "Bind fd={} failed with errno={}", fd, static_cast<int>(result));
}
return result;
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
}
Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
LOG_ERROR(Service, "Connect failed: Invalid fd={}", fd);
return Errno::BADF;
}
UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
auto addr_in = GetValue<SockAddrIn>(addr);
LOG_INFO(Service, "Connect fd={} to {}:{}", fd, Network::IPv4AddressToString(addr_in.ip),
addr_in.portno);
const auto result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
if (result != Errno::SUCCESS) {
LOG_ERROR(Service, "Connect fd={} failed with errno={}", fd, static_cast<int>(result));
} else {
LOG_INFO(Service, "Connect fd={} succeeded", fd);
}
return result;
return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
}
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
@@ -737,8 +719,8 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
}
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
LOG_WARNING(Service, "(STUBBED) Unknown getsockopt level={}, returning INVAL", level);
return Errno::INVAL;
UNIMPLEMENTED_MSG("Unknown getsockopt level");
return Errno::SUCCESS;
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
@@ -768,52 +750,32 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8
}
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
LOG_WARNING(Service, "(STUBBED) Unknown setsockopt level={}, returning INVAL", level);
return Errno::INVAL;
UNIMPLEMENTED_MSG("Unknown setsockopt level");
return Errno::SUCCESS;
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) {
if (optval.size() != sizeof(Linger)) {
LOG_WARNING(Service, "LINGER optval size mismatch: expected {}, got {}", sizeof(Linger),
optval.size());
return Errno::INVAL;
}
ASSERT(optval.size() == sizeof(Linger));
auto linger = GetValue<Linger>(optval);
if (linger.onoff != 0 && linger.onoff != 1) {
LOG_WARNING(Service, "Invalid LINGER onoff value: {}", linger.onoff);
return Errno::INVAL;
}
ASSERT(linger.onoff == 0 || linger.onoff == 1);
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
}
if (optval.size() != sizeof(u32)) {
LOG_WARNING(Service, "optval size mismatch: expected {}, got {} for optname={}", sizeof(u32),
optval.size(), static_cast<u32>(optname));
return Errno::INVAL;
}
ASSERT(optval.size() == sizeof(u32));
auto value = GetValue<u32>(optval);
switch (optname) {
case OptName::REUSEADDR:
if (value != 0 && value != 1) {
LOG_WARNING(Service, "Invalid REUSEADDR value: {}", value);
return Errno::INVAL;
}
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
case OptName::KEEPALIVE:
if (value != 0 && value != 1) {
LOG_WARNING(Service, "Invalid KEEPALIVE value: {}", value);
return Errno::INVAL;
}
ASSERT(value == 0 || value == 1);
return Translate(socket->SetKeepAlive(value != 0));
case OptName::BROADCAST:
if (value != 0 && value != 1) {
LOG_WARNING(Service, "Invalid BROADCAST value: {}", value);
return Errno::INVAL;
}
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
case OptName::SNDBUF:
return Translate(socket->SetSndBuf(value));
@@ -827,9 +789,8 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
return Errno::SUCCESS;
default:
LOG_WARNING(Service, "(STUBBED) Unimplemented optname={} (0x{:x}), returning INVAL",
static_cast<u32>(optname), static_cast<u32>(optname));
return Errno::INVAL;
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Errno::SUCCESS;
}
}

View File

@@ -35,6 +35,7 @@ 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.

View File

@@ -0,0 +1,8 @@
# 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.

View File

@@ -73,6 +73,7 @@ add_library(dynarmic STATIC
frontend/imm.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
@@ -142,10 +143,8 @@ 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
@@ -291,7 +290,35 @@ if ("riscv" IN_LIST ARCHITECTURE)
backend/riscv64/a32_interface.cpp
backend/riscv64/code_block.h
)
message(FATAL_ERROR "TODO: Unimplemented 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 (WIN32)

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD

View File

@@ -18,7 +18,7 @@
#if defined(ARCHITECTURE_x86_64)
namespace Dynarmic::Backend::X64 {
class BlockOfCode;
} // namespace Dynarmic::Backend::X64
}
#elif defined(ARCHITECTURE_arm64)
namespace oaknut {
class CodeBlock;
@@ -26,7 +26,11 @@ class CodeBlock;
#elif defined(ARCHITECTURE_riscv64)
namespace Dynarmic::Backend::RV64 {
class CodeBlock;
} // namespace Dynarmic::Backend::RV64
}
#elif defined(ARCHITECTURE_ppc64)
namespace Dynarmic::Backend::PPC64 {
class CodeBlock;
}
#else
# error "Invalid architecture"
#endif
@@ -45,6 +49,9 @@ struct FakeCall {
#elif defined(ARCHITECTURE_riscv64)
struct FakeCall {
};
#elif defined(ARCHITECTURE_ppc64)
struct FakeCall {
};
#else
# error "Invalid architecture"
#endif
@@ -60,6 +67,8 @@ public:
void Register(oaknut::CodeBlock& mem, std::size_t mem_size);
#elif defined(ARCHITECTURE_riscv64)
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
#elif defined(ARCHITECTURE_ppc64)
void Register(PPC64::CodeBlock& mem, std::size_t mem_size);
#else
# error "Invalid architecture"
#endif

View File

@@ -17,20 +17,19 @@ ExceptionHandler::ExceptionHandler() = default;
ExceptionHandler::~ExceptionHandler() = default;
#if defined(ARCHITECTURE_x86_64)
void ExceptionHandler::Register(X64::BlockOfCode&) {
// Do nothing
}
void ExceptionHandler::Register(X64::BlockOfCode&)
#elif defined(ARCHITECTURE_arm64)
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
// Do nothing
}
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t)
#elif defined(ARCHITECTURE_riscv64)
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
// Do nothing
}
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t)
#elif defined(ARCHITECTURE_ppc64)
void ExceptionHandler::Register(PPC64::CodeBlock&, std::size_t)
#else
# error "Invalid architecture"
#endif
{
// Do nothing
}
bool ExceptionHandler::SupportsFastmem() const noexcept {
return false;

View File

@@ -7,6 +7,7 @@
*/
#include <cstring>
#include <algorithm>
#include <functional>
#include <memory>
#include <mutex>
@@ -26,6 +27,8 @@
# include "dynarmic/backend/arm64/abi.h"
#elif defined(ARCHITECTURE_riscv64)
# include "dynarmic/backend/riscv64/code_block.h"
#elif defined(ARCHITECTURE_ppc64)
# include "dynarmic/backend/ppc64/code_block.h"
#else
# error "Invalid architecture"
#endif
@@ -139,10 +142,8 @@ 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);
#elif defined(ARCHITECTURE_riscv64)
UNREACHABLE();
#else
# error "Invalid architecture"
UNREACHABLE();
#endif
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
@@ -200,6 +201,10 @@ void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
}
#elif defined(ARCHITECTURE_ppc64)
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

View File

@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2025 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;
void* run_fn = nullptr;
IR::LocationDescriptor GetLocationDescriptor() const {
return IR::LocationDescriptor{regs[15] | (u64(upper_location_descriptor) << 32)};
}
};
} // namespace Dynarmic::Backend::RV64

View File

@@ -0,0 +1,248 @@
// SPDX-FileCopyrightText: Copyright 2025 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/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 ir_block = A32::Translate(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();
}
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*);
thread_ctx.run_fn = (void*)&A32Core::Run;
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

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2025 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;
void* run_fn = nullptr;
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

View File

@@ -0,0 +1,359 @@
// SPDX-FileCopyrightText: Copyright 2025 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/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 ir_block = A64::Translate(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();
}
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;
};
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 CodeFn = HaltReason (*)(A64AddressSpace*, A64JitState*, volatile u32*, void*);
thread_ctx.run_fn = (void*)&A64Core::Run;
return (CodeFn(entry))(&process, &thread_ctx, halt_reason, reinterpret_cast<void*>(&A64Core::Run));
}
};
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

View File

@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2025 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 RNZCV = powah::R31;
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{
//6, 7, 8, 9, 10, 11, 12, //volatile
// 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

View File

@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,280 @@
// SPDX-FileCopyrightText: Copyright 2025 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&, EmitContext&, IR::Term::Interpret, IR::LocationDescriptor, bool) {
ASSERT(false && "unimp");
}
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::RJIT, offsetof(A64JitState, run_fn));
code.MTCTR(tmp);
code.BCTRL();
code.LD(powah::R2, powah::R1, offsetof(StackLayout, sp));
} 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) {
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.LD(tmp, powah::R1, offsetof(StackLayout, check_bit));
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};
auto const start_offset = code.offset;
ebi.entry_point = &code.base[start_offset];
code.MFLR(powah::R0);
code.STD(powah::R0, powah::R1, offsetof(StackLayout, lr));
// Non-volatile saves
std::vector<powah::GPR> abi_callee_saved{ABI_CALLEE_SAVED};
for (size_t i = 0; i < abi_callee_saved.size(); ++i)
code.STD(abi_callee_saved[i], powah::R1, -(8 + i * 8));
code.STDU(powah::R1, powah::R1, -sizeof(StackLayout));
code.STD(powah::R2, powah::R1, offsetof(StackLayout, sp));
for (auto iter = block.begin(); iter != block.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();
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
code.ADDI(powah::R1, powah::R1, sizeof(StackLayout));
code.LD(powah::R0, powah::R1, offsetof(StackLayout, lr));
code.MTLR(powah::R0);
for (size_t i = 0; i < abi_callee_saved.size(); ++i)
code.LD(abi_callee_saved[i], powah::R1, -(8 + i * 8));
code.BLR();
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

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,318 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,347 @@
// SPDX-FileCopyrightText: Copyright 2025 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.STD(value, powah::R1, offsetof(StackLayout, check_bit));
}
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

View File

@@ -0,0 +1,933 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,458 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,380 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,213 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,110 @@
// SPDX-FileCopyrightText: Copyright 2025 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 {
inline RegLock(RegAlloc& reg_alloc, T const value) noexcept
: reg_alloc{reg_alloc}
, value{value}
{
SetLock(true);
}
inline ~RegLock() noexcept { SetLock(false); }
operator T const&() { return value; }
operator T() const { 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;
uint32_t lru_counter = 0;
};
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

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2025 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 {
u64 resv0; //0
u64 resv1; //8
u64 lr; //16
u64 sp; //24
std::array<u64, SpillCount> spill;
u64 check_bit;
};
static_assert(sizeof(StackLayout) % 16 == 0);
} // namespace Dynarmic::Backend::RV64

View File

@@ -51,6 +51,16 @@
[[maybe_unused]] auto& mctx = ucontext->uc_mcontext; \
[[maybe_unused]] const auto fpctx = GetFloatingPointState(mctx);
# endif
#elif defined(ARCHITECTURE_ppc64)
# ifdef __OpenBSD__
# define CTX_DECLARE(raw_context) ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context);
# else
# define CTX_DECLARE(raw_context) \
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context); \
[[maybe_unused]] auto& mctx = ucontext->uc_mcontext;
# endif
#else
# error "Invalid architecture"
#endif
#if defined(ARCHITECTURE_x86_64)
@@ -118,8 +128,6 @@
# else
# error "Unknown platform"
# endif
#else
# error "unimplemented"
#endif
#ifdef ARCHITECTURE_arm64

View File

@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 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

View File

@@ -66,9 +66,7 @@ void Fermi2D::Blit() {
UNIMPLEMENTED_IF_MSG(regs.operation != Operation::SrcCopy, "Operation is not copy");
UNIMPLEMENTED_IF_MSG(regs.src.layer != 0, "Source layer is not zero");
UNIMPLEMENTED_IF_MSG(regs.dst.layer != 0, "Destination layer is not zero");
if (regs.src.depth != 1) {
LOG_DEBUG(HW_GPU, "Source depth is {}, expected 1 - using first layer", regs.src.depth);
}
UNIMPLEMENTED_IF_MSG(regs.src.depth != 1, "Source depth is not one");
UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
const auto& args = regs.pixels_from_memory;

View File

@@ -17,8 +17,6 @@ extern "C" {
// for querying VAAPI driver information
#include <libavutil/hwcontext_vaapi.h>
#endif
#include <libavutil/hwcontext.h>
}
namespace FFmpeg {
@@ -29,287 +27,282 @@ constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
constexpr std::array PreferredGpuDecoders = {
#if defined (_WIN32)
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_D3D12VA,
AV_HWDEVICE_TYPE_DXVA2,
#elif defined(__FreeBSD__)
AV_HWDEVICE_TYPE_VDPAU,
#elif defined(__APPLE__)
AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
#elif defined(ANDROID)
AV_HWDEVICE_TYPE_MEDIACODEC,
AV_HWDEVICE_TYPE_VDPAU,
#elif defined(__unix__)
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_VDPAU,
#endif
AV_HWDEVICE_TYPE_VULKAN,
};
AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
const auto desc = av_pix_fmt_desc_get(codec_context->pix_fmt);
if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i);
if (!config) {
break;
}
const auto desc = av_pix_fmt_desc_get(codec_context->pix_fmt);
if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(codec_context->codec, i);
if (!config) {
break;
}
for (const auto type : PreferredGpuDecoders) {
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
codec_context->pix_fmt = config->pix_fmt;
}
}
}
}
for (const auto type : PreferredGpuDecoders) {
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
codec_context->pix_fmt = config->pix_fmt;
}
}
}
}
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
if (*p == codec_context->pix_fmt) {
return codec_context->pix_fmt;
}
}
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
if (*p == codec_context->pix_fmt) {
return codec_context->pix_fmt;
}
}
LOG_INFO(HW_GPU, "Could not find supported GPU pixel format, falling back to CPU decoder");
av_buffer_unref(&codec_context->hw_device_ctx);
codec_context->pix_fmt = PreferredCpuFormat;
return codec_context->pix_fmt;
LOG_INFO(HW_GPU, "Could not find supported GPU pixel format, falling back to CPU decoder");
av_buffer_unref(&codec_context->hw_device_ctx);
codec_context->pix_fmt = PreferredCpuFormat;
return codec_context->pix_fmt;
}
std::string AVError(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum);
return errbuf;
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum);
return errbuf;
}
}
Packet::Packet(std::span<const u8> data) {
m_packet = av_packet_alloc();
m_packet->data = const_cast<u8*>(data.data());
m_packet->size = static_cast<s32>(data.size());
m_packet = av_packet_alloc();
m_packet->data = const_cast<u8*>(data.data());
m_packet->size = static_cast<s32>(data.size());
}
Packet::~Packet() {
av_packet_free(&m_packet);
av_packet_free(&m_packet);
}
Frame::Frame() {
m_frame = av_frame_alloc();
m_frame = av_frame_alloc();
}
Frame::~Frame() {
av_frame_free(&m_frame);
av_frame_free(&m_frame);
}
Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
const AVCodecID av_codec = [&] {
switch (codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return AV_CODEC_ID_H264;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return AV_CODEC_ID_VP8;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
return AV_CODEC_ID_VP9;
default:
UNIMPLEMENTED_MSG("Unknown codec {}", codec);
return AV_CODEC_ID_NONE;
}
}();
const AVCodecID av_codec = [&] {
switch (codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return AV_CODEC_ID_H264;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return AV_CODEC_ID_VP8;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
return AV_CODEC_ID_VP9;
default:
UNIMPLEMENTED_MSG("Unknown codec {}", codec);
return AV_CODEC_ID_NONE;
}
}();
m_codec = avcodec_find_decoder(av_codec);
m_codec = avcodec_find_decoder(av_codec);
}
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
if (!config) {
LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type));
break;
}
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
if (!config) {
LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name, av_hwdevice_get_type_name(type));
break;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
*out_pix_fmt = config->pix_fmt;
return true;
}
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) {
LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
*out_pix_fmt = config->pix_fmt;
return true;
}
}
return false;
return false;
}
std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
std::vector<AVHWDeviceType> types;
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
std::vector<AVHWDeviceType> types;
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
while (true) {
current_device_type = av_hwdevice_iterate_types(current_device_type);
if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
return types;
}
while (true) {
current_device_type = av_hwdevice_iterate_types(current_device_type);
if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
return types;
}
types.push_back(current_device_type);
}
types.push_back(current_device_type);
}
}
HardwareContext::~HardwareContext() {
av_buffer_unref(&m_gpu_decoder);
av_buffer_unref(&m_gpu_decoder);
}
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder) {
const auto supported_types = GetSupportedDeviceTypes();
for (const auto type : PreferredGpuDecoders) {
AVPixelFormat hw_pix_fmt;
const auto supported_types = GetSupportedDeviceTypes();
for (const auto type : PreferredGpuDecoders) {
AVPixelFormat hw_pix_fmt;
if (std::ranges::find(supported_types, type) == supported_types.end()) {
LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
continue;
}
if (std::ranges::find(supported_types, type) == supported_types.end()) {
LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
continue;
}
if (!this->InitializeWithType(type)) {
continue;
}
if (!this->InitializeWithType(type)) {
continue;
}
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
return true;
}
}
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
return true;
}
}
return false;
return false;
}
bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
av_buffer_unref(&m_gpu_decoder);
av_buffer_unref(&m_gpu_decoder);
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) {
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
return false;
}
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0); ret < 0) {
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type), AVError(ret));
return false;
}
#ifdef LIBVA_FOUND
if (type == AV_HWDEVICE_TYPE_VAAPI) {
// We need to determine if this is an impersonated VAAPI driver.
auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data);
auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
const char* vendor_name = vaQueryVendorString(vactx->display);
if (strstr(vendor_name, "VDPAU backend")) {
// VDPAU impersonated VAAPI impls are super buggy, we need to skip them.
LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver");
return false;
} else {
// According to some user testing, certain VAAPI drivers (Intel?) could be buggy.
// Log the driver name just in case.
LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name);
}
}
if (type == AV_HWDEVICE_TYPE_VAAPI) {
// We need to determine if this is an impersonated VAAPI driver.
auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data);
auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
const char* vendor_name = vaQueryVendorString(vactx->display);
if (strstr(vendor_name, "VDPAU backend")) {
// VDPAU impersonated VAAPI impls are super buggy, we need to skip them.
LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver");
return false;
} else {
// According to some user testing, certain VAAPI drivers (Intel?) could be buggy.
// Log the driver name just in case.
LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name);
}
}
#endif
return true;
return true;
}
DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec());
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
m_codec_context = avcodec_alloc_context3(m_decoder.GetCodec());
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
}
DecoderContext::~DecoderContext() {
av_buffer_unref(&m_codec_context->hw_device_ctx);
avcodec_free_context(&m_codec_context);
av_buffer_unref(&m_codec_context->hw_device_ctx);
avcodec_free_context(&m_codec_context);
}
void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt) {
m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
m_codec_context->get_format = GetGpuFormat;
m_codec_context->pix_fmt = hw_pix_fmt;
m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
m_codec_context->get_format = GetGpuFormat;
m_codec_context->pix_fmt = hw_pix_fmt;
}
bool DecoderContext::OpenContext(const Decoder& decoder) {
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
return false;
}
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
return false;
}
if (!m_codec_context->hw_device_ctx) {
LOG_INFO(HW_GPU, "Using FFmpeg CPU decoder");
}
if (!m_codec_context->hw_device_ctx) {
LOG_INFO(HW_GPU, "Using FFmpeg CPU decoder");
}
return true;
return true;
}
bool DecoderContext::SendPacket(const Packet& packet) {
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
return false;
}
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
return false;
}
return true;
return true;
}
std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
auto ReceiveImpl = [&](AVFrame* frame) -> int {
auto ReceiveImpl = [&](AVFrame* frame) -> int {
const int ret = avcodec_receive_frame(m_codec_context, frame);
if (ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
}
return ret;
};
if (ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
}
return ret;
};
std::shared_ptr<Frame> intermediate_frame = std::make_shared<Frame>();
if (ReceiveImpl(intermediate_frame->GetFrame()) < 0) {
return {};
}
std::shared_ptr<Frame> intermediate_frame = std::make_shared<Frame>();
if (ReceiveImpl(intermediate_frame->GetFrame()) < 0) {
return {};
}
m_final_frame = std::make_shared<Frame>();
if (m_codec_context->hw_device_ctx) {
m_final_frame->SetFormat(PreferredGpuFormat);
if (const int ret = av_hwframe_transfer_data(m_final_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
return {};
}
} else {
m_final_frame = std::move(intermediate_frame);
}
m_final_frame = std::make_shared<Frame>();
if (m_codec_context->hw_device_ctx) {
m_final_frame->SetFormat(PreferredGpuFormat);
if (const int ret = av_hwframe_transfer_data(m_final_frame->GetFrame(), intermediate_frame->GetFrame(), 0); ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
return {};
}
} else {
m_final_frame = std::move(intermediate_frame);
}
return std::move(m_final_frame);
return std::move(m_final_frame);
}
void DecodeApi::Reset() {
m_hardware_context.reset();
m_decoder_context.reset();
m_decoder.reset();
m_hardware_context.reset();
m_decoder_context.reset();
m_decoder.reset();
}
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
this->Reset();
m_decoder.emplace(codec);
m_decoder_context.emplace(*m_decoder);
this->Reset();
m_decoder.emplace(codec);
m_decoder_context.emplace(*m_decoder);
// Enable GPU decoding if requested.
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
m_hardware_context.emplace();
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
}
// Enable GPU decoding if requested.
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
m_hardware_context.emplace();
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
}
// Open the decoder context.
if (!m_decoder_context->OpenContext(*m_decoder)) {
this->Reset();
return false;
}
// Open the decoder context.
if (!m_decoder_context->OpenContext(*m_decoder)) {
this->Reset();
return false;
}
return true;
return true;
}
bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
FFmpeg::Packet packet(packet_data);
return m_decoder_context->SendPacket(packet);
FFmpeg::Packet packet(packet_data);
return m_decoder_context->SendPacket(packet);
}
std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
// Receive raw frame from decoder.
return m_decoder_context->ReceiveFrame();
// Receive raw frame from decoder.
return m_decoder_context->ReceiveFrame();
}
}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -307,7 +304,7 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
case Tegra::Texture::WrapMode::Border:
return GL_CLAMP_TO_BORDER;
case Tegra::Texture::WrapMode::Clamp:
return GL_CLAMP_TO_EDGE;
return GL_CLAMP;
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
return GL_MIRROR_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::MirrorOnceBorder:
@@ -322,10 +319,9 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
} else {
return GL_MIRROR_CLAMP_TO_EDGE;
}
default:
UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", wrap_mode);
return GL_REPEAT;
}
UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", wrap_mode);
return GL_REPEAT;
}
inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {

View File

@@ -50,8 +50,7 @@ VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter
return {};
}
VkSamplerAddressMode WrapMode(const Device& device,
Tegra::Texture::WrapMode wrap_mode,
VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wrap_mode,
Tegra::Texture::TextureFilter filter) {
switch (wrap_mode) {
case Tegra::Texture::WrapMode::Wrap:
@@ -63,6 +62,12 @@ VkSamplerAddressMode WrapMode(const Device& device,
case Tegra::Texture::WrapMode::Border:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case Tegra::Texture::WrapMode::Clamp:
if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) {
// Nvidia's Vulkan driver defaults to GL_CLAMP on invalid enumerations, we can hack this
// by sending an invalid enumeration.
return static_cast<VkSamplerAddressMode>(0xcafe);
}
// TODO(Rodrigo): Emulate GL_CLAMP properly on other vendors
switch (filter) {
case Tegra::Texture::TextureFilter::Nearest:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
@@ -76,8 +81,6 @@ VkSamplerAddressMode WrapMode(const Device& device,
case Tegra::Texture::WrapMode::MirrorOnceBorder:
UNIMPLEMENTED();
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::MirrorOnceClampOGL:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
default:
UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", wrap_mode);
return {};

View File

@@ -97,28 +97,8 @@ VkDescriptorSet DescriptorAllocator::Commit() {
return sets[index / SETS_GROW_RATE][index % SETS_GROW_RATE];
}
VkDescriptorSet DescriptorAllocator::CommitWithTracking(u64 current_frame, const void* descriptor_data) {
const size_t index = CommitResource();
const size_t group = index / SETS_GROW_RATE;
const size_t slot = index % SETS_GROW_RATE;
set_states[group][slot].set = sets[group][slot];
set_states[group][slot].last_update_frame = current_frame;
set_states[group][slot].last_data_ptr = descriptor_data;
return set_states[group][slot].set;
}
bool DescriptorAllocator::NeedsUpdate(VkDescriptorSet set, u64 current_frame, const void* descriptor_data) const {
for (const auto& group : set_states)
for (const auto& st : group)
if (st.set == set) // Update if pointer changed or the set hasn't been updated this frame
return st.last_data_ptr != descriptor_data || st.last_update_frame != current_frame;
return true;
}
void DescriptorAllocator::Allocate(size_t begin, size_t end) {
const size_t count = end - begin;
sets.push_back(AllocateDescriptors(count));
set_states.emplace_back(count); // create parallel state storage
sets.push_back(AllocateDescriptors(end - begin));
}
vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) {

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,7 +6,7 @@
#include <shared_mutex>
#include <span>
#include <vector>
#include "common/common_types.h"
#include "shader_recompiler/shader_info.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -47,15 +44,7 @@ public:
DescriptorAllocator(const DescriptorAllocator&) = delete;
VkDescriptorSet Commit();
// commit + remember when/with what we last updated this set
struct DescriptorSetState {
VkDescriptorSet set = VK_NULL_HANDLE;
u64 last_update_frame = 0;
const void* last_data_ptr = nullptr; // fast pointer compare
};
VkDescriptorSet CommitWithTracking(u64 current_frame, const void* descriptor_data);
bool NeedsUpdate(VkDescriptorSet set, u64 current_frame, const void* descriptor_data) const;
private:
explicit DescriptorAllocator(const Device& device_, MasterSemaphore& master_semaphore_,
DescriptorBank& bank_, VkDescriptorSetLayout layout_);
@@ -69,7 +58,6 @@ private:
VkDescriptorSetLayout layout{};
std::vector<vk::DescriptorSets> sets;
std::vector<std::vector<DescriptorSetState>> set_states;
};
class DescriptorPool {

View File

@@ -515,20 +515,7 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
const bool update_rescaling{scheduler.UpdateRescaling(is_rescaling)};
const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)};
const void* const descriptor_data{guest_descriptor_queue.UpdateData()};
// allocate/bind descriptor set and only update if needed
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
bool needs_update = false;
if (descriptor_set_layout && !uses_push_descriptor) {
descriptor_set = descriptor_allocator.CommitWithTracking(current_frame_number, descriptor_data);
needs_update = descriptor_allocator.NeedsUpdate(descriptor_set, current_frame_number, descriptor_data);
if (needs_update) {
const vk::Device& dev{device.GetLogical()};
dev.UpdateDescriptorSet(descriptor_set, *descriptor_update_template, descriptor_data);
}
}
scheduler.Record([this, descriptor_data, descriptor_set, bind_pipeline, rescaling_data = rescaling.Data(),
scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(),
is_rescaling, update_rescaling,
uses_render_area = render_area.uses_render_area,
render_area_data = render_area.words](vk::CommandBuffer cmdbuf) {
@@ -554,8 +541,12 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
return;
}
if (uses_push_descriptor) {
cmdbuf.PushDescriptorSetWithTemplateKHR(*descriptor_update_template, *pipeline_layout, 0, descriptor_data);
cmdbuf.PushDescriptorSetWithTemplateKHR(*descriptor_update_template, *pipeline_layout,
0, descriptor_data);
} else {
const VkDescriptorSet descriptor_set{descriptor_allocator.Commit()};
const vk::Device& dev{device.GetLogical()};
dev.UpdateDescriptorSet(descriptor_set, *descriptor_update_template, descriptor_data);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
descriptor_set, nullptr);
}

View File

@@ -116,7 +116,7 @@ public:
maxwell3d = maxwell3d_;
gpu_memory = gpu_memory_;
}
void SetFrameNumber(u64 frame) { current_frame_number = frame; }
private:
template <typename Spec>
bool ConfigureImpl(bool is_indexed);
@@ -160,7 +160,6 @@ private:
std::mutex build_mutex;
std::atomic_bool is_built{false};
bool uses_push_descriptor{false};
u64 current_frame_number{0};
};
} // namespace Vulkan

View File

@@ -234,17 +234,19 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
// update engine as channel may be different.
pipeline->SetEngine(maxwell3d, gpu_memory);
pipeline->SetFrameNumber(current_frame_number);
if (!pipeline->Configure(is_indexed))
return;
UpdateDynamicStates();
HandleTransformFeedback();
query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
draw_func();
query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, false);
}
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
@@ -772,7 +774,6 @@ void RasterizerVulkan::FlushCommands() {
}
void RasterizerVulkan::TickFrame() {
current_frame_number++;
draw_counter = 0;
guest_descriptor_queue.TickFrame();
compute_pass_descriptor_queue.TickFrame();

View File

@@ -160,7 +160,7 @@ private:
void UpdateDynamicStates();
void HandleTransformFeedback();
u64 current_frame_number{0};
void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs);

View File

@@ -13,6 +13,7 @@ Tools for Eden and other subprojects.
- `shellcheck.sh`: Ensure POSIX compliance (and syntax sanity) for all tools in this directory and subdirectories.
- `llvmpipe-run.sh`: Sets environment variables needed to run any command (or Eden) with llvmpipe.
- `optimize-assets.sh`: Optimizes PNG assets with OptiPng.
- `setup-cross-sysroot.sh`: Allows to quickly create a sysroot of a given system for cross compilation (experimental).
- `update-cpm.sh`: Updates CPM.cmake to the latest version.
- `update-icons.sh`: Rebuild all icons (macOS, Windows, bitmaps) based on the master SVG file (`dist/dev.eden_emu.eden.svg`)
* Also optimizes the master SVG

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