Compare commits

..

49 Commits

Author SHA1 Message Date
lizzie 8827c30bee fix for tests?!?!? 2026-08-13 00:01:15 +00:00
lizzie c02fd907aa use common return point to not corrupt regs also use jit state for checkbit :) 2026-08-12 23:21:09 +00:00
lizzie 476a2366f2 appropriate new terminals 2026-08-12 22:11:07 +00:00
lizzie 90159c83c5 fix ppc build errs as well 2026-08-12 22:02:57 +00:00
lizzie 76bbb973c6 fix ppc 2026-08-12 19:15:06 +00:00
lizzie ebb9a9c8f3 fixup wrong ARCHITECTURE_powerpc64 2026-08-12 19:15:06 +00:00
lizzie 6802b5b8e5 fastlink impl 2026-08-12 19:15:06 +00:00
lizzie e26cb4e6f2 update to newer dynarmic changes 2026-08-12 19:15:06 +00:00
lizzie 75bfed0a3a update loicense 2026-08-12 19:15:06 +00:00
lizzie dd352f8ce7 cross compile insn, exclude x11 from headless 2026-08-12 19:15:06 +00:00
lizzie d4764811b0 emission focused on branch/bctr 2026-08-12 19:15:06 +00:00
lizzie f5687b4af0 move docs inline, add data.txt + altivec_data.txt 2026-08-12 19:15:06 +00:00
lizzie 6eea2adcea the abi sucks
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 88c87629df fix stdu/ldu
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 2389032f39 save more regs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie e8577c9ab5 Use ctr like gcc does
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 1301af96af more ppc fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 978770372b chained calls for ppc64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 07a4292f87 inline a32core/a64core
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
crueter 7e6d28f54a build fixes + gentoo cross docs
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie a8b99595d5 fix invalidations, use stack for checkbit
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie d2d495d263 A64checkbit
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 9d36a0973a A64: addition fixes extra
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie f80ecd6f75 even more stupid fixes i feel angry
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 057e09627f fix xs stuffs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie cc570b89cb more bclr fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 5afcfd0de5 fixes for cmpld(i) encodings
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie cc9e023a01 C -> O for ppc64; impl NZCV?
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie a35d1f7183 more fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie d2b5af5d8a i forgot jit pointer, again
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie b8c987013c just use STD() + offset, reorder stuff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 20e039331d let type deduction do its thing
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 04ca01f5ea reglock draft 1
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie dc5bdfe5ca A64: ADD now passes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie cb6435a88d terminal draft1
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 78a5155e2e "A64: ADD" passes (except on PC check)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 0860070b26 polish up A64 to be ready to accept stuff (but NO-op)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 8cb83e22df remove args, directly ref inst stuff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie f3a5934ae4 fix license
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 9e5271eb0d exclude powah from non-ppc64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 375b5ddaf7 generation of add and extraction of flags
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie ecffaf558a first emitted block
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie da5accd1c2 backwards relocs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 15701abe1d relocations
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie a86511a282 cross compile instructions; ignore toolchain files
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:06 +00:00
lizzie 5812e6a063 spinlocks, annoyances with organistaion
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:05 +00:00
lizzie a7fd1b79c2 cross compile instructions
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:05 +00:00
lizzie 3299ab33b8 a64+a32 stubs (+some impls)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-12 19:15:05 +00:00
lizzie e8276bedaf fix stuff? 2026-08-12 19:15:05 +00:00
73 changed files with 10005 additions and 1630 deletions
+1
View File
@@ -19,6 +19,7 @@ log.txt
# Generated source files
src/common/scm_rev.cpp
dist/english_plurals/generated_en.ts
*-toolchain.cmake
# Project/editor files
*.swp
+18
View File
@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
set(CROSS_TARGET "powerpc64le" CACHE STRING "Cross-compilation target (aarch64, powerpc64le, riscv64, etc)")
set(CMAKE_SYSROOT /usr/${CROSS_TARGET}-unknown-linux-gnu)
set(CMAKE_C_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-g++)
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH /usr/${CROSS_TARGET}-unknown-linux-gnu)
+137 -1
View File
@@ -2,7 +2,122 @@
General guide for cross compiling.
## Debian ARM64
## Gentoo
Gentoo's cross-compilation setup is relatively easy, provided you're already familiar with portage.
### Crossdev
First, emerge crossdev via `sudo emerge -a sys-devel/crossdev`.
Now, set up the environment depending on the target architecture; e.g.
```sh
sudo crossdev powerpc64le
sudo crossdev aarch64
```
### QEMU
Installing a qemu user setup is recommended for testing. To do so, you will need the relevant USE flags:
```sh
app-emulation/qemu static-user qemu_user_targets_ppc64le qemu_user_targets_aarch64
```
Note that to use cross-emerged libraries, you will need to tell qemu where the sysroot is. You can do this with an alias:
```sh
alias qemu-ppc64le="qemu-ppc64le -L /usr/powerpc64le-unknown-linux-gnu"
alias qemu-aarch64="qemu-aarch64 -L /usr/aarch64-unknown-linux-gnu"
```
### Dependencies
Some packages have broken USE flags on other architectures; you'll also need to set up python targets. In `/usr/<target>-unknown-linux-gnu/etc/portage/package.use`:
```sh
>=net-misc/curl-8.16.0-r1 ssl
*/* PYTHON_TARGETS: python3_13 PYTHON_SINGLE_TARGET: python3_13
*/* pam
sys-apps/util-linux pam su
app-shells/bash -readline
>=dev-libs/libpcre2-10.47 unicode
>=x11-libs/libxkbcommon-1.12.3 X
>=sys-libs/zlib-1.3.1-r1 minizip
>=app-alternatives/gpg-1-r3 ssl
>=app-crypt/gnupg-2.5.13-r2 ssl
dev-libs/* -introspection
media-libs/harfbuzz -introspection
dev-libs/quazip -qt5 qt6
```
Dependencies should be about the same [as normal Gentoo](./Deps.md), but removing gamemode and renderdoc is recommended. Keep in mind that when emerging, you want to use `emerge-<target>-unknown-linux-gnu`, e.g. `emerge-powerpc64le-unknown-linux-gnu`.
Enable GURU in the cross environment (as root):
```sh
mkdir -p /usr/powerpc64le-unknown-linux-gnu/etc/portage/repos.conf
cat << EOF > /usr/powerpc64le-unknown-linux-gnu/etc/portage/repos.conf/guru.conf
[guru]
location = /var/db/repos/guru
auto-sync = no
priority = 1
EOF
```
Now emerge your dependencies:
```sh
sudo emerge-powerpc64le-unknown-linux-gnu -aU app-arch/lz4 app-arch/zstd app-arch/unzip \
dev-libs/libfmt dev-libs/libusb dev-libs/mcl dev-libs/sirit dev-libs/oaknut \
dev-libs/unordered_dense dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
dev-util/vulkan-utility-libraries dev-util/glslang \
media-libs/libva media-libs/opus media-video/ffmpeg \
media-libs/VulkanMemoryAllocator media-libs/libsdl2 media-libs/cubeb \
net-libs/enet net-libs/mbedtls \
sys-libs/zlib \
dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt dev-cpp/catch \
net-wireless/wireless-tools \
dev-qt/qtbase:6 dev-libs/quazip \
virtual/pkgconfig
```
### Building
A toolchain is provided in `CMakeModules/GentooCross.cmake`. To use it:
```sh
cmake -S . -B build/ppc64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/GentooCross.cmake -G Ninja -DCROSS_TARGET=powerpc64le -DENABLE_OPENGL=OFF
```
Now build as normal:
```sh
cmake --build build/ppc64 -j$(nproc)
```
### Alternatively
Only emerge the absolute necessities:
```sh
sudo emerge-powerpc64le-unknown-linux-gnu -aU media-video/ffmpeg media-libs/libsdl2 dev-qt/qtbase:6
```
Then set `YUZU_USE_CPM=ON`:
```sh
cmake -S . -B build/ppc64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/GentooCross.cmake -G Ninja -DCROSS_TARGET=powerpc64le -DENABLE_OPENGL=OFF -DYUZU_USE_CPM=ON
```
## ARM64
### Debian ARM64
A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main.
@@ -88,3 +203,24 @@ And finally, run the compiled executable with QEMU!
```sh
qemu-aarch64 build/aarch64/bin/eden
```
## PowerPC
This is a guide for FreeBSD users mainly.
Now you got a PowerPC sysroot - quickly decompress it somewhere, say `/home/user/opt/powerpc64le`. Create a toolchain file, for example `powerpc64le-toolchain.cmake`; always [consult the manual](https://man.freebsd.org/cgi/man.cgi?query=cmake-toolchains&sektion=7&manpath=FreeBSD+13.2-RELEASE+and+Ports).
There is a script to automatically do all of this under `./tools/setup-cross-sysroot.sh`.
Remember to add `-mabi=elfv1` to `CFLAGS`/`CXXFLAGS` otherwise the program will crash.
Specify:
- `YUZU_USE_CPM`: Set this to `ON` so packages can be found and built if your sysroot doesn't have them.
- `YUZU_USE_EXTERNAL_FFMPEG`: Set this to `ON` as well.
Then run using a program such as QEMU to emulate userland syscalls:
```sh
cmake --build build-ppc64-pc-freebsd -t dynarmic_tests -- -j8 && qemu-ppc64-static -L $HOME/opt/ppc64-freebsd/sysroot ./build-ppc64-pc-freebsd/bin/dynarmic_tests
```
+8
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.
+2
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.
@@ -50,6 +51,7 @@ Documentation
-------------
Design documentation can be found at [./Design.md](./Design.md).
PowerPC design documentation can be found at [./PowerPC](./PowerPC.md).
Usage Example
+5
View File
@@ -254,6 +254,11 @@ endif()
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)
# PowerPC emitter
if (ARCHITECTURE_powerpc64)
add_subdirectory(powah)
endif()
add_library(tz tz/tz/tz.cpp)
target_include_directories(tz PUBLIC ./tz)
+2 -2
View File
@@ -166,7 +166,7 @@ detect_architecture_symbols(
"_M_MRX000")
detect_architecture_symbols(
ARCH ppc64
ARCH powerpc64
SYMBOLS
"__ppc64__"
"__powerpc64__"
@@ -174,7 +174,7 @@ detect_architecture_symbols(
"_M_PPC64")
detect_architecture_symbols(
ARCH ppc
ARCH powerpc
SYMBOLS
"__ppc__"
"__ppc"
+10
View File
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
add_library(powah INTERFACE)
target_include_directories(powah INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(powah INTERFACE -Wno-unused-parameter)
add_executable(powah_tests tests.cpp)
create_target_directory_groups(powah_tests)
target_link_libraries(powah_tests PRIVATE Catch2::Catch2WithMain)
+145
View File
@@ -0,0 +1,145 @@
vabsdub,VX,04,1027
vabsduh,VX,04,1091
vabsduw,VX,04,1155
vaddcuw,VX,04,384
vaddfp,VX,04,10
vaddsbs,VX,04,768
vaddshs,VX,04,832
vaddsws,VX,04,896
vaddubm,VX,04,0
vaddubs,VX,04,512
vadduhm,VX,04,64
vadduhs,VX,04,576
vadduwm,VX,04,128
vadduws,VX,04,640
vand,VX,04,1028
vandc,VX,04,1092
vavgsb,VX,04,1282
vavgsh,VX,04,1346
vavgsw,VX,04,1410
vavgub,VX,04,1026
vavguh,VX,04,1090
vavguw,VX,04,1154
vcfsx,VDU,04842
vcfux,VDU,04778
vcmpbfp,VXR,04,966
vcmpeqfp,VXR,04,198
vcmpequb,VXR,04,6
vcmpequh,VXR,04,70
vcmpequw,VXR,04,134
vcmpgefp,VXR,04,454
vcmpgtfp,VXR,04,710
vcmpgtsb,VXR,04,774
vcmpgtsh,VXR,04,838
vcmpgtsw,VXR,04,902
vcmpgtub,VXR,04,518
vcmpgtuh,VXR,04,582
vcmpgtuw,VXR,04,646
vctsxs,VDU,04970
vctuxs,VDU,04906
vexptefp,VY,04,394
vlogefp,VY,04,458
vmaddfp,VXC,46
vmaxfp,VX,04,1034
vmaxsb,VX,04,258
vmaxsh,VX,04,322
vmaxsw,VX,04,386
vmaxub,VX,04,2
vmaxuh,VX,04,66
vmaxuw,VX,04,130
vmhaddshs,VXC,32
vmhraddshs,VXC,33
vminfp,VX,04,1098
vminsb,VX,04,770
vminsh,VX,04,834
vminsw,VX,04,898
vminub,VX,04,514
vminuh,VX,04,578
vminuw,VX,04,642
vmladduhm,VXC,34
vmrghb,VX,04,12
vmrghh,VX,04,76
vmrghw,VX,04,140
vmrglb,VX,04,268
vmrglh,VX,04,332
vmrglw,VX,04,396
vmsummbm,VXC,37
vmsumshm,VXC,40
vmsumshs,VXC,41
vmsumubm,VXC,36
vmsumuhm,VXC,38
vmsumuhs,VXC,39
vmulesb,VX,04,776
vmulesh,VX,04,840
vmuleub,VX,04,520
vmuleuh,VX,04,584
vmulosb,VX,04,264
vmulosh,VX,04,328
vmuloub,VX,04,8
vmulouh,VX,04,72
vnmsubfp,VXC,47
vnor,VX,04,1284
vor,VX,04,1156
vperm,VXC,43
vpkpx,VX,04,782
vpkshss,VX,04,398
vpkshus,VX,04,270
vpkswss,VX,04,462
vpkswus,VX,04,334
vpkuhum,VX,04,14
vpkuhus,VX,04,142
vpkuwum,VX,04,78
vpkuwus,VX,04,206
vrefp,VY,04,266
vrfim,VY,04,714
vrfin,VY,04,522
vrfip,VY,04,650
vrfiz,VY,04,586
vrlb,VX,04,4
vrlh,VX,04,68
vrlw,VX,04,132
vrsqrtefp,VY,04,330
vsel,VXC,42
vsl,VX,04,452
vslb,VX,04,260
vsldoi,VX,04,/ SH 44
vslh,VX,04,324
vslo,VX,04,1036
vslw,VX,04,388
vspltb,VU,04,524
vsplth,VU,04,588
vspltisb,VS,04,780
vspltish,VS,04,844
vspltisw,VS,04,908
vspltw,VSU,04,652
vsr,VX,04,708
vsrab,VX,04,772
vsrah,VX,04,836
vsraw,VX,04,900
vsrb,VX,04,516
vsrh,VX,04,580
vsro,VX,04,1100
vsrw,VX,04,644
vsubcuw,VX,04,1408
vsubfp,VX,04,74
vsubsbs,VX,04,1792
vsubshs,VX,04,1856
vsubsws,VX,04,1920
vsububm,VX,04,1024
vsububs,VX,04,1536
vsubuhm,VX,04,1088
vsubuhs,VX,04,1600
vsubuwm,VX,04,1152
vsubuws,VX,04,1664
vsumsws,VX,04,1928
vsum2sws,VX,04,1672
vsum4sbs,VX,04,1800
vsum4shs,VX,04,1608
vsum4ubs,VX,04,1544
vupkhpx,VY,04,846
vupkhsb,VY,04,526
vupkhsh,VY,04,590
vupklpx,VY,04,974
vupklsb,VY,04,654
vupklsh,VY,04,718
vxor,VX,04,122
Vendored Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
$CC data2code.c -o data2code && ./data2code data.txt >powah_gen_base.hpp
$CC --target=powerpc64le test.S -c -o test && objdump -SC test
+222
View File
@@ -0,0 +1,222 @@
add[o][.],Add,XO,31,266
addc[o][.],Add Carrying,XO,31,10
adde[o][.],Add Extended,XO,31,138
addi,Add Immediate,D,14,
addic,Add Immediate Carrying,D,12,
addic.,Add Immediate Carrying and Record,D,13,
addis,Add Immediate Shifted,D,15,
addme[o][.],Add to Minus One Extended,XO,31,234
addze[o][.],Add to Zero Extended,XO,31,202
and[.],AND,X,31,28
andc[.],AND with Complement,X,31,60
andi.,AND Immediate,D,28,
andis.,AND Immediate Shifted,D,29,
b[l][a],Branch,I,18,
bc[l][a],Branch Conditional,B,16,
bcctr[l],Branch Conditional to Count Register,XL,19,528
bclr[l],Branch Conditional Link Register,XL,19,16
cmp,Compare,X,31,0
cmpi,Compare Immediate,D,11,
cmpl,Compare Logical,X,31,32
cmpli,Compare Logical Immediate,D,10,
cntlzd,Count Leading Zeros Doubleword,X,31,58
cntlzw[.],Count Leading Zeros Word,X,31,26
crand,Condition Register AND,XL,19,257
crandc,Condition Register AND with Complement,XL,19,129
creqv,Condition Register Equivalent,XL,19,289
crnand,Condition Register NAND,XL,19,225
crnor,Condition Register NOR,XL,19,33
cror,Condition Register OR,XL,19,449
crorc,Condition Register OR with Complement,XL,19,417
crxor,Condition Register XOR,XL,19,193
dcbf,Data Cache Block Flush,X,31,86
dcbi,Data Cache Block Invalidate,X,31,470
dcbst,Data Cache Block Store,X,31,54
dcbt,Data Cache Block Touch,X,31,278
dcbtst,Data Cache Block Touch for Store,X,31,246
dcbz,Data Cache Block Set to Zero,X,31,1014
divd,Divide Doubleword,XO,31,489
divdu,Divide Doubleword Unsigned,XO,31,457
divw[o][.],Divide Word,XO,31,491
divwu[o][.],Divide Word Unsigned,XO,31,459
eciwx,External Control in Word Indexed (opt.),X,31,310
ecowx,External Control out Word Indexed (opt.),X,31,438
eieio,Enforce In-order Execution of I/O,X,31,854
eqv[.],Equivalent,X,31,284
extsb[.],Extend Sign Byte,X,31,954
extsh[.],Extend Sign Halfword,XO,31,922
extsw,Extend Sign Word,X,31,986
fabs[.],Floating Absolute Value,X,63,264
fadd[.],Floating Add,A,63,21
fadds[.],Floating Add Single,A,59,21
fcfid,Floating Convert from Integer Doubleword,X,63,846
fcmpo,Floating Compare Ordered,X,63,32
fcmpu,Floating Compare Unordered,XL,63,0
fctid,Floating Convert to Integer Doubleword,X,63,814
fctidz,Floating Convert to Integer Doubleword with Round Toward Zero,X,63,815
fctiw[.],Floating Convert to Integer Word,X,63,14
fctiwz[.],Floating Convert to Integer Word with Round to Zero,XL,63,15
fdiv[.],Floating Divide,A,63,18
fdivs[.],Floating Divide Single,A,59,18
fmadd[.],Floating Multiply-Add,A,63,29
fmadds[.],Floating Multiply-Add Single,A,59,29
fmr[.],Floating Move Register,X,63,72
fmsub[.],Floating Multiply-Subtract,A,63,28
fmsubs[.],Floating Multiply-Subtract Single,A,59,28
fmul[.],Floating Multiply,A,63,25
fmuls[.],Floating Multiply Single,A,59,25
fnabs[.],Floating Negative Absolute Value,X,63,136
fneg[.],Floating Negate,X,63,40
fnmadd[.],Floating Negative Multiply-Add,A,63,31
fnmadds[.],Floating Negative Multiply-Add Single,A,59,31
fnmsub[.],Floating Negative Multiply-Subtract,A,63,30
fnmsubs[.],Floating Negative Multiply-Subtract Single,A,59,30
fres[.],Floating Reciprocal Estimate Single (optional),A,59,24
frsp[.],Floating Round to Single Precision,X,63,12
frsqrte[.],Floating Reciprocal Square Root Estimate (optional),A,63,26
fsel[.],Floating-Point Select (optional),A,63,23
fsub[.],Floating Subtract,A,63,20
fsubs[.],Floating Subtract Single,A,59,20
icbi,Instruction Cache Block Invalidate,X,31,982
isync,Instruction Synchronize,X,19,150
lbz,Load Byte and Zero,D,34,
lbzu,Load Byte and Zero with Update,D,35,
lbzux,Load Byte and Zero with Update Indexed,X,31,119
lbzx,Load Byte and Zero Indexed,X,31,87
ld,Load Doubleword,DS,58,0
ldarx,Load Doubleword and Reserve Indexed,X,31,84
ldu,Load Doubleword with Update,DS,58,1
ldux,Load Doubleword with Update Indexed,X,31,53
ldx,Load Doubleword Indexed,X,31,21
lfd,Load Floating-Point Double,D,50,
lfdu,Load Floating-Point Double with Update,D,51,
lfdux,Load Floating-Point Double with Update Indexed,X,31,631
lfdx,Load Floating-Point Double Indexed,X,31,599
lfs,Load Floating-Point Single,D,48,
lfsu,Load Floating-Point Single with Update,D,49,
lfsux,Load Floating-Point Single with Update Indexed,X,31,567
lfsx,Load Floating-Point Single Indexed,X,31,535
lha,Load Half Algebraic,D,42,
lhau,Load Half Algebraic with Update,D,43,
lhaux,Load Half Algebraic with Update Indexed,X,31,375
lhax,Load Half Algebraic Indexed,X,31,343
lhbrx,Load Half Byte-Reversed Indexed,X,31,790
lhz,Load Half and Zero,D,40,
lhzu,Load Half and Zero with Update,D,41,
lhzux,Load Half and Zero with Update Indexed,X,31,331
lhzx,Load Half and Zero Indexed,X,31,279
lmw,Load Multiple Word,D,46,
lswi,Load String Word Immediate,X,31,597
lswx,Load String Word Indexed,X,31,533
lwa,Load Word Algebraic,DS,58,2
lwarx,Load Word and Reserve Indexed,X,31,20
lwaux,Load Word Algebraic with Update Indexed,X,31,373
lwax,Load Word Algebraic Indexed,X,31,341
lwbrx,Load Word Byte-Reversed Indexed,X,31,534
lwz,Load Word and Zero,D,32,
lwzu,Load Word with Zero Update,D,33,
lwzux,Load Word and Zero with Update Indexed,X,31,55
lwzx,Load Word and Zero Indexed,X,31,23
mcrf,Move Condition Register Field,XL,19,0
mcrfs,Move to Condition Register from FPSCR,X,63,64
mcrxr,Move to Condition Register from XER,X,31,512
mfcr,Move from Condition Register,X,31,19
mffs[.],Move from FPSCR,X,63,583
mfmsr,Move from Machine State Register,X,31,83
mfspr,Move from Special-Purpose Register,X,31,339
mfsr,Move from Segment Register,X,31,595
mfsrin,Move from Segment Register Indirect,X,31,659
mtcrf,Move to Condition Register Fields,XFX,31,144
mtfsb0[.],Move to FPSCR Bit 0,X,63,70
mtfsb1[.],Move to FPSCR Bit 1,X,63,38
mtfsf[.],Move to FPSCR Fields,XFL,63,711
mtfsfi[.],Move to FPSCR Field Immediate,X,63,134
mtmsr,Move to Machine State Register,X,31,146
mtspr,Move to Special-Purpose Register,X,31,467
mtsr,Move to Segment Register,X,31,210
mtsrin,Move to Segment Register Indirect,X,31,242
mulhd,Multiply High Doubleword,XO,31,73
mulhdu,Multiply High Doubleword Unsigned,XO,31,9
mulhw[.],Multiply High Word,XO,31,75
mulhwu[.],Multiply High Word Unsigned,XO,31,11
mulld,Multiply Low Doubleword,XO,31,233
mulli,Multiply Low Immediate,D,07,
mullw[o][.],Multiply Low Word,XO,31,235
nand[.],NAND,X,31,476
neg[o][.],Negate,XO,31,104
nor[.],NOR,X,31,124
or[.],OR,X,31,444
orc[.],OR with Complement,X,31,412
ori,OR Immediate,D,24,
oris,OR Immediate Shifted,D,25,
rfi,Return from Interrupt,X,19,50
rldcl,Rotate Left Doubleword then Clear Left,MDS,30,8
rldcr,Rotate Left Doubleword then Clear Right,MDS,30,9
rldic,Rotate Left Doubleword Immediate then Clear,MD,30,2
rldicl,Rotate Left Doubleword Immediate then Clear Left,MD,30,0
rldicr,Rotate Left Doubleword Immediate then Clear Right,MD,30,1
rldimi,Rotate Left Doubleword Immediate then Mask Insert,MD,30,3
rlwimi[.],Rotate Left Word Immediate then Mask Insert,M,20,
rlwinm[.],Rotate Left Word Immediate then AND with Mask,M,21,
rlwnm[.],Rotate Left Word then AND with Mask,M,23,
sc,System Call,SC,17,
si,Subtract Immediate,D,12,
si.,Subtract Immediate and Record,D,13,
slbia,SLB Invalidate All,X,31,498
slbie,SLB Invalidate Entry,X,31,434
sld,Shift Left Doubleword,X,31,27
slw[.],Shift Left Word,X,31,24
srad,Shift Right Algebraic Doubleword,X,31,794
sradi,Shift Right Algebraic Doubleword Immediate,XS,31,413
srd,Shift Right Doubleword,X,31,539
sraw[.],Shift Right Algebraic Word,X,31,792
srawi[.],Shift Right Algebraic Word Immediate,X,31,824
srw[.],Shift Right Word,X,31,536
stb,Store Byte,D,38,
stbu,Store Byte with Update,D,39,
stbux,Store Byte with Update Indexed,X,31,247
stbx,Store Byte Indexed,X,31,215
std,Store Doubleword,DS,62,0
stdcx,Store Doubleword Conditional Indexed,X,31,214
stdu,Store Doubleword with Update,DS,62,1
stdux,Store Doubleword with Update Indexed,X,31,181
stdx,Store Doubleword Indexed,X,31,149
stfd,Store Floating-Point Double,D,54,
stfdu,Store Floating-Point Double with Update,D,55,
stfdux,Store Floating-Point Double with Update Indexed,X,31,759
stfdx,Store Floating-Point Double Indexed,X,31,727
stfiwx,Store Floating-Point as Integer Word Indexed (optional),X,31,983
stfs,Store Floating-Point Single,D,52,
stfsu,Store Floating-Point Single with Update,D,53,
stfsux,Store Floating-Point Single with Update Indexed,X,31,695
stfsx,Store Floating-Point Single Indexed,X,31,663
sth,Store Half,D,44,
sthbrx,Store Half Byte-Reverse Indexed,X,31,918
sthu,Store Half with Update,D,45,
sthux,Store Half with Update Indexed,X,31,439
sthx,Store Half Indexed,X,31,407
stmw,Store Multiple Word,D,47,
stswi,Store String Word Immediate,X,31,725
stswx,Store String Word Indexed,X,31,661
stw,Store,D,36,
stwbrx,Store Word Byte-Reversed Indexed,X,31,662
stwcx.,Store Word Conditional Indexed,X,31,150
stwu,Store Word with Update,D,37,
stwux,Store Word with Update Indexed,X,31,183
stwx,Store Word Indexed,X,31,151
subf[o][.],Subtract from,XO,31,40
subfc[o][.],Subtract from Carrying,XO,31,08
subfe[o][.],Subtract from Extended,XO,31,136
subfic,Subtract from Immediate Carrying,D,08,
subfme[o][.],Subtract from Minus One Extended,XO,31,232
subfze[o][.],Subtract from Zero Extended,XO,31,200
sync,Synchronize,X,31,598
td,Trap Doubleword,X,31,68
tdi,Trap Doubleword Immediate,D,2,
tlbie,Translation Look-aside Buffer Invalidate Entry (optional),X,31,306
tlbsync,Translation Look-aside Buffer Synchronize (optional),X,31,566
tw,Trap Word,X,31,04
twi,Trap Word Immediate,D,03,
xor[.],XOR,X,31,316
xori,XOR Immediate,D,26,
xoris,XOR Immediate Shift,D,27,
+343
View File
@@ -0,0 +1,343 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#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;
}
+401
View File
@@ -0,0 +1,401 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#undef NDEBUG
#include <cstdint>
#include <cstddef>
#include <cassert>
#include <vector>
#include <utility>
#include <sys/mman.h>
#include <cstdlib>
//#ifndef __cpp_lib_unreachable
namespace std {
[[noreturn]] inline void unreachable() {
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
__assume(false);
#else // GCC, Clang
__builtin_unreachable();
#endif
}
}
//#endif
namespace powah {
// Symbolic conditions
enum class Cond : uint8_t {
LT, LE, NG, EQ, GE, NL, GT, NE, SO, UN, NS, NU
};
struct GPR { uint32_t index; };
struct FPR { uint32_t index; };
struct VPR { uint32_t index; };
struct CPR { uint32_t index; };
struct Label { uint32_t index; };
constexpr inline GPR R0{0};
constexpr inline GPR R1{1};
constexpr inline GPR R2{2};
constexpr inline GPR R3{3};
constexpr inline GPR R4{4};
constexpr inline GPR R5{5};
constexpr inline GPR R6{6};
constexpr inline GPR R7{7};
constexpr inline GPR R8{8};
constexpr inline GPR R9{9};
constexpr inline GPR R10{10};
constexpr inline GPR R11{11};
constexpr inline GPR R12{12};
constexpr inline GPR R13{13};
constexpr inline GPR R14{14};
constexpr inline GPR R15{15};
constexpr inline GPR R16{16};
constexpr inline GPR R17{17};
constexpr inline GPR R18{18};
constexpr inline GPR R19{19};
constexpr inline GPR R20{20};
constexpr inline GPR R21{21};
constexpr inline GPR R22{22};
constexpr inline GPR R23{23};
constexpr inline GPR R24{24};
constexpr inline GPR R25{25};
constexpr inline GPR R26{26};
constexpr inline GPR R27{27};
constexpr inline GPR R28{28};
constexpr inline GPR R29{29};
constexpr inline GPR R30{30};
constexpr inline GPR R31{31};
constexpr inline FPR FR0{0};
constexpr inline FPR FR1{1};
constexpr inline FPR FR2{2};
constexpr inline FPR FR3{3};
constexpr inline FPR FR4{4};
constexpr inline FPR FR5{5};
constexpr inline FPR FR6{6};
constexpr inline FPR FR7{7};
constexpr inline FPR FR8{8};
constexpr inline FPR FR9{9};
constexpr inline FPR FR10{10};
constexpr inline FPR FR11{11};
constexpr inline FPR FR12{12};
constexpr inline FPR FR13{13};
constexpr inline FPR FR14{14};
constexpr inline FPR FR15{15};
constexpr inline FPR FR16{16};
constexpr inline FPR FR17{17};
constexpr inline FPR FR18{18};
constexpr inline FPR FR19{19};
constexpr inline FPR FR20{20};
constexpr inline FPR FR21{21};
constexpr inline FPR FR22{22};
constexpr inline FPR FR23{23};
constexpr inline FPR FR24{24};
constexpr inline FPR FR25{25};
constexpr inline FPR FR26{26};
constexpr inline FPR FR27{27};
constexpr inline FPR FR28{28};
constexpr inline FPR FR29{29};
constexpr inline FPR FR30{30};
constexpr inline FPR FR31{31};
// They call it CPR because when programmers see this code they-
constexpr inline CPR CR0{0};
constexpr inline CPR CR1{4};
constexpr inline CPR CR2{8};
constexpr inline CPR CR3{12};
constexpr inline CPR CR4{16};
constexpr inline CPR CR5{20};
constexpr inline CPR CR6{24};
constexpr inline CPR CR7{28};
constexpr uint32_t XER_SO = 32;
constexpr uint32_t XER_OV = 33;
constexpr uint32_t XER_CA = 34;
enum class RelocKind : uint8_t {
FormB,
FormI,
};
struct RelocInfo {
uint32_t offset;
RelocKind kind;
};
struct Context {
Context() = default;
Context(void* ptr, size_t size)
: base{reinterpret_cast<uint32_t*>(ptr)}
, offset{0}
, size{uint32_t(size)} {
}
~Context() = default;
std::vector<uint32_t> labels;
std::vector<std::pair<uint32_t, RelocInfo>> relocs;
Label DefineLabel() {
labels.push_back(0);
return Label{ uint32_t(labels.size() - 1) };
}
void ApplyRelocs() {
for (auto const &[index, info] : relocs) {
//assert(labels[index] != 0); //label must have an addr
int32_t rel = (int32_t)labels[index] - (int32_t)info.offset;
switch (info.kind) {
case RelocKind::FormB:
base[info.offset] |= bitExt(rel, 16, 14);
break;
case RelocKind::FormI:
base[info.offset] |= bitExt(rel, 6, 24);
break;
}
}
relocs.clear();
}
void LABEL(Label l) {
assert(labels[l.index] == 0);
labels[l.index] = offset;
}
static constexpr uint32_t cond2offset(Cond c) noexcept {
switch (c) {
case Cond::LT: return 1;
case Cond::LE: return 2;
case Cond::NG: return 2;
case Cond::EQ: return 3;
case Cond::GE: return 1;
case Cond::NL: return 1;
case Cond::GT: return 2;
case Cond::NE: return 3;
case Cond::SO: return 4;
case Cond::UN: return 4;
case Cond::NS: return 4;
case Cond::NU: return 4;
default: return 0; //hopefully icc isn't stupid
}
}
uint32_t bitExt(uint32_t value, uint32_t offs, uint32_t n) {
uint32_t mask = (1UL << n) - 1;
return (value & mask) << (32 - (n + offs));
}
void emit_XO(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool oe, bool rc) {
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| bitExt(oe, 21, 1)
| bitExt(rc, 31, 1)
);
}
void emit_D(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| (d & 0xffff)
);
}
void emit_X(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool rc) {
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| bitExt(rc, 31, 1)
);
}
void emit_XS(uint32_t op, GPR const rt, GPR const ra, uint32_t sh, bool rc) {
base[offset++] = (op |
((rt.index & 0x1f) << 16)
| ((ra.index & 0x1f) << 21)
| ((sh & 0x1f) << 11)
| ((sh >> 4) & 0x02)
| bitExt(rc, 31, 1)
);
}
void emit_reloc_I(uint32_t op, Label const& l, bool lk) {
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormI });
base[offset++] = (op |
bitExt(lk, 31, 1)
);
}
void emit_reloc_B(uint32_t op, uint32_t bo, uint32_t cri, Label const& l, bool lk) {
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormB });
base[offset++] = (op |
bitExt(bo, 6, 5)
| bitExt(cri, 11, 5)
| bitExt(lk, 31, 1)
);
}
void emit_XL(uint32_t op, uint32_t bt, uint32_t ba, uint32_t bb, bool lk) {
base[offset++] = (op |
bitExt(bt, 6, 5)
| bitExt(ba, 11, 5)
| bitExt(bb, 16, 5)
| bitExt(lk, 31, 1)
);
}
void emit_A(uint32_t op, FPR const frt, FPR const fra, FPR const frb, FPR const frc, bool rc) {
base[offset++] = (op |
bitExt(frt.index, 6, 5)
| bitExt(fra.index, 11, 5)
| bitExt(frb.index, 16, 5)
| bitExt(frc.index, 21, 5)
| bitExt(rc, 31, 1)
);
}
void emit_DS(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
//assert(d & 0x03 == 0);
base[offset++] = (op |
bitExt(rt.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(d, 16, 14)
);
}
void emit_M(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me, bool rc) {
assert(sh <= 0x3f && mb <= 0x3f);
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| ((sh & 0x1f) << 11)
| ((mb & 0x1f) << 6)
| ((me & 0x1f) << 1)
| bitExt(rc, 31, 1)
);
}
void emit_MD(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
assert(mb <= 0x3f);
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| ((mb & 0x1f) << 6) | (mb & 0x20)
| bitExt(rc, 31, 1)
);
}
void emit_MD(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, bool rc) {
assert(sh <= 0x3f && mb <= 0x3f);
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| ((mb & 0x1f) << 6) | (mb & 0x20)
| ((sh & 0x1f) << 11) | ((sh >> 4) & 0x02)
| bitExt(rc, 31, 1)
);
}
void emit_MDS(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
base[offset++] = (op |
bitExt(rs.index, 6, 5)
| bitExt(ra.index, 11, 5)
| bitExt(rb.index, 16, 5)
| ((mb & 0x1f) << 6) | (mb & 0x20)
| bitExt(rc, 31, 1)
);
}
void emit_XFL(uint32_t op) {
(void)op;
std::abort();
}
void emit_XFX(uint32_t op, GPR const rt, uint32_t spr) {
base[offset++] = (op |
(rt.index & 0x1f) << 21
| ((spr & 0xff) << 12)
);
}
void emit_SC(uint32_t op, uint32_t lev) {
(void)op;
(void)lev;
std::abort();
}
// Extended Memmonics, hand coded :)
void MR(GPR const ra, GPR const rs) { OR(ra, rs, rs); }
void NOP() { ORI(R0, R0, 0); }
void NOT(GPR const ra, GPR const rs) { NOR(ra, rs, rs); }
void ROTLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, n, 0); }
void ROTRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, 0); }
void ROTLWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, n, 0, 31); }
void ROTRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, 0, 31); }
void ROTLW(GPR const ra, GPR const rs, GPR const rb) { RLWNM(ra, rs, rb, 0, 31); }
void ROTLD(GPR const ra, GPR const rs, GPR const rb) { RLDCL(ra, rs, rb, 0); }
void EXTLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, b, n - 1); }
void SLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, n, 63 - n); }
void CLRLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 0, n); }
void EXTRDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICL(ra, rs, b + n, 64 - n); }
void SRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, n); }
void CLLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, 0, n); }
void CLRSLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, n, b - n); }
void EXTLWI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLWINM(ra, rs, b, 0, n - 1); }
void SRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, n, 31); }
void CLRRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 0, 0, 31 - n); }
void CRSET(CPR const bx) { CREQV(bx, bx, bx); }
void CRCLR(CPR const bx) { CRXOR(bx, bx, bx); }
void CRMOVE(CPR const bx, CPR const by) { CROR(bx, by, by); }
void CRNOT(CPR const bx, CPR const by) { CRNOR(bx, by, by); }
void CMPLDI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 1, rx, v); }
void CMPLWI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 0, rx, v); }
void CMPLD(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 1, rx, ry); }
void CMPLW(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 0, rx, ry); }
void CMPLDI(GPR const rx, uint32_t v) { CMPLI(0, 1, rx, v); }
void CMPLWI(GPR const rx, uint32_t v) { CMPLI(0, 0, rx, v); }
void CMPLD(GPR const rx, GPR const ry) { CMPL(0, 1, rx, ry); }
void CMPLW(GPR const rx, GPR const ry) { CMPL(0, 0, rx, ry); }
void CMPWI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 0, rx, si); }
void CMPW(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 0, rx, ry); }
void CMPDI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 1, rx, si); }
void CMPD(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 1, rx, ry); }
void CMPWI(GPR const rx, uint32_t si) { CMPI(0, 0, rx, si); }
void CMPW(GPR const rx, GPR const ry) { CMP(0, 0, rx, ry); }
void CMPDI(GPR const rx, uint32_t si) { CMPI(0, 1, rx, si); }
void CMPD(GPR const rx, GPR const ry) { CMP(0, 1, rx, ry); }
void LI(GPR const rx, uint32_t value) { ADDI(rx, R0, value); }
void LIS(GPR const rx, uint32_t value) { ADDIS(rx, R0, value); }
void MFLR(GPR const rt) { MFSPR(powah::GPR{8}, rt, powah::GPR{0}); }
void MTLR(GPR const rt) { MTSPR(powah::GPR{8}, rt, powah::GPR{0}); }
void BLR() { base[offset++] = 0x4e800020; } //BCLR(R0, CR0, R0);
void MFCTR(GPR const rt) { MFSPR(powah::GPR{9}, rt, powah::GPR{0}); }
void MTCTR(GPR const rt) { MTSPR(powah::GPR{9}, rt, powah::GPR{0}); }
void BCTRL() { base[offset++] = 0x4e800421; } //BCCTRL(R0, CR0, R0);
void BCTR() { base[offset++] = 0x4e800420; } //BCCTR(R0, CR0, R0);
// TODO: PowerPC 11 stuff
void ISEL(GPR const rd, GPR const ra, GPR const rb, uint32_t d) {
(void)rd;
(void)ra;
(void)rb;
(void)d;
std::unreachable();
}
void ISELLT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 0); }
void ISELGT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 1); }
void ISELEQ(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 2); }
// Rawly pasted because fuck you
#include "powah_gen_base.hpp"
uint32_t* base = nullptr;
uint32_t offset = 0;
uint32_t size = 0;
};
}
+487
View File
@@ -0,0 +1,487 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// 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
View File
@@ -0,0 +1,340 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <catch2/catch_test_macros.hpp>
#include <sys/mman.h>
#include "powah_emit.hpp"
#define EB32(X) __bswap32(X)
TEST_CASE("ppc64: addi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.ADDI(powah::R2, powah::R2, 0);
ctx.ADDIS(powah::R2, powah::R12, 0);
REQUIRE(data[0] == EB32(0x00004238));
REQUIRE(data[1] == EB32(0x00004c3c));
}
TEST_CASE("ppc64: std/mr", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.STD(powah::R26, powah::R1, 144);
ctx.MR(powah::R26, powah::R4);
ctx.STD(powah::R30, powah::R1, 176);
ctx.MR(powah::R30, powah::R3);
ctx.RLDICR(powah::R4, powah::R5, 25, 6);
ctx.STD(powah::R28, powah::R1, 160);
REQUIRE(data[0] == EB32(0x900041fb));
REQUIRE(data[1] == EB32(0x78239a7c));
REQUIRE(data[2] == EB32(0xb000c1fb));
REQUIRE(data[3] == EB32(0x781b7e7c));
REQUIRE(data[4] == EB32(0x84c9a478));
REQUIRE(data[5] == EB32(0xa00081fb));
}
TEST_CASE("ppc64: sldi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.SLDI(powah::R3, powah::R5, 37);
REQUIRE(data[0] == EB32(0x862ea378));
}
TEST_CASE("ppc64: rldicr", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.RLDICR(powah::R1, powah::R1, 0, 0);
ctx.RLDICR(powah::R1, powah::R1, 1, 0);
ctx.RLDICR(powah::R1, powah::R1, 0, 1);
ctx.RLDICR(powah::R1, powah::R1, 1, 1);
ctx.RLDICR(powah::R1, powah::R1, 31, 31);
REQUIRE(data[0] == EB32(0x04002178));
REQUIRE(data[1] == EB32(0x04082178));
REQUIRE(data[2] == EB32(0x44002178));
REQUIRE(data[3] == EB32(0x44082178));
REQUIRE(data[4] == EB32(0xc4ff2178));
}
TEST_CASE("ppc64: mr/or/std", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.MR(powah::R27, powah::R7);
ctx.MR(powah::R29, powah::R6);
ctx.OR(powah::R3, powah::R3, powah::R4);
ctx.MR(powah::R4, powah::R28);
ctx.CRMOVE(powah::CPR{8}, powah::CPR{1});
ctx.MR(powah::R25, powah::R5);
ctx.OR(powah::R3, powah::R3, powah::R26);
ctx.STD(powah::R3, powah::R1, 56);
ctx.MR(powah::R3, powah::R30);
REQUIRE(data[0] == EB32(0x783bfb7c));
REQUIRE(data[1] == EB32(0x7833dd7c));
REQUIRE(data[2] == EB32(0x7823637c));
REQUIRE(data[3] == EB32(0x78e3847f));
REQUIRE(data[4] == EB32(0x820b014d));
REQUIRE(data[5] == EB32(0x782bb97c));
REQUIRE(data[6] == EB32(0x78d3637c));
REQUIRE(data[7] == EB32(0x380061f8));
REQUIRE(data[8] == EB32(0x78f3c37f));
}
TEST_CASE("ppc64: ld/crand+addi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.LD(powah::R4, powah::R1, 72);
ctx.LWZ(powah::R5, powah::R1, 80);
ctx.CRAND(powah::CPR{20}, powah::CPR{10}, powah::CPR{9});
ctx.ADDI(powah::R6, powah::R4, 4);
ctx.ANDIS_(powah::R4, powah::R5, 1992);
ctx.LD(powah::R5, powah::R30, 1984);
ctx.LBZ(powah::R3, powah::R1, 84);
ctx.CLRLDI(powah::R26, powah::R6, 8);
ctx.STW(powah::R4, powah::R1, 80);
ctx.ADDI(powah::R5, powah::R5, 1);
ctx.STD(powah::R26, powah::R1, 72);
ctx.STD(powah::R5, powah::R30, 1984);
REQUIRE(data[0] == EB32(0x480081e8));
REQUIRE(data[1] == EB32(0x5000a180));
REQUIRE(data[2] == EB32(0x024a8a4e));
REQUIRE(data[3] == EB32(0x0400c438));
REQUIRE(data[4] == EB32(0xc807a474));
REQUIRE(data[5] == EB32(0xc007bee8));
REQUIRE(data[6] == EB32(0x54006188));
REQUIRE(data[7] == EB32(0x0002da78));
REQUIRE(data[8] == EB32(0x50008190));
REQUIRE(data[9] == EB32(0x0100a538));
REQUIRE(data[10] == EB32(0x480041fb));
REQUIRE(data[11] == EB32(0xc007bef8));
}
TEST_CASE("ppc64: rotldi", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.ROTLDI(powah::R5, powah::R3, 16);
ctx.ROTLDI(powah::R4, powah::R3, 8);
ctx.RLDIMI(powah::R4, powah::R5, 8, 48);
ctx.ROTLDI(powah::R5, powah::R3, 24);
ctx.RLDIMI(powah::R4, powah::R5, 16, 40);
ctx.ROTLDI(powah::R5, powah::R3, 32);
ctx.RLDIMI(powah::R4, powah::R5, 24, 32);
ctx.ROTLDI(powah::R5, powah::R3, 48);
ctx.RLDIMI(powah::R4, powah::R5, 40, 16);
ctx.ROTLDI(powah::R5, powah::R3, 56);
ctx.RLDIMI(powah::R4, powah::R5, 48, 8);
ctx.RLDIMI(powah::R4, powah::R3, 56, 0);
ctx.MR(powah::R3, powah::R4);
REQUIRE(data[0] == EB32(0x00806578));
REQUIRE(data[1] == EB32(0x00406478));
REQUIRE(data[2] == EB32(0x2c44a478));
REQUIRE(data[3] == EB32(0x00c06578));
REQUIRE(data[4] == EB32(0x2c82a478));
REQUIRE(data[5] == EB32(0x02006578));
REQUIRE(data[6] == EB32(0x2cc0a478));
REQUIRE(data[7] == EB32(0x02806578));
REQUIRE(data[8] == EB32(0x0e44a478));
REQUIRE(data[9] == EB32(0x02c06578));
REQUIRE(data[10] == EB32(0x0e82a478));
REQUIRE(data[11] == EB32(0x0ec06478));
REQUIRE(data[12] == EB32(0x7823837c));
}
TEST_CASE("ppc64: branch-backwards", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
powah::Label l_3 = ctx.DefineLabel();
ctx.LABEL(l_3);
ctx.ADD(powah::R1, powah::R2, powah::R3);
ctx.B(l_3);
ctx.ApplyRelocs();
REQUIRE(data[0] == EB32(0x141a227c));
REQUIRE(data[1] == EB32(0xfcffff4b));
}
TEST_CASE("ppc64: rlwimi madness", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.ROTLWI(powah::R10, powah::R3, 24);
ctx.SRDI(powah::R9, powah::R3, 32);
ctx.RLWIMI(powah::R10, powah::R3, 8, 8, 15);
ctx.RLWIMI(powah::R10, powah::R3, 8, 24, 31);
ctx.ROTLWI(powah::R3, powah::R9, 24);
ctx.RLWIMI(powah::R3, powah::R9, 8, 8, 15);
ctx.RLWIMI(powah::R3, powah::R9, 8, 24, 31);
ctx.RLDIMI(powah::R3, powah::R10, 32, 0);
REQUIRE(data[0] == EB32(0x3ec06a54));
REQUIRE(data[1] == EB32(0x22006978));
REQUIRE(data[2] == EB32(0x1e426a50));
REQUIRE(data[3] == EB32(0x3e466a50));
REQUIRE(data[4] == EB32(0x3ec02355));
REQUIRE(data[5] == EB32(0x1e422351));
REQUIRE(data[6] == EB32(0x3e462351));
REQUIRE(data[7] == EB32(0x0e004379));
}
/*
0: 78 1b 68 7c mr 8, 3
4: 38 28 83 7c and 3, 4, 5
8: 74 00 6a 7c cntlzd 10, 3
c: 76 06 69 7c sradi 9, 3, 32
10: 82 d1 4a 79 rldicl 10, 10, 58, 6
14: 00 00 29 55 rlwinm 9, 9, 0, 0, 0
18: 64 f0 4a 79 sldi 10, 10, 30
1c: 78 53 29 7d or 9, 9, 10
20: 00 00 28 f9 std 9, 0(8)
24: 20 00 80 4e blr
*/
TEST_CASE("ppc64: functor-2", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
ctx.MR(powah::R8, powah::R3);
ctx.AND(powah::R3, powah::R4, powah::R5);
ctx.CNTLZD(powah::R10, powah::R3);
ctx.SRADI(powah::R9, powah::R3, 32);
ctx.RLDICL(powah::R10, powah::R10, 58, 6);
ctx.RLWINM(powah::R9, powah::R9, 0, 0, 0);
ctx.SLDI(powah::R10, powah::R10, 30);
ctx.OR(powah::R9, powah::R9, powah::R10);
ctx.STD(powah::R9, powah::R8, 0);
ctx.BLR();
REQUIRE(data[0] == EB32(0x781b687c));
REQUIRE(data[1] == EB32(0x3828837c));
REQUIRE(data[2] == EB32(0x74006a7c));
REQUIRE(data[3] == EB32(0x7606697c));
REQUIRE(data[4] == EB32(0x82d14a79));
REQUIRE(data[5] == EB32(0x00002955));
REQUIRE(data[6] == EB32(0x64f04a79));
REQUIRE(data[7] == EB32(0x7853297d));
REQUIRE(data[8] == EB32(0x000028f9));
REQUIRE(data[9] == EB32(0x2000804e));
}
TEST_CASE("ppc64: functor-1", "[ppc64]") {
std::vector<uint32_t> data(64);
powah::Context ctx(data.data(), data.size());
powah::Label l_4 = ctx.DefineLabel();
powah::Label l_7 = ctx.DefineLabel();
ctx.CMPLD(powah::CR0, powah::R3, powah::R4);
ctx.MR(powah::R9, powah::R3);
ctx.BGT(powah::CR0, l_7);
ctx.CMPLDI(powah::CR0, powah::R3, 3781);
ctx.LI(powah::R3, 1);
ctx.BGTLR(powah::CR0);
ctx.CMPLD(powah::CR0, powah::R9, powah::R5);
ctx.BLE(powah::CR0, l_4);
ctx.SUBF(powah::R5, powah::R9, powah::R5);
ctx.XOR(powah::R3, powah::R5, powah::R4);
ctx.BLR();
ctx.LABEL(l_4);
ctx.ADD(powah::R5, powah::R4, powah::R5);
ctx.ADD(powah::R3, powah::R5, powah::R9);
ctx.BLR();
ctx.LABEL(l_7);
ctx.ADD(powah::R3, powah::R4, powah::R5);
ctx.BLR();
ctx.ApplyRelocs();
REQUIRE(data[0] == EB32(0x4020237c));
REQUIRE(data[1] == EB32(0x781b697c));
REQUIRE(data[2] == EB32(0x30008141));
REQUIRE(data[3] == EB32(0xc50e2328));
REQUIRE(data[4] == EB32(0x01006038));
REQUIRE(data[5] == EB32(0x2000814d));
REQUIRE(data[6] == EB32(0x4028297c));
REQUIRE(data[7] == EB32(0x10008140));
REQUIRE(data[8] == EB32(0x5028a97c));
REQUIRE(data[9] == EB32(0x7822a37c));
REQUIRE(data[10] == EB32(0x2000804e));
REQUIRE(data[11] == EB32(0x142aa47c));
REQUIRE(data[12] == EB32(0x144a657c));
REQUIRE(data[13] == EB32(0x2000804e));
REQUIRE(data[14] == EB32(0x142a647c));
REQUIRE(data[15] == EB32(0x2000804e));
}
#if defined(ARCHITECTURE_powerpc64)
/*
0: d619637c mullw 3, 3, 3
4: 20006378 clrldi 3, 3, 32
8: 2000804e blr
*/
TEST_CASE("ppc64: live-exec square", "[ppc64]") {
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
REQUIRE(data != nullptr);
powah::Context ctx((void*)data, 4096);
ctx.MULLW(powah::R3, powah::R3, powah::R3);
ctx.CLRLDI(powah::R3, powah::R3, 32);
ctx.BLR();
REQUIRE(data[0] == EB32(0xd619637c));
REQUIRE(data[1] == EB32(0x20006378));
REQUIRE(data[2] == EB32(0x2000804e));
auto* fn = (int (*)(int))data;
REQUIRE(fn(4) == 4 * 4);
REQUIRE(fn(8) == 8 * 8);
munmap((void*)data, 4096);
}
/*
int f(int a, int b, int c) {
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
return a * a + b ^ c & b;
}
*/
TEST_CASE("ppc64: live-exec xoralu", "[ppc64]") {
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
REQUIRE(data != nullptr);
powah::Context ctx((void*)data, 4096);
powah::Label l_2 = ctx.DefineLabel();
powah::Label l_3 = ctx.DefineLabel();
ctx.CMPW(powah::R3, powah::R4);
ctx.BGT(powah::CR0, l_2);
ctx.ADDI(powah::R6, powah::R5, 402);
ctx.B(l_3);
ctx.LABEL(l_2);
ctx.ADDI(powah::R6, powah::R4, 1);
ctx.ADDIS(powah::R6, powah::R6, -1);
ctx.LABEL(l_3);
ctx.ADDI(powah::R4, powah::R4, 2);
ctx.ADD(powah::R3, powah::R6, powah::R3);
ctx.MULLW(powah::R6, powah::R4, powah::R5);
ctx.MULLW(powah::R3, powah::R3, powah::R3);
ctx.AND(powah::R5, powah::R5, powah::R6);
ctx.ADD(powah::R3, powah::R3, powah::R4);
ctx.AND(powah::R4, powah::R5, powah::R4);
ctx.XOR(powah::R3, powah::R3, powah::R4);
ctx.EXTSW(powah::R3, powah::R3);
ctx.BLR();
ctx.ApplyRelocs();
REQUIRE(data[0] == EB32(0x0020037c));
REQUIRE(data[1] == EB32(0x0c008141));
REQUIRE(data[2] == EB32(0x9201c538));
REQUIRE(data[3] == EB32(0x0c000048));
REQUIRE(data[4] == EB32(0x0100c438));
REQUIRE(data[5] == EB32(0xffffc63c));
REQUIRE(data[6] == EB32(0x02008438));
REQUIRE(data[7] == EB32(0x141a667c));
REQUIRE(data[8] == EB32(0xd629c47c));
REQUIRE(data[9] == EB32(0xd619637c));
REQUIRE(data[10] == EB32(0x3830a57c));
REQUIRE(data[11] == EB32(0x1422637c));
REQUIRE(data[12] == EB32(0x3820a47c));
REQUIRE(data[13] == EB32(0x7822637c));
REQUIRE(data[14] == EB32(0xb407637c));
REQUIRE(data[15] == EB32(0x2000804e));
auto const orig = [](int a, int b, int c) {
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
return a * a + b ^ c & b;
};
auto* fn = (int (*)(int, int, int))data;
REQUIRE(fn(0, 1, 2) == orig(0, 1, 2));
REQUIRE(fn(6456, 4564, 4564561) == orig(6456, 4564, 4564561));
munmap((void*)data, 4096);
}
#endif
+1 -1
View File
@@ -8,7 +8,7 @@
include_directories(.)
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64 OR ARCHITECTURE_powerpc64) AND NOT YUZU_STATIC_ROOM)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()
+1
View File
@@ -108,6 +108,7 @@ add_library(
settings_input.h
settings_setting.h
slot_vector.h
socket_types.h
spin_lock.h
stb.cpp
stb.h
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -11,7 +11,7 @@
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "web_service/web_result.h"
namespace AnnounceMultiplayerRoom {
+178
View File
@@ -0,0 +1,178 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
namespace Network {
/// Address families
enum class Domain : u8 {
Unspecified, ///< Represents 0, used in getaddrinfo hints
INET, ///< Address family for IPv4
};
/// Socket types
enum class Type {
Unspecified, ///< Represents 0, used in getaddrinfo hints
STREAM,
DGRAM,
RAW,
SEQPACKET,
};
/// Protocol values for sockets
enum class Protocol : u8 {
Unspecified, ///< Represents 0, usable in various places
IP,
ICMP,
TCP,
UDP,
IPV6,
RAW,
IGMP,
GGP,
IPV4,
ST,
EGP,
PIGP,
RCCMON,
NVPII,
PUP,
ARGUS,
EMCON,
XNET,
CHAOS,
MUX,
MEAS,
HMP,
PRM,
IDP,
TRUNK1,
TRUNK2,
LEAF1,
LEAF2,
RDP,
IRTP,
TP,
BLT,
NSP,
INP,
DCCP,
//TODO: 3PC,
IDPR,
XTP,
DDP,
CMTP,
TPXX,
IL,
SDRP,
ROUTING,
FRAGMENT,
IDRP,
RSVP,
GRE,
MHRP,
BHA,
ESP,
AH,
INLSP,
SWIPE,
NHRP,
MOBILE,
TLSP,
SKIP,
ICMPV6,
NONE,
DSTOPTS,
AHIP,
CFTP,
HELLO,
SATEXPAK,
KRYPTOLAN,
RVD,
IPPC,
ADFS,
SATMON,
VISA,
IPCV,
CPNX,
CPHB,
WSN,
PVP,
BRSATMON,
ND,
WBMON,
WBEXPAK,
EON,
VMTP,
SVMTP,
VINES,
TTP,
IGP,
DGP,
TCF,
IGRP,
OSPFIGP,
SRPC,
LARP,
MTP,
AX25,
IPEIP,
MICP,
SCCSP,
ETHERIP,
ENCAP,
APES,
GMTP,
IPCOMP,
SCTP,
MH,
UDPLITE,
HIP,
SHIM6,
PIM,
CARP,
PGM,
MPLS,
PFSYNC
};
/// Shutdown mode
enum class ShutdownHow {
RD,
WR,
RDWR,
};
/// Array of IPv4 address
using IPv4Address = std::array<u8, 4>;
/// Cross-platform sockaddr structure
struct SockAddrIn {
Domain family;
IPv4Address ip;
u16 portno;
};
constexpr u32 FLAG_MSG_PEEK = 0x2;
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
constexpr u32 FLAG_O_NONBLOCK = 0x800;
/// Cross-platform addrinfo structure
struct AddrInfo {
Domain family;
Type socket_type;
Protocol protocol;
SockAddrIn addr;
std::optional<std::string> canon_name;
};
} // namespace Network
+1 -4
View File
@@ -1133,9 +1133,6 @@ add_library(core STATIC
internal_network/network_interface.h
internal_network/socket_proxy.cpp
internal_network/socket_proxy.h
internal_network/socket_icmp.cpp
internal_network/socket_icmp.h
internal_network/socket_types.h
internal_network/sockets.h
internal_network/wifi_scanner.h
launch_timestamp_cache.cpp
@@ -1254,7 +1251,7 @@ if (HAS_NCE)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_powerpc64 OR ARCHITECTURE_powerpc64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp
+1 -1
View File
@@ -18,7 +18,7 @@
#include <ankerl/unordered_dense.h>
#include "common/logging.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/hle/result.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h"
+8 -7
View File
@@ -129,13 +129,6 @@ ServerManager::~ServerManager() {
}
}
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
for (size_t i = 0; i < num_threads; i++) {
auto thread_name = fmt::format("{}:{}", name, i + 1);
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
}
}
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
server_manager->m_system.RunServer(std::move(server_manager));
}
@@ -252,6 +245,14 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
R_SUCCEED();
}
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
for (size_t i = 0; i < num_threads; i++) {
auto thread_name = fmt::format("{}:{}", name, i + 1);
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
}
}
Result ServerManager::LoopProcess() {
SCOPE_EXIT {
m_stopped.Set();
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -51,8 +48,8 @@ public:
Result ManageDeferral(Kernel::KEvent** out_event);
Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads);
static void RunServer(std::unique_ptr<ServerManager>&& server);
private:
+273 -217
View File
@@ -12,7 +12,7 @@
#include <fmt/ranges.h>
#include "common/logging.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/core.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/ipc_helpers.h"
@@ -20,7 +20,6 @@
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h"
#include "core/internal_network/socket_proxy.h"
#include "core/internal_network/socket_icmp.h"
#include "core/internal_network/sockets.h"
#include "network/network.h"
#include <common/settings.h>
@@ -29,11 +28,11 @@ namespace Service::Sockets {
namespace {
bool IsConnectionBased(Network::Type type) {
bool IsConnectionBased(Type type) {
switch (type) {
case Network::Type::STREAM:
case Type::STREAM:
return true;
case Network::Type::DGRAM:
case Type::DGRAM:
return false;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
@@ -93,7 +92,7 @@ void BSD::ConnectWork::Execute(BSD* bsd) {
void BSD::ConnectWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Network::Errno::SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
@@ -171,8 +170,12 @@ void BSD::Socket(HLERequestContext& ctx) {
const u32 domain = rp.Pop<u32>();
const u32 type = rp.Pop<u32>();
const u32 protocol = rp.Pop<u32>();
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type),
static_cast<Protocol>(protocol));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(fd);
@@ -243,13 +246,13 @@ void BSD::GetPeerName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Network::Errno::SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -261,13 +264,13 @@ void BSD::GetSockName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Network::Errno::SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -275,19 +278,21 @@ void BSD::GetSockName(HLERequestContext& ctx) {
void BSD::GetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const auto level = Network::SocketLevel(rp.Pop<u32>());
const auto optname = Network::OptName(rp.Pop<u32>());
const u32 level = rp.Pop<u32>();
const auto optname = static_cast<OptName>(rp.Pop<u32>());
std::vector<u8> optval(ctx.GetWriteBufferSize());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
optval.size());
const Errno err = GetSockOptImpl(fd, level, optname, optval);
ctx.WriteBuffer(optval);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(err == Network::Errno::SUCCESS ? 0 : -1);
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(err);
rb.Push<u32>(static_cast<u32>(optval.size()));
}
@@ -310,7 +315,7 @@ void BSD::Fcntl(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -322,11 +327,13 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
const u32 level = rp.Pop<u32>();
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
const auto optval = ctx.ReadBuffer();
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
static_cast<u32>(optname), optval.size());
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
}
@@ -447,7 +454,7 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
struct OutputParameters {
s32 ret;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x8);
@@ -461,10 +468,10 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
if (auto* res = std::get_if<s32>(&res_v)) {
rb.PushRaw(OutputParameters{
.ret = *res,
.bsd_errno = Network::Errno::SUCCESS,
.bsd_errno = Errno::SUCCESS,
});
} else {
auto* err = std::get_if<Network::Errno>(&res_v);
auto* err = std::get_if<Errno>(&res_v);
rb.PushRaw(OutputParameters{
.ret = 0,
.bsd_errno = *err,
@@ -479,7 +486,7 @@ void BSD::EventFd(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
BuildErrnoResponse(ctx, Network::Errno::SUCCESS);
BuildErrnoResponse(ctx, Errno::SUCCESS);
}
template <typename Work>
@@ -488,130 +495,130 @@ void BSD::ExecuteWork(HLERequestContext& ctx, Work work) {
work.Response(ctx);
}
std::pair<s32, Network::Errno> BSD::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
if (type == Network::Type::SEQPACKET) {
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
if (type == Type::SEQPACKET) {
UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management");
} else if (type == Network::Type::RAW && (domain != Network::Domain::INET || protocol != Network::Protocol::ICMP)) {
} else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) {
UNIMPLEMENTED_MSG("SOCK_RAW errno management");
}
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
type = Network::Type(u32(type) & ~0x20000000);
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
const s32 fd = FindFreeFileDescriptorHandle();
if (fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Network::Errno::MFILE};
}
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
file_descriptors[fd].reset();
return {-1, Network::Errno::NOTCONN};
return {-1, Errno::MFILE};
}
file_descriptors[fd] = FileDescriptor{};
FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
// While room is important -- we need to remember ICMP takes priority over **everything else**
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
// to browse the internet while playing LDN or something stupid like that?
LOG_INFO(Service, "New socket fd={}", fd);
auto room_member = Network::GetRoomMember().lock();
if (protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6) {
descriptor.socket = std::make_shared<Network::IcmpSocket>();
} else if (room_member && room_member->IsConnected()) {
if (room_member && room_member->IsConnected()) {
descriptor.socket = std::make_shared<Network::ProxySocket>();
} else {
descriptor.socket = std::make_shared<Network::Socket>();
}
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
descriptor.is_connection_based = IsConnectionBased(type);
#ifdef _WIN32
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
#else
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
#endif
file_descriptors[fd].reset();
return {-1, bsd_errno};
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
return {-1, Errno::NOTCONN};
}
return {fd, Network::Errno::SUCCESS};
return {fd, Errno::SUCCESS};
}
std::pair<s32, Network::Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout) {
if (nfds <= 0) {
// When no entries are provided, -1 is returned with errno zero
return {-1, Network::Errno::SUCCESS};
return {-1, Errno::SUCCESS};
}
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::INVAL};
if (read_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::INVAL};
if (write_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
std::vector<PollFD> fds(nfds);
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
if (timeout >= 0) {
const s64 seconds = timeout / 1000;
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
if (seconds < 0) {
return {-1, Network::Errno::INVAL};
return {-1, Errno::INVAL};
}
if (nanoseconds > 999'999'999) {
return {-1, Network::Errno::INVAL};
return {-1, Errno::INVAL};
}
} else if (timeout != -1) {
return {-1, Network::Errno::INVAL};
return {-1, Errno::INVAL};
}
for (size_t i = 0; i < in_fds.size(); ++i) {
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
if (!IsFileDescriptorValid(in_fds[i].fd)) {
out_fds[i].revents = {};
if (!file_descriptors[in_fds[i].fd])
out_fds[i].revents = Network::PollEvents::NVAL;
return {0, Network::Errno::SUCCESS};
for (PollFD& pollfd : fds) {
ASSERT(False(pollfd.revents));
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
pollfd.revents = PollEvents{};
return {0, Errno::SUCCESS};
}
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
if (!descriptor) {
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
pollfd.revents = PollEvents::Nval;
return {0, Errno::SUCCESS};
}
}
std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
Network::HostPollFD result{};
result.socket = file_descriptors[e.fd]->socket.get();
result.events = e.events;
result.revents = {};
std::vector<Network::PollFD> host_pollfds(fds.size());
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
Network::PollFD result;
result.socket = file_descriptors[pollfd.fd]->socket.get();
result.events = Translate(pollfd.events);
result.revents = Network::PollEvents{};
return result;
});
auto const res = Network::Poll(host_pollfds, timeout);
for (size_t i = 0; i < in_fds.size(); ++i)
out_fds[i].revents = host_pollfds[i].revents;
return res;
const auto result = Network::Poll(host_pollfds, timeout);
const size_t num = host_pollfds.size();
for (size_t i = 0; i < num; ++i) {
fds[i].revents = Translate(host_pollfds[i].revents);
}
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
return Translate(result);
}
std::pair<s32, Network::Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Network::Errno::MFILE};
return {-1, Errno::MFILE};
}
FileDescriptor& descriptor = *file_descriptors[fd];
auto [result, bsd_errno] = descriptor.socket->Accept();
if (bsd_errno != Network::Errno::SUCCESS) {
return {-1, bsd_errno};
return {-1, Translate(bsd_errno)};
}
file_descriptors[new_fd] = FileDescriptor{};
@@ -619,218 +626,267 @@ std::pair<s32, Network::Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_bu
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
PutValue(write_buffer, result.sockaddr_in);
return {new_fd, Network::Errno::SUCCESS};
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
PutValue(write_buffer, guest_addr_in);
return {new_fd, Errno::SUCCESS};
}
Network::Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
auto addr_in = GetValue<Network::SockAddrIn>(addr);
return file_descriptors[fd]->socket->Bind(addr_in);
auto addr_in = GetValue<SockAddrIn>(addr);
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
}
Network::Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
auto addr_in = GetValue<Network::SockAddrIn>(addr);
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
if (result == Network::Errno::ISCONN) {
auto addr_in = GetValue<SockAddrIn>(addr);
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
if (result == Errno::ISCONN) {
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
return Network::Errno::SUCCESS;
return Errno::SUCCESS;
}
return result;
}
Network::Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
if (bsd_errno != Network::Errno::SUCCESS) {
return bsd_errno;
return Translate(bsd_errno);
}
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
}
Network::Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
if (bsd_errno != Network::Errno::SUCCESS) {
return bsd_errno;
return Translate(bsd_errno);
}
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
}
Network::Errno BSD::ListenImpl(s32 fd, s32 backlog) {
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
Errno BSD::ListenImpl(s32 fd, s32 backlog) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
return file_descriptors[fd]->socket->Listen(backlog);
return Translate(file_descriptors[fd]->socket->Listen(backlog));
}
std::pair<s32, Network::Errno> BSD::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
switch (cmd) {
case Network::FcntlCmd::GETFL:
case FcntlCmd::GETFL:
ASSERT(arg == 0);
return {descriptor.flags, Network::Errno::SUCCESS};
case Network::FcntlCmd::SETFL: {
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
if (bsd_errno != Network::Errno::SUCCESS) {
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {-1, bsd_errno};
}
descriptor.flags = arg;
return {0, Network::Errno::SUCCESS};
return {0, Errno::SUCCESS};
}
default:
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
return {-1, Network::Errno::SUCCESS};
return {-1, Errno::SUCCESS};
}
}
Network::Errno BSD::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
if (level != Network::SocketLevel::SOCKET) {
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
UNIMPLEMENTED_MSG("Unknown getsockopt level");
return Errno::SUCCESS;
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
switch (optname) {
case Network::OptName::ERROR_: {
case OptName::ERROR_: {
auto [pending_err, getsockopt_err] = socket->GetPendingError();
if (getsockopt_err == Network::Errno::SUCCESS) {
Errno translated_pending_err = Translate(pending_err);
ASSERT_OR_EXECUTE_MSG(
optval.size() == sizeof(Network::Errno), { return Network::Errno::INVAL; },
optval.size() == sizeof(Errno), { return Errno::INVAL; },
"Incorrect getsockopt option size");
optval.resize(sizeof(Network::Errno));
PutValue(optval, pending_err);
optval.resize(sizeof(Errno));
PutValue(optval, translated_pending_err);
}
return getsockopt_err;
return Translate(getsockopt_err);
}
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Network::Errno::SUCCESS;
return Errno::SUCCESS;
}
}
Network::Errno BSD::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
return Errno::SUCCESS;
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
return socket->SetSockOpt(level, optname, optval);
if (optname == OptName::LINGER) {
ASSERT(optval.size() == sizeof(Linger));
auto linger = GetValue<Linger>(optval);
ASSERT(linger.onoff == 0 || linger.onoff == 1);
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
}
ASSERT(optval.size() == sizeof(u32));
auto value = GetValue<u32>(optval);
switch (optname) {
case OptName::REUSEADDR:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
case OptName::KEEPALIVE:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetKeepAlive(value != 0));
case OptName::BROADCAST:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
case OptName::SNDBUF:
return Translate(socket->SetSndBuf(value));
case OptName::RCVBUF:
return Translate(socket->SetRcvBuf(value));
case OptName::SNDTIMEO:
return Translate(socket->SetSndTimeo(value));
case OptName::RCVTIMEO:
return Translate(socket->SetRcvTimeo(value));
case OptName::NOSIGPIPE:
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
return Errno::SUCCESS;
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Errno::SUCCESS;
}
}
Network::Errno BSD::ShutdownImpl(s32 fd, s32 how) {
LOG_DEBUG(Network, "fd={},how={}", fd, how);
Errno BSD::ShutdownImpl(s32 fd, s32 how) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
}
std::pair<s32, Network::Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
// Apply flags
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
// Restore original state
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(false);
}
return {ret, bsd_errno};
}
std::pair<s32, Network::Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
@@ -845,17 +901,19 @@ std::pair<s32, Network::Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<
}
// Apply flags
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
// Restore original state
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(false);
}
@@ -864,59 +922,58 @@ std::pair<s32, Network::Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<
addr.clear();
} else {
ASSERT(addr.size() >= 16);
PutValue(addr, addr_in);
const SockAddrIn result = Translate(addr_in);
PutValue(addr, result);
}
}
return {ret, bsd_errno};
}
std::pair<s32, Network::Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
return file_descriptors[fd]->socket->Send(message, flags);
return Translate(file_descriptors[fd]->socket->Send(message, flags));
}
std::pair<s32, Network::Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::BADF};
return {-1, Errno::BADF};
}
Network::SockAddrIn addr_in{};
Network::SockAddrIn addr_in;
Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) {
ASSERT(addr.size() >= 16);
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
addr_in = guest_addr_in;
auto guest_addr_in = GetValue<SockAddrIn>(addr);
addr_in = Translate(guest_addr_in);
p_addr_in = &addr_in;
}
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
}
Network::Errno BSD::CloseImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD::CloseImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::BADF;
return Errno::BADF;
}
auto const bsd_errno = file_descriptors[fd]->socket->Close();
if (bsd_errno != Network::Errno::SUCCESS) {
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
if (bsd_errno != Errno::SUCCESS) {
return bsd_errno;
}
@@ -926,16 +983,15 @@ Network::Errno BSD::CloseImpl(s32 fd) {
return bsd_errno;
}
std::variant<s32, Network::Errno> BSD::DuplicateSocketImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::BADF;
return Errno::BADF;
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (!IsFileDescriptorValid(new_fd)) {
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return Network::Errno::MFILE;
return Errno::MFILE;
}
file_descriptors[new_fd] = FileDescriptor{
@@ -947,7 +1003,6 @@ std::variant<s32, Network::Errno> BSD::DuplicateSocketImpl(s32 fd) {
}
std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return std::nullopt;
}
@@ -959,40 +1014,41 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
}
s32 BSD::FindFreeFileDescriptorHandle() noexcept {
// first three file descriptors are reserved for:
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
if (!file_descriptors[fd])
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
if (!file_descriptors[fd]) {
return fd;
}
}
return -1;
}
bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
if (fd < 0 || fd >= s32(file_descriptors.size())) {
LOG_ERROR(Service, "Invalid handle={}", fd);
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
return false;
}
if (!file_descriptors[fd]) {
LOG_ERROR(Service, "handle={} is not allocated", fd);
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
return false;
}
return true;
}
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Network::Errno::SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
for (auto& optional_descriptor : file_descriptors) {
if (optional_descriptor.has_value()) {
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
if (!optional_descriptor.has_value()) {
continue;
}
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
}
}
+27 -27
View File
@@ -11,7 +11,7 @@
#include <variant>
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
#include "network/network.h"
@@ -35,8 +35,8 @@ public:
// These methods are called from SSL; the first two are also called from
// this class for the corresponding IPC methods.
// On the real device, the SSL service makes IPC calls to this service.
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
Network::Errno CloseImpl(s32 fd);
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
Errno CloseImpl(s32 fd);
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
private:
@@ -58,7 +58,7 @@ private:
std::span<const u8> read_buffer;
std::vector<u8> write_buffer;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct AcceptWork {
@@ -68,7 +68,7 @@ private:
s32 fd;
std::vector<u8> write_buffer;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct ConnectWork {
@@ -77,7 +77,7 @@ private:
s32 fd;
std::span<const u8> addr;
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct RecvWork {
@@ -88,7 +88,7 @@ private:
u32 flags;
std::vector<u8> message;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct RecvFromWork {
@@ -100,7 +100,7 @@ private:
std::vector<u8> message;
std::vector<u8> addr;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct SendWork {
@@ -111,7 +111,7 @@ private:
u32 flags;
std::span<const u8> message;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct SendToWork {
@@ -123,7 +123,7 @@ private:
std::span<const u8> message;
std::span<const u8> addr;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
void RegisterClient(HLERequestContext& ctx);
@@ -154,30 +154,30 @@ private:
template <typename Work>
void ExecuteWork(HLERequestContext& ctx, Work work);
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout);
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
Network::Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Errno BindImpl(s32 fd, std::span<const u8> addr);
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr);
std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr);
s32 FindFreeFileDescriptorHandle() noexcept;
bool IsFileDescriptorValid(s32 fd) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
+44 -40
View File
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
}
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case Network::GetAddrInfoError::SUCCESS:
case GetAddrInfoError::SUCCESS:
return NetDbError::Success;
case Network::GetAddrInfoError::AGAIN:
case GetAddrInfoError::AGAIN:
return NetDbError::TryAgain;
case Network::GetAddrInfoError::NODATA:
case GetAddrInfoError::NODATA:
return NetDbError::HostNotFound;
case Network::GetAddrInfoError::SERVICE:
case GetAddrInfoError::SERVICE:
return NetDbError::Success;
default:
return NetDbError::HostNotFound;
}
}
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case Network::GetAddrInfoError::SUCCESS:
case GetAddrInfoError::SUCCESS:
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
// some reason, but that doesn't seem useful to implement.
return Network::Errno::SUCCESS;
case Network::GetAddrInfoError::AGAIN:
return Network::Errno::SUCCESS;
case Network::GetAddrInfoError::NODATA:
return Network::Errno::SUCCESS;
case Network::GetAddrInfoError::SERVICE:
return Network::Errno::INVAL;
return Errno::SUCCESS;
case GetAddrInfoError::AGAIN:
return Errno::SUCCESS;
case GetAddrInfoError::NODATA:
return Errno::SUCCESS;
case GetAddrInfoError::SERVICE:
return Errno::INVAL;
default:
return Network::Errno::SUCCESS;
return Errno::SUCCESS;
}
}
@@ -155,7 +155,9 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
// doesn't implement h_errno.
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
std::vector<u8> data;
// h_name: use the input hostname (append nul-terminated)
AppendNulTerminated(data, host);
@@ -163,12 +165,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
Append<u32_be>(data, 0); // count of h_aliases
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
// h_addr_list:
size_t count = vec.size();
ASSERT(count <= UINT32_MAX);
Append<u32_be>(data, u32(count));
Append<u32_be>(data, static_cast<uint32_t>(count));
for (const Network::AddrInfo& addrinfo : vec) {
// On the Switch, this is passed through htonl despite already being
// big-endian, so it ends up as little-endian.
@@ -180,7 +182,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
return data;
}
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -203,7 +205,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, Network::GetAddrInfoError::AGAIN};
return {0, GetAddrInfoError::AGAIN};
}
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
@@ -211,10 +213,10 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, Network::GetAddrInfoError::SUCCESS};
return {data_size, GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, *err};
return {0, Translate(*err)};
}
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
@@ -222,7 +224,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
struct OutputParameters {
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -242,7 +244,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -255,23 +257,24 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
});
}
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
// Adapted from
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
std::vector<u8> data;
for (const Network::AddrInfo& addrinfo : vec) {
// serialized addrinfo:
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
Append<u32_be>(data, 16); // ai_addrlen
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
// ai_addr:
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
// On the Switch, the following fields are passed through htonl despite
// already being big-endian, so they end up as little-endian.
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
@@ -293,7 +296,7 @@ static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec,
return data;
}
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -318,7 +321,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, Network::GetAddrInfoError::AGAIN};
return {0, GetAddrInfoError::AGAIN};
}
std::optional<std::string> service = std::nullopt;
@@ -328,23 +331,24 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
}
// Serialized hints are also passed in a buffer, but are ignored for now.
auto res_v = Network::GetAddressInfo(host, service);
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
const std::vector<u8> data = SerializeAddrInfo(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, Network::GetAddrInfoError::SUCCESS};
return {data_size, GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, *err};
return {0, Translate(*err)};
}
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
struct OutputParameters {
Network::Errno bsd_errno;
Network::GetAddrInfoError gai_error;
Errno bsd_errno;
GetAddrInfoError gai_error;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -360,7 +364,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
struct InputParameters {
Network::GetAddrInfoError gai_errno;
GetAddrInfoError gai_errno;
};
IPC::RequestParser rp{ctx};
auto input = rp.PopRaw<InputParameters>();
@@ -378,9 +382,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
Network::GetAddrInfoError gai_error;
GetAddrInfoError gai_error;
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x10);
+1 -3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,6 +11,7 @@ namespace Service::Sockets {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD>(system, "bsd:s"));
server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD>(system, "bsd:u"));
server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system));
+236 -1
View File
@@ -8,7 +8,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
namespace Core {
class System;
@@ -16,6 +15,242 @@ class System;
namespace Service::Sockets {
enum class Errno : u32 {
SUCCESS = 0,
BADF = 9,
AGAIN = 11,
INVAL = 22,
MFILE = 24,
PIPE = 32,
MSGSIZE = 90,
CONNABORTED = 103,
CONNRESET = 104,
NOTCONN = 107,
TIMEDOUT = 110,
CONNREFUSED = 111,
INPROGRESS = 115,
ISCONN = 106,
};
enum class GetAddrInfoError : s32 {
SUCCESS = 0,
ADDRFAMILY = 1,
AGAIN = 2,
BADFLAGS = 3,
FAIL = 4,
FAMILY = 5,
MEMORY = 6,
NODATA = 7,
NONAME = 8,
SERVICE = 9,
SOCKTYPE = 10,
SYSTEM = 11,
BADHINTS = 12,
PROTOCOL = 13,
OVERFLOW_ = 14, // avoid name collision with Windows macro
OTHER = 15,
};
enum class Domain : u32 {
Unspecified = 0,
INET = 2,
};
enum class Type : u32 {
Unspecified = 0,
STREAM = 1,
DGRAM = 2,
RAW = 3,
SEQPACKET = 5,
};
enum class Protocol : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
//
IPV6 = 41,
RAW = 255,
//
HOPOPTS = 0,
IGMP = 2,
GGP = 3,
IPV4 = 4,
ST = 7,
EGP = 8,
PIGP = 9,
RCCMON = 10,
NVPII = 11,
PUP = 12,
ARGUS = 13,
EMCON = 14,
XNET = 15,
CHAOS = 16,
MUX = 18,
MEAS = 19,
HMP = 20,
PRM = 21,
IDP = 22,
TRUNK1 = 23,
TRUNK2 = 24,
LEAF1 = 25,
LEAF2 = 26,
RDP = 27,
IRTP = 28,
TP = 29,
BLT = 30,
NSP = 31,
INP = 32,
DCCP = 33,
//3PC = 34,
IDPR = 35,
XTP = 36,
DDP = 37,
CMTP = 38,
TPXX = 39,
IL = 40,
SDRP = 42,
ROUTING = 43,
FRAGMENT = 44,
IDRP = 45,
RSVP = 46,
GRE = 47,
MHRP = 48,
BHA = 49,
ESP = 50,
AH = 51,
INLSP = 52,
SWIPE = 53,
NHRP = 54,
MOBILE = 55,
TLSP = 56,
SKIP = 57,
ICMPV6 = 58,
NONE = 59,
DSTOPTS = 60,
AHIP = 61,
CFTP = 62,
HELLO = 63,
SATEXPAK = 64,
KRYPTOLAN = 65,
RVD = 66,
IPPC = 67,
ADFS = 68,
SATMON = 69,
VISA = 70,
IPCV = 71,
CPNX = 72,
CPHB = 73,
WSN = 74,
PVP = 75,
BRSATMON = 76,
ND = 77,
WBMON = 78,
WBEXPAK = 79,
EON = 80,
VMTP = 81,
SVMTP = 82,
VINES = 83,
TTP = 84,
IGP = 85,
DGP = 86,
TCF = 87,
IGRP = 88,
OSPFIGP = 89,
SRPC = 90,
LARP = 91,
MTP = 92,
AX25 = 93,
IPEIP = 94,
MICP = 95,
SCCSP = 96,
ETHERIP = 97,
ENCAP = 98,
APES = 99,
GMTP = 100,
IPCOMP = 108,
SCTP = 132,
MH = 135,
UDPLITE = 136,
HIP = 139,
SHIM6 = 140,
PIM = 103,
CARP = 112,
PGM = 113,
MPLS = 137,
PFSYNC = 240,
};
enum class SocketLevel : u32 {
IP = 0,
TCP = 6,
SOCKET = 0xffff, // i.e. SOL_SOCKET
};
enum class OptName : u32 {
REUSEADDR = 0x4,
KEEPALIVE = 0x8,
BROADCAST = 0x20,
LINGER = 0x80,
SNDBUF = 0x1001,
RCVBUF = 0x1002,
SNDTIMEO = 0x1005,
RCVTIMEO = 0x1006,
ERROR_ = 0x1007, // avoid name collision with Windows macro
NOSIGPIPE = 0x800, // at least according to libnx
ACCEPTFILTER = 0x1000,
BINTIME = 0x2000,
NO_OFFLOAD = 0x4000,
NO_DDP = 0x8000,
};
enum class ShutdownHow : s32 {
RD = 0,
WR = 1,
RDWR = 2,
};
enum class FcntlCmd : s32 {
GETFL = 3,
SETFL = 4,
};
struct SockAddrIn {
u8 len;
u8 family;
u16 portno;
std::array<u8, 4> ip;
std::array<u8, 248> zeroes;
};
static_assert(sizeof(SockAddrIn) == 0x100);
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
In = 1 << 0,
Pri = 1 << 1,
Out = 1 << 2,
Err = 1 << 3,
Hup = 1 << 4,
Nval = 1 << 5,
RdNorm = 1 << 6,
RdBand = 1 << 7,
WrBand = 1 << 8,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
s32 fd;
PollEvents events;
PollEvents revents;
};
struct Linger {
u32 onoff;
u32 linger;
};
void LoopProcess(Core::System& system);
} // namespace Service::Sockets
@@ -15,42 +15,388 @@
namespace Service::Sockets {
const char* Translate(Network::GetAddrInfoError error) {
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
Errno Translate(Network::Errno value) {
switch (value) {
case Network::Errno::SUCCESS:
return Errno::SUCCESS;
case Network::Errno::BADF:
return Errno::BADF;
case Network::Errno::AGAIN:
return Errno::AGAIN;
case Network::Errno::INVAL:
return Errno::INVAL;
case Network::Errno::MFILE:
return Errno::MFILE;
case Network::Errno::PIPE:
return Errno::PIPE;
case Network::Errno::CONNREFUSED:
return Errno::CONNREFUSED;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
case Network::Errno::TIMEDOUT:
return Errno::TIMEDOUT;
case Network::Errno::CONNABORTED:
return Errno::CONNABORTED;
case Network::Errno::CONNRESET:
return Errno::CONNRESET;
case Network::Errno::INPROGRESS:
return Errno::INPROGRESS;
case Network::Errno::ISCONN:
return Errno::ISCONN;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
return Errno::SUCCESS;
}
}
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
return {value.first, Translate(value.second)};
}
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
switch (error) {
case Network::GetAddrInfoError::SUCCESS:
return "Success";
return GetAddrInfoError::SUCCESS;
case Network::GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported";
return GetAddrInfoError::ADDRFAMILY;
case Network::GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution";
return GetAddrInfoError::AGAIN;
case Network::GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags";
return GetAddrInfoError::BADFLAGS;
case Network::GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution";
return GetAddrInfoError::FAIL;
case Network::GetAddrInfoError::FAMILY:
return "ai_family not supported";
return GetAddrInfoError::FAMILY;
case Network::GetAddrInfoError::MEMORY:
return "Memory allocation failure";
return GetAddrInfoError::MEMORY;
case Network::GetAddrInfoError::NODATA:
return "No address associated with hostname";
return GetAddrInfoError::NODATA;
case Network::GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known";
return GetAddrInfoError::NONAME;
case Network::GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype";
return GetAddrInfoError::SERVICE;
case Network::GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported";
return GetAddrInfoError::SOCKTYPE;
case Network::GetAddrInfoError::SYSTEM:
return "System error returned in errno";
return GetAddrInfoError::SYSTEM;
case Network::GetAddrInfoError::BADHINTS:
return "Invalid value for hints";
return GetAddrInfoError::BADHINTS;
case Network::GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown";
return GetAddrInfoError::PROTOCOL;
case Network::GetAddrInfoError::OVERFLOW_:
return GetAddrInfoError::OVERFLOW_;
case Network::GetAddrInfoError::OTHER:
return GetAddrInfoError::OTHER;
default:
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
return GetAddrInfoError::OTHER;
}
}
const char* Translate(GetAddrInfoError error) {
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
switch (error) {
case GetAddrInfoError::SUCCESS:
return "Success";
case GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported";
case GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution";
case GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags";
case GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution";
case GetAddrInfoError::FAMILY:
return "ai_family not supported";
case GetAddrInfoError::MEMORY:
return "Memory allocation failure";
case GetAddrInfoError::NODATA:
return "No address associated with hostname";
case GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known";
case GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype";
case GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported";
case GetAddrInfoError::SYSTEM:
return "System error returned in errno";
case GetAddrInfoError::BADHINTS:
return "Invalid value for hints";
case GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown";
case GetAddrInfoError::OVERFLOW_:
return "Argument buffer overflow";
default:
return "Unknown error";
}
}
Network::Domain Translate(Domain domain) {
switch (domain) {
case Domain::Unspecified:
return Network::Domain::Unspecified;
case Domain::INET:
return Network::Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
return {};
}
}
Domain Translate(Network::Domain domain) {
switch (domain) {
case Network::Domain::Unspecified:
return Domain::Unspecified;
case Network::Domain::INET:
return Domain::INET;
default:
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
return {};
}
}
Network::Type Translate(Type type) {
switch (type) {
case Type::Unspecified:
return Network::Type::Unspecified;
case Type::STREAM:
return Network::Type::STREAM;
case Type::DGRAM:
return Network::Type::DGRAM;
case Type::RAW:
return Network::Type::RAW;
case Type::SEQPACKET:
return Network::Type::SEQPACKET;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return Network::Type{};
}
}
Type Translate(Network::Type type) {
switch (type) {
case Network::Type::Unspecified: return Type::Unspecified;
case Network::Type::STREAM: return Type::STREAM;
case Network::Type::DGRAM: return Type::DGRAM;
case Network::Type::RAW: return Type::RAW;
case Network::Type::SEQPACKET: return Type::SEQPACKET;
default:
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return Type{};
}
}
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
switch (protocol) {
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
NETWORK_PROTOCOL_TRANSLATE_LIST
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
default:
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
return {};
}
}
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
switch (protocol) {
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
NETWORK_PROTOCOL_TRANSLATE_LIST
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
default:
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
return {};
}
}
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
Network::PollEvents Translate(PollEvents flags) {
Network::PollEvents result{};
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
if (True(flags & from)) {
flags &= ~from;
result |= to;
}
};
translate(PollEvents::In, Network::PollEvents::In);
translate(PollEvents::Pri, Network::PollEvents::Pri);
translate(PollEvents::Out, Network::PollEvents::Out);
translate(PollEvents::Err, Network::PollEvents::Err);
translate(PollEvents::Hup, Network::PollEvents::Hup);
translate(PollEvents::Nval, Network::PollEvents::Nval);
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
return result;
}
PollEvents Translate(Network::PollEvents flags) {
PollEvents result{};
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
if (True(flags & from)) {
flags &= ~from;
result |= to;
}
};
translate(Network::PollEvents::In, PollEvents::In);
translate(Network::PollEvents::Pri, PollEvents::Pri);
translate(Network::PollEvents::Out, PollEvents::Out);
translate(Network::PollEvents::Err, PollEvents::Err);
translate(Network::PollEvents::Hup, PollEvents::Hup);
translate(Network::PollEvents::Nval, PollEvents::Nval);
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
return result;
}
Network::SockAddrIn Translate(SockAddrIn value) {
// All lengths are valid, from [0 upto 256]
return {
.family = Translate(Domain(value.family)),
.ip = value.ip,
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
};
}
SockAddrIn Translate(Network::SockAddrIn value) {
return {
.len = 16,
.family = static_cast<u8>(Translate(value.family)),
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
.ip = value.ip,
.zeroes = {},
};
}
Network::ShutdownHow Translate(ShutdownHow how) {
switch (how) {
case ShutdownHow::RD:
return Network::ShutdownHow::RD;
case ShutdownHow::WR:
return Network::ShutdownHow::WR;
case ShutdownHow::RDWR:
return Network::ShutdownHow::RDWR;
default:
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
return {};
}
}
} // namespace Service::Sockets
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,49 @@
namespace Service::Sockets {
/// Translate abstract errno to guest errno
Errno Translate(Network::Errno value);
/// Translate abstract return value errno pair to guest return value errno pair
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
/// Translate abstract getaddrinfo error to guest getaddrinfo error
GetAddrInfoError Translate(Network::GetAddrInfoError value);
/// Translate guest error to string
const char* Translate(Network::GetAddrInfoError value);
const char* Translate(GetAddrInfoError value);
/// Translate guest domain to abstract domain
Network::Domain Translate(Domain domain);
/// Translate abstract domain to guest domain
Domain Translate(Network::Domain domain);
/// Translate guest type to abstract type
Network::Type Translate(Type type);
/// Translate abstract type to guest type
Type Translate(Network::Type type);
/// Translate guest protocol to abstract protocol
Network::Protocol Translate(Protocol protocol);
/// Translate abstract protocol to guest protocol
Protocol Translate(Network::Protocol protocol);
/// Translate guest poll event flags to abstract poll event flags
Network::PollEvents Translate(PollEvents flags);
/// Translate abstract poll event flags to guest poll event flags
PollEvents Translate(Network::PollEvents flags);
/// Translate guest socket address structure to abstract socket address structure
Network::SockAddrIn Translate(SockAddrIn value);
/// Translate abstract socket address structure to guest socket address structure
SockAddrIn Translate(Network::SockAddrIn value);
/// Translate guest shutdown mode to abstract shutdown mode
Network::ShutdownHow Translate(ShutdownHow how);
} // namespace Service::Sockets
+1 -1
View File
@@ -132,7 +132,7 @@ public:
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
if (bsd) {
auto err = bsd->CloseImpl(fd);
if (err != Network::Errno::SUCCESS) {
if (err != Service::Sockets::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
}
}
+2 -3
View File
@@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <common/socket_types.h>
#include <mutex>
#include "core/internal_network/socket_types.h"
namespace Network {
File diff suppressed because it is too large Load Diff
+63 -9
View File
@@ -13,7 +13,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -31,10 +31,68 @@ namespace Network {
class SocketBase;
class Socket;
struct HostPollFD {
SocketBase* socket = nullptr;
Network::PollEvents events = {};
Network::PollEvents revents = {};
/// Error code for network functions
enum class Errno {
SUCCESS,
BADF,
INVAL,
MFILE,
PIPE,
NOTCONN,
AGAIN,
CONNREFUSED,
CONNRESET,
CONNABORTED,
HOSTUNREACH,
NETDOWN,
NETUNREACH,
TIMEDOUT,
MSGSIZE,
INPROGRESS,
ISCONN,
OTHER,
};
enum class GetAddrInfoError {
SUCCESS,
ADDRFAMILY,
AGAIN,
BADFLAGS,
FAIL,
FAMILY,
MEMORY,
NODATA,
NONAME,
SERVICE,
SOCKTYPE,
SYSTEM,
BADHINTS,
PROTOCOL,
OVERFLOW_,
OTHER,
};
/// Cross-platform poll fd structure
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
In = 1 << 0,
Pri = 1 << 1,
Out = 1 << 2,
Err = 1 << 3,
Hup = 1 << 4,
Nval = 1 << 5,
RdNorm = 1 << 6,
RdBand = 1 << 7,
WrBand = 1 << 8,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
SocketBase* socket;
PollEvents events;
PollEvents revents;
};
class NetworkInstance {
@@ -43,10 +101,6 @@ public:
~NetworkInstance();
};
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
s32 TranslateMsgOptToNative(s32 flags);
void CancelPendingSocketOperations();
void RestartSocketOperations();
@@ -46,7 +46,7 @@ namespace Network {
#ifdef _WIN32
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
ULONG buf_size = 0;
if (GetAdaptersAddresses(
@@ -66,7 +66,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
return {};
}
std::vector<NetworkInterface> result;
std::vector<Network::NetworkInterface> result;
for (auto* a = addrs; a; a = a->Next) {
@@ -89,7 +89,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
->sin_addr;
result.emplace_back(NetworkInterface{
result.emplace_back(Network::NetworkInterface{
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
.ip_address = ip,
.subnet_mask = mask,
@@ -103,7 +103,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#else
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#if defined(__ANDROID__) || defined(__linux__)
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) {
@@ -135,7 +135,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
}
#endif
std::vector<NetworkInterface> ifaces;
std::vector<Network::NetworkInterface> ifaces;
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
@@ -149,7 +149,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
});
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
gw.s_addr = it != routes.end() ? it->gateway : 0;
ifaces.emplace_back(NetworkInterface{
ifaces.emplace_back(Network::NetworkInterface{
.name = ifa->ifa_name,
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
@@ -159,7 +159,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
freeifaddrs(ifaddr);
return ifaces;
#elif defined(__FreeBSD__)
std::vector<NetworkInterface> ifaces;
std::vector<Network::NetworkInterface> ifaces;
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (fd < 0) {
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
@@ -191,7 +191,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
char const* p = (char const*)(ifm + 1);
NetworkInterface iface{};
Network::NetworkInterface iface{};
for (size_t i = 0; i < RTAX_MAX; i++)
if ((ifm->ifm_addrs & (1 << i)) != 0) {
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
@@ -220,7 +220,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#endif // _WIN32
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
auto const& sel_if = Settings::values.network_interface.GetValue();
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
if (sel_if.empty())
-174
View File
@@ -1,174 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <cstdio>
#include "common/assert.h"
#include "common/logging.h"
#include "core/internal_network/socket_icmp.h"
#ifdef __unix__
#include <unistd.h>
#include <sys/socket.h>
#endif
namespace Network {
namespace {
u16 ComputeChecksum(std::span<const u8> data) {
u32 sum = 0;
for (size_t i = 0; i < data.size(); i += 2) {
u32 value = (u32(data[i + 0]) << 8ull) | u32(data[i + 1]); //big endian
sum += value;
}
if (data.size() % 2 != 0){
sum += u16(data[data.size() - 1]) << 8;
}
while ((sum >> 16) != 0)
sum = (sum & 0xffff) + (sum >> 16);
return (~sum) & 0xffff;
}
}
IcmpSocket::IcmpSocket() noexcept {}
IcmpSocket::~IcmpSocket() {
if (fd == INVALID_SOCKET) {
return;
}
fd = INVALID_SOCKET;
}
Errno IcmpSocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_WARNING(Network, "(stubbed) level={},optname={},optval={}", level, optname, optval.size());
return Errno::SUCCESS;
}
Errno IcmpSocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
return Errno::SUCCESS;
}
std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
LOG_WARNING(Network, "(stubbed) called");
return {AcceptResult{}, Errno::SUCCESS};
}
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetPeerName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
}
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetSockName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
}
Errno IcmpSocket::Bind(Network::SockAddrIn addr) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
Errno IcmpSocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
Errno IcmpSocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::SUCCESS;
}
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
LOG_DEBUG(Network, "(stubbed) called");
return {s32(0), Errno::NOTCONN};
}
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "(stubbed) called");
ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
#ifdef __unix__
if (addr) {
if (seq_ident.empty())
return {0, Errno::SUCCESS};
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
auto const cmd = fmt::format("ping -t 3 -o {}.{}.{}.{}", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]);
if (::system(cmd.c_str()) == 0) {
std::vector<u8> data{
8,
0,
0, //checksum
0,
u8(seq_ident.front() >> 24), //ident
u8(seq_ident.front() >> 16),
u8(seq_ident.front() >> 8), // seq
u8(seq_ident.front() >> 0),
};
seq_ident.pop_back();
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
data[2] = u8(csum >> 8); //hi
data[3] = u8(csum); //lo
auto const n = (std::max)(data.size(), message.size());
std::copy(data.begin(), data.begin() + n, message.begin());
return {n, Errno::SUCCESS};
}
return {-1, Errno::TIMEDOUT};
}
#endif
return {-1, Errno::INVAL};
}
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
LOG_DEBUG(Network, "(stubbed) called");
seq_ident.push_back(
(u32(message[4]) << 24)
| (u32(message[5]) << 16)
| (u32(message[6]) << 8)
| (u32(message[7]) << 0)
);
return {s32(0), Errno::NOTCONN};
}
std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "(stubbed) called");
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
// 0 -> 8 (IPv4), 128 (IPv6)
// 1 -> 0
// 2..4 -> checksum
// 4..6 -> ident
// 6..8 -> seq
if (!message.empty())
return {s32(message.size()), Errno::SUCCESS};
return {-1, Errno::INVAL};
}
Errno IcmpSocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET;
return Errno::SUCCESS;
}
std::pair<Errno, Errno> IcmpSocket::GetPendingError() {
LOG_DEBUG(Network, "called");
return {Errno::SUCCESS, Errno::SUCCESS};
}
bool IcmpSocket::IsOpened() const {
return fd != INVALID_SOCKET;
}
void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
LOG_WARNING(Network, "(stubbed) called");
}
Errno IcmpSocket::SetNonBlock(bool enable) {
return Errno::SUCCESS;
}
} // namespace Network
-37
View File
@@ -1,37 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <span>
#include "core/internal_network/sockets.h"
namespace Network {
class IcmpSocket : public Network::SocketBase {
public:
explicit IcmpSocket() noexcept;
~IcmpSocket() override;
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
Errno Close() override;
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(Network::SockAddrIn addr_in) override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
Errno Shutdown(ShutdownHow how) override;
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
std::pair<Errno, Errno> GetPendingError() override;
bool IsOpened() const override;
void HandleProxyPacket(const ProxyPacket& packet) override;
Errno SetNonBlock(bool enable) override;
std::vector<u32> seq_ident;
};
} // namespace Network
+95 -57
View File
@@ -47,82 +47,71 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
received_packets.push(decompressed);
}
Errno ProxySocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::SUCCESS;
}
Errno ProxySocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
LOG_DEBUG(Network, "level={},optname={},optval={}", level, optname, optval.size());
// numeric values?
if (optval.size() >= sizeof(u32)) {
u32 value;
std::memcpy(&value, optval.data(), sizeof(value));
if (optname == Network::OptName::BROADCAST)
broadcast = bool(value);
if (optname == Network::OptName::SNDTIMEO)
send_timeout = value;
if (optname == Network::OptName::RCVTIMEO)
receive_timeout = value;
}
template <typename T>
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
LOG_DEBUG(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
protocol = socket_protocol;
SetSockOpt(fd, SO_TYPE, type);
return Errno::SUCCESS;
}
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
LOG_WARNING(Network, "(stubbed) called");
LOG_WARNING(Network, "(STUBBED) called");
return {AcceptResult{}, Errno::SUCCESS};
}
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
Errno ProxySocket::Connect(SockAddrIn addr_in) {
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
}
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::SUCCESS};
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
}
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
Errno ProxySocket::Bind(SockAddrIn addr) {
if (is_bound) {
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
return Errno::SUCCESS;
}
local_endpoint = addr;
is_bound = true;
return Errno::SUCCESS;
}
Errno ProxySocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(stubbed) called");
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
Errno ProxySocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(stubbed) called");
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
LOG_WARNING(Network, "(stubbed) called");
LOG_WARNING(Network, "(STUBBED) called");
ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
return {s32(0), Errno::SUCCESS};
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
return {static_cast<s32>(0), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
const auto timestamp = std::chrono::steady_clock::now();
@@ -145,29 +134,29 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Ne
std::this_thread::yield();
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
const auto time_diff_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
if (time_diff_ms > timeout) {
return {-1, Errno::TIMEDOUT};
}
}
}
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
LOG_DEBUG(Network, "called");
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length) {
ProxyPacket& packet = received_packets.front();
if (addr) {
addr->len = 16;
addr->family = u8(Network::Domain::INET);
addr->family = Domain::INET;
addr->ip = packet.local_endpoint.ip; // The senders ip address
addr->portno = packet.local_endpoint.portno; // The senders port number
addr->zeroes = {};
}
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
bool peek = (flags & FLAG_MSG_PEEK) != 0;
std::size_t read_bytes;
if (packet.data.size() > max_length) {
read_bytes = max_length;
std::memcpy(message.data(), packet.data.data(), max_length);
memcpy(message.data(), packet.data.data(), max_length);
if (protocol == Protocol::UDP) {
if (!peek) {
@@ -176,48 +165,51 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> messag
return {-1, Errno::MSGSIZE};
} else if (protocol == Protocol::TCP) {
std::vector<u8> numArray(packet.data.size() - max_length);
std::copy(packet.data.begin() + max_length, packet.data.end(), std::back_inserter(numArray));
std::copy(packet.data.begin() + max_length, packet.data.end(),
std::back_inserter(numArray));
packet.data = numArray;
}
} else {
read_bytes = packet.data.size();
std::memcpy(message.data(), packet.data.data(), read_bytes);
memcpy(message.data(), packet.data.data(), read_bytes);
if (!peek) {
received_packets.pop();
}
}
return {u32(read_bytes), Errno::SUCCESS};
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
LOG_WARNING(Network, "(stubbed) called");
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
LOG_WARNING(Network, "(STUBBED) called");
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
ASSERT(flags == 0);
return {s32(0), Errno::SUCCESS};
return {static_cast<s32>(0), Errno::SUCCESS};
}
void ProxySocket::SendPacket(ProxyPacket& packet) {
if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
packet.data.size());
room_member->SendProxyPacket(packet);
}
}
}
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "called");
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) {
ASSERT(flags == 0);
if (!is_bound) {
LOG_ERROR(Network, "ProxySocket is not bound!");
return {s32(message.size()), Errno::SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
if (auto room_member = Network::GetRoomMember().lock()) {
if (!room_member->IsConnected()) {
return {s32(message.size()), Errno::SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
}
@@ -242,19 +234,65 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
SendPacket(packet);
return {s32(message.size()), Errno::SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
Errno ProxySocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET;
closed = true;
return Errno::SUCCESS;
}
Errno ProxySocket::SetLinger(bool enable, u32 linger) {
struct Linger {
u16 linger_enable;
u16 linger_time;
} values;
values.linger_enable = enable ? 1 : 0;
values.linger_time = static_cast<u16>(linger);
return SetSockOpt(fd, SO_LINGER, values);
}
Errno ProxySocket::SetReuseAddr(bool enable) {
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
}
Errno ProxySocket::SetBroadcast(bool enable) {
broadcast = enable;
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
}
Errno ProxySocket::SetSndBuf(u32 value) {
return SetSockOpt(fd, SO_SNDBUF, value);
}
Errno ProxySocket::SetKeepAlive(bool enable) {
return Errno::SUCCESS;
}
Errno ProxySocket::SetRcvBuf(u32 value) {
return SetSockOpt(fd, SO_RCVBUF, value);
}
Errno ProxySocket::SetSndTimeo(u32 value) {
send_timeout = value;
return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
}
Errno ProxySocket::SetRcvTimeo(u32 value) {
receive_timeout = value;
return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
}
Errno ProxySocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::SUCCESS;
}
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
LOG_DEBUG(Network, "called");
LOG_DEBUG(Network, "(STUBBED) called");
return {Errno::SUCCESS, Errno::SUCCESS};
}
+27 -10
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,13 +29,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(Network::SockAddrIn addr_in) override;
Errno Connect(SockAddrIn addr_in) override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Bind(SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
@@ -43,9 +43,9 @@ public:
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr,
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length);
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
@@ -53,11 +53,28 @@ public:
void SendPacket(ProxyPacket& packet);
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const Network::SockAddrIn* addr) override;
const SockAddrIn* addr) override;
Errno SetLinger(bool enable, u32 linger) override;
Errno SetReuseAddr(bool enable) override;
Errno SetBroadcast(bool enable) override;
Errno SetKeepAlive(bool enable) override;
Errno SetSndBuf(u32 value) override;
Errno SetRcvBuf(u32 value) override;
Errno SetSndTimeo(u32 value) override;
Errno SetRcvTimeo(u32 value) override;
Errno SetNonBlock(bool enable) override;
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override;
@@ -69,7 +86,7 @@ private:
u32 send_timeout = 0;
u32 receive_timeout = 0;
bool is_bound = false;
Network::SockAddrIn local_endpoint{};
SockAddrIn local_endpoint{};
bool blocking = true;
std::queue<ProxyPacket> received_packets;
Protocol protocol;
-360
View File
@@ -1,360 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string>
#include "common/common_types.h"
#include "common/common_funcs.h"
// Most of these structures are direct mappings of guest's
// expectations for these values, in other words, they're the
// values that HOS is expected to use AND handle.
namespace Network {
enum class Errno : u32 {
SUCCESS = 0,
BADF = 9,
AGAIN = 11,
INVAL = 22,
MFILE = 24,
PIPE = 32,
NOTSOCK = 88,
MSGSIZE = 90,
PROTOTYPE = 91,
NOPROTOOPT = 92,
PROTONOSUPPORT = 93,
SOCKTNOSUPPORT = 94,
NOTSUP = 95,
ADDRINUSE = 98,
ADDRNOTAVAIL = 99,
NETDOWN = 100,
NETUNREACH = 101,
CONNABORTED = 103,
CONNRESET = 104,
ISCONN = 106,
NOTCONN = 107,
TIMEDOUT = 110,
CONNREFUSED = 111,
HOSTUNREACH = 113,
ALREADY = 114,
INPROGRESS = 115,
STALE = 116,
/* made up error? */
OTHER = 196,
};
enum class GetAddrInfoError : s32 {
SUCCESS = 0,
ADDRFAMILY = 1,
AGAIN = 2,
BADFLAGS = 3,
FAIL = 4,
FAMILY = 5,
MEMORY = 6,
NODATA = 7,
NONAME = 8,
SERVICE = 9,
SOCKTYPE = 10,
SYSTEM = 11,
BADHINTS = 12,
PROTOCOL = 13,
OVERFLOW_ = 14, // avoid name collision with Windows macro
OTHER = 15,
};
enum class Domain : u32 {
Unspecified = 0,
UNIX = 1,
INET = 2,
IMPLINK = 3,
PUP = 4,
CHAOS = 5,
NETBIOS = 6,
ISO = 7,
ECMA = 8,
DATAKIT = 9,
CCITT = 10,
SNA = 11,
DECnet = 12,
DLI = 13,
LAT = 14,
HYLINK = 15,
APPLETALK = 16,
ROUTE = 17,
LINK = 18,
COIP = 20,
CNT = 21,
IPX = 23,
SIP = 24,
ISDN = 26,
INET6 = 28,
NATM = 29,
ATM = 30,
NETGRAPH = 32,
SLOW = 33,
SCLUSTER = 34,
ARP = 35,
BLUETOOTH = 36,
IEEE80211 = 37,
NETLINK = 38,
INET_SDP = 40,
INET6_SDP = 42,
};
enum class Type : u32 {
Unspecified = 0,
STREAM = 1,
DGRAM = 2,
RAW = 3,
RDM = 4,
SEQPACKET = 5,
};
enum class Protocol : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
//
IPV6 = 41,
RAW = 255,
//
HOPOPTS = 0,
IGMP = 2,
GGP = 3,
IPV4 = 4,
ST = 7,
EGP = 8,
PIGP = 9,
RCCMON = 10,
NVPII = 11,
PUP = 12,
ARGUS = 13,
EMCON = 14,
XNET = 15,
CHAOS = 16,
MUX = 18,
MEAS = 19,
HMP = 20,
PRM = 21,
IDP = 22,
TRUNK1 = 23,
TRUNK2 = 24,
LEAF1 = 25,
LEAF2 = 26,
RDP = 27,
IRTP = 28,
TP = 29,
BLT = 30,
NSP = 31,
INP = 32,
DCCP = 33,
//3PC = 34,
IDPR = 35,
XTP = 36,
DDP = 37,
CMTP = 38,
TPXX = 39,
IL = 40,
SDRP = 42,
ROUTING = 43,
FRAGMENT = 44,
IDRP = 45,
RSVP = 46,
GRE = 47,
MHRP = 48,
BHA = 49,
ESP = 50,
AH = 51,
INLSP = 52,
SWIPE = 53,
NHRP = 54,
MOBILE = 55,
TLSP = 56,
SKIP = 57,
ICMPV6 = 58,
NONE = 59,
DSTOPTS = 60,
AHIP = 61,
CFTP = 62,
HELLO = 63,
SATEXPAK = 64,
KRYPTOLAN = 65,
RVD = 66,
IPPC = 67,
ADFS = 68,
SATMON = 69,
VISA = 70,
IPCV = 71,
CPNX = 72,
CPHB = 73,
WSN = 74,
PVP = 75,
BRSATMON = 76,
ND = 77,
WBMON = 78,
WBEXPAK = 79,
EON = 80,
VMTP = 81,
SVMTP = 82,
VINES = 83,
TTP = 84,
IGP = 85,
DGP = 86,
TCF = 87,
IGRP = 88,
OSPFIGP = 89,
SRPC = 90,
LARP = 91,
MTP = 92,
AX25 = 93,
IPEIP = 94,
MICP = 95,
SCCSP = 96,
ETHERIP = 97,
ENCAP = 98,
APES = 99,
GMTP = 100,
IPCOMP = 108,
SCTP = 132,
MH = 135,
UDPLITE = 136,
HIP = 139,
SHIM6 = 140,
PIM = 103,
CARP = 112,
PGM = 113,
MPLS = 137,
PFSYNC = 240,
};
enum class SocketLevel : u32 {
IP = 0,
ICMP = 1,
TCP = 6,
UDP = 17,
CONFIG = 0xfffe,
SOCKET = 0xffff, // i.e. SOL_SOCKET
};
enum class MsgOpt : u32 {
OOB = 0x00001,
PEEK = 0x00002,
DONTROUTE = 0x00004,
EOR_ = 0x00008,
TRUNC = 0x00010,
CTRUNC = 0x00020,
WAITALL = 0x00040,
DONTWAIT = 0x00080,
EOF_ = 0x00100,
NOSIGNAL = 0x20000,
};
enum class OptName : u32 {
DEBUG = 0x0001,
ACCEPTCONN = 0x0002,
REUSEADDR = 0x0004,
KEEPALIVE = 0x0008,
DONTROUTE = 0x0010,
BROADCAST = 0x0020,
USELOOPBACK = 0x0040,
LINGER = 0x0080,
OOBINLINE = 0x0100,
REUSEPORT = 0x0200,
TIMESTAMP = 0x0400,
NOSIGPIPE = 0x0800, // at least according to libnx
ACCEPTFILER = 0x1000,
SNDBUF = 0x1001,
RCVBUF = 0x1002,
SNDTIMEO = 0x1005,
RCVTIMEO = 0x1006,
ERROR_ = 0x1007, // avoid name collision with Windows macro
ACCEPTFILTER = 0x1000,
BINTIME = 0x2000,
NO_OFFLOAD = 0x4000,
NO_DDP = 0x8000,
};
enum class TcpOptName : u32 {
NODELAY = 0x0001,
MAXSEG = 0x0002,
NOPUSH = 0x0004,
NOOPT = 0x0008,
MS5SIG = 0x0010,
INFO = 0x0020
};
enum class ShutdownHow : s32 {
RD = 0,
WR = 1,
RDWR = 2,
};
enum class FcntlCmd : s32 {
GETFL = 3,
SETFL = 4,
};
enum class FcntlFlags : u32 {
NONBLOCK = 0x004,
NONBLOCK_NX = 0x800,
// Provided for convenience
NONBLOCK_ANY = u32(NONBLOCK) | u32(NONBLOCK_NX),
};
/// Array of IPv4 address
using IPv4Address = std::array<u8, 4>;
struct SockAddrIn {
u8 len;
u8 family;
u16 portno;
IPv4Address ip;
std::array<u8, 248> zeroes;
};
static_assert(sizeof(SockAddrIn) == 0x100);
enum class PollEvents : u16 {
// Using Pascal case because IN is a macro on Windows.
IN_ = 0x0001,
PRI_ = 0x0002,
OUT_ = 0x0004,
ERR_ = 0x0008,
HUP_ = 0x0010,
NVAL = 0x0020,
RDNORM = 0x0040,
RDBAND = 0x0080,
WRBAND = 0x0100,
IGNEOF = 0x2000,
};
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
s32 fd;
Network::PollEvents events;
Network::PollEvents revents;
};
static_assert(sizeof(PollFD) == 8);
struct Linger {
s32 onoff;
s32 linger;
};
static_assert(sizeof(Linger) == 8);
/// @brief Cross-platform addrinfo structure (not guest)
struct AddrInfo {
Domain family;
Type socket_type;
Protocol protocol;
SockAddrIn addr;
std::optional<std::string> canon_name;
};
} // namespace Network
+54 -19
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -30,7 +30,7 @@ public:
struct AcceptResult {
std::unique_ptr<SocketBase> socket;
Network::SockAddrIn sockaddr_in;
SockAddrIn sockaddr_in;
};
SocketBase() = default;
@@ -46,13 +46,13 @@ public:
virtual std::pair<AcceptResult, Errno> Accept() = 0;
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
virtual Errno Connect(SockAddrIn addr_in) = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
virtual Errno Bind(Network::SockAddrIn addr) = 0;
virtual Errno Bind(SockAddrIn addr) = 0;
virtual Errno Listen(s32 backlog) = 0;
@@ -60,16 +60,31 @@ public:
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) = 0;
virtual Errno SetLinger(bool enable, u32 linger) = 0;
virtual Errno SetReuseAddr(bool enable) = 0;
virtual Errno SetKeepAlive(bool enable) = 0;
virtual Errno SetBroadcast(bool enable) = 0;
virtual Errno SetSndBuf(u32 value) = 0;
virtual Errno SetRcvBuf(u32 value) = 0;
virtual Errno SetSndTimeo(u32 value) = 0;
virtual Errno SetRcvTimeo(u32 value) = 0;
virtual Errno SetNonBlock(bool enable) = 0;
virtual Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
virtual std::pair<Errno, Errno> GetPendingError() = 0;
virtual bool IsOpened() const = 0;
@@ -80,6 +95,7 @@ public:
return fd;
}
protected:
SOCKET fd = INVALID_SOCKET;
};
@@ -98,13 +114,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(Network::SockAddrIn addr_in) override;
Errno Connect(SockAddrIn addr_in) override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Bind(SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
@@ -112,19 +128,38 @@ public:
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) override;
Errno SetLinger(bool enable, u32 linger) override;
Errno SetReuseAddr(bool enable) override;
Errno SetKeepAlive(bool enable) override;
Errno SetBroadcast(bool enable) override;
Errno SetSndBuf(u32 value) override;
Errno SetRcvBuf(u32 value) override;
Errno SetSndTimeo(u32 value) override;
Errno SetRcvTimeo(u32 value) override;
Errno SetNonBlock(bool enable) override;
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override;
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
template <typename T>
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
bool IsOpened() const override;
@@ -134,6 +169,6 @@ private:
bool is_non_blocking = false;
};
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
} // namespace Network
+30 -2
View File
@@ -74,6 +74,7 @@ add_library(dynarmic STATIC
interface/halt_reason.h
interface/exclusive_monitor.h
interface/optimization_flags.h
interface/halt_reason.h
ir/acc_type.h
ir/basic_block.cpp
ir/basic_block.h
@@ -139,10 +140,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
@@ -294,6 +293,35 @@ if ("riscv64" IN_LIST ARCHITECTURE)
)
message(WARNING "TODO: Incomplete frontend for this host architecture")
endif()
if ("powerpc64" IN_LIST ARCHITECTURE)
target_link_libraries(dynarmic PRIVATE powah)
target_sources(dynarmic PRIVATE
backend/ppc64/abi.h
backend/ppc64/emit_context.h
backend/ppc64/emit_ppc64_a32.cpp
backend/ppc64/emit_ppc64_a64.cpp
backend/ppc64/emit_ppc64_misc.cpp
backend/ppc64/emit_ppc64_data_processing.cpp
backend/ppc64/emit_ppc64_floating_point.cpp
backend/ppc64/emit_ppc64_vector.cpp
backend/ppc64/emit_ppc64.cpp
backend/ppc64/emit_ppc64.h
backend/ppc64/exclusive_monitor.cpp
backend/ppc64/reg_alloc.cpp
backend/ppc64/reg_alloc.h
backend/ppc64/stack_layout.h
backend/ppc64/hostloc.h
common/spin_lock_ppc64.cpp
common/spin_lock_ppc64.h
# A32
backend/ppc64/a32_core.h
backend/ppc64/a32_interface.cpp
# A64
backend/ppc64/a64_core.h
backend/ppc64/a64_interface.cpp
backend/ppc64/code_block.h
)
endif()
if ("loongarch64" IN_LIST ARCHITECTURE)
target_link_libraries(dynarmic PRIVATE lagoon::lagoon)
@@ -17,7 +17,7 @@
#if defined(ARCHITECTURE_x86_64)
namespace Dynarmic::Backend::X64 {
class BlockOfCode;
} // namespace Dynarmic::Backend::X64
}
#elif defined(ARCHITECTURE_arm64)
namespace oaknut {
class CodeBlock;
@@ -30,6 +30,10 @@ class CodeBlock;
namespace Dynarmic::Backend::LoongArch64 {
class CodeBlock;
} // namespace Dynarmic::Backend::LoongArch64
#elif defined(ARCHITECTURE_powerpc64)
namespace Dynarmic::Backend::PPC64 {
class CodeBlock;
}
#else
# error "Invalid architecture"
#endif
@@ -53,6 +57,9 @@ struct FakeCall {
struct FakeCall {
u64 call_pc;
};
#elif defined(ARCHITECTURE_powerpc64)
struct FakeCall {
};
#else
# error "Invalid architecture"
#endif
@@ -70,6 +77,8 @@ public:
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
#elif defined(ARCHITECTURE_loongarch64)
void Register(LoongArch64::CodeBlock& mem, std::size_t mem_size);
#elif defined(ARCHITECTURE_powerpc64)
void Register(PPC64::CodeBlock& mem, std::size_t mem_size);
#else
# error "Invalid architecture"
#endif
@@ -17,24 +17,21 @@ 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_loongarch64)
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t) {
// Do nothing
}
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t)
#elif defined(ARCHITECTURE_powerpc64)
void ExceptionHandler::Register(PPC64::CodeBlock&, std::size_t)
#else
# error "Invalid architecture"
#endif
{
// Do nothing
}
bool ExceptionHandler::SupportsFastmem() const noexcept {
return false;
@@ -11,6 +11,7 @@
#include <algorithm>
#include <bit>
#include <cstring>
#include <algorithm>
#include <functional>
#include <memory>
#include <mutex>
@@ -34,6 +35,8 @@
# include "dynarmic/backend/riscv64/code_block.h"
#elif defined(ARCHITECTURE_loongarch64)
# include "dynarmic/backend/loongarch64/code_block.h"
#elif defined(ARCHITECTURE_powerpc64)
# include "dynarmic/backend/ppc64/code_block.h"
#else
# error "Invalid architecture"
#endif
@@ -168,7 +171,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
}
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
#else
# error "Invalid architecture"
UNREACHABLE();
#endif
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
@@ -230,6 +233,10 @@ void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
void ExceptionHandler::Register(LoongArch64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
}
#elif defined(ARCHITECTURE_powerpc64)
void ExceptionHandler::Register(PPC64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
}
#else
# error "Invalid architecture"
#endif
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#include <ankerl/unordered_dense.h>
#include "dynarmic/backend/ppc64/code_block.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/ir/terminal.h"
#include "dynarmic/ir/basic_block.h"
namespace Dynarmic::Backend::PPC64 {
struct A32JitState {
std::array<u32, 16> regs{};
alignas(16) std::array<u32, 64> ext_regs{};
u32 upper_location_descriptor;
u32 exclusive_state = 0;
u32 cpsr_nzcv = 0;
u32 fpscr = 0;
u32 check_bit = 0;
IR::LocationDescriptor GetLocationDescriptor() const {
return IR::LocationDescriptor{regs[15] | (u64(upper_location_descriptor) << 32)};
}
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,250 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <memory>
#include <mutex>
#include <boost/icl/interval_set.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/frontend/A32/translate/a32_translate.h"
#include "dynarmic/frontend/A32/FPSCR.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/ir/opt_passes.h"
#include "dynarmic/interface/A32/a32.h"
namespace Dynarmic::A32 {
using namespace Dynarmic::Backend::PPC64;
using CodePtr = std::uint32_t*;
struct A32AddressSpace final {
explicit A32AddressSpace(const A32::UserConfig& conf)
: conf(conf)
, cb(conf.code_cache_size)
, as(cb.ptr<u8*>(), conf.code_cache_size) {
}
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
return it->second;
ir_block.Reset(A32::LocationDescriptor{desc});
A32::Translate(ir_block, A32::LocationDescriptor{desc}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
Optimization::Optimize(ir_block, conf, {});
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
block_infos.insert_or_assign(desc.Value(), block_info);
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
return block_info.entry_point;
}
void ClearCache() {
block_entries.clear();
block_infos.clear();
}
EmittedBlockInfo Emit(IR::Block block) {
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
.enable_cycle_counting = conf.enable_cycle_counting,
.always_little_endian = conf.always_little_endian,
.a64_variant = false
});
Link(block_info);
return block_info;
}
void Link(EmittedBlockInfo& block_info) {
//UNREACHABLE();
}
IR::Block ir_block = {LocationDescriptor(0, PSR(0), FPSCR(0), false)};
const A32::UserConfig conf;
CodeBlock cb;
powah::Context as;
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
};
struct A32Core final {
static HaltReason Run(A32AddressSpace& process, A32JitState& thread_ctx, volatile u32* halt_reason) {
auto const loc = thread_ctx.GetLocationDescriptor();
auto const entry = process.GetOrEmit(loc);
using CodeFn = HaltReason (*)(A32AddressSpace*, A32JitState*, volatile u32*, void*);
return (CodeFn(entry))(&process, &thread_ctx, halt_reason, reinterpret_cast<void*>(&A32Core::Run));
}
};
struct Jit::Impl final {
Impl(Jit* jit_interface, A32::UserConfig conf)
: conf(conf)
, current_address_space(conf)
, jit_interface(jit_interface) {}
HaltReason Run() {
ASSERT(!is_executing);
is_executing = false;
HaltReason hr = A32Core::Run(current_address_space, jit_state, &halt_reason);
is_executing = true;
RequestCacheInvalidation();
return hr;
}
HaltReason Step() {
// HaltReason hr = A32Core::Step(current_address_space, jit_state, &halt_reason);
// RequestCacheInvalidation();
return HaltReason{};
}
void ClearCache() {
std::unique_lock lock{invalidation_mutex};
invalidate_entire_cache = true;
HaltExecution(HaltReason::CacheInvalidation);
}
void InvalidateCacheRange(u32 start_address, size_t length) {
std::unique_lock lock{invalidation_mutex};
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, u32(start_address + length - 1)));
HaltExecution(HaltReason::CacheInvalidation);
}
void Reset() {
jit_state = {};
}
void HaltExecution(HaltReason hr) {
Atomic::Or(&halt_reason, ~u32(hr));
}
void ClearHalt(HaltReason hr) {
Atomic::And(&halt_reason, ~u32(hr));
}
std::array<u32, 16>& Regs() {
return jit_state.regs;
}
const std::array<u32, 16>& Regs() const {
return jit_state.regs;
}
std::array<u32, 64>& ExtRegs() {
return jit_state.ext_regs;
}
const std::array<u32, 64>& ExtRegs() const {
return jit_state.ext_regs;
}
u32 Cpsr() const {
return jit_state.cpsr_nzcv;
}
void SetCpsr(u32 value) {
jit_state.cpsr_nzcv = value;
}
u32 Fpscr() const {
return jit_state.fpscr;
}
void SetFpscr(u32 value) {
jit_state.fpscr = value;
}
void ClearExclusiveState() {
jit_state.exclusive_state = false;
}
private:
void RequestCacheInvalidation() {
// UNREACHABLE();
invalidate_entire_cache = false;
invalid_cache_ranges.clear();
}
A32::UserConfig conf;
A32JitState jit_state{};
A32AddressSpace current_address_space;
Jit* jit_interface;
volatile u32 halt_reason = 0;
bool is_executing = false;
boost::icl::interval_set<u32> invalid_cache_ranges;
bool invalidate_entire_cache = false;
std::mutex invalidation_mutex;
};
Jit::Jit(UserConfig conf) : impl(std::make_unique<Impl>(this, conf)) {}
Jit::~Jit() = default;
HaltReason Jit::Run() {
return impl->Run();
}
HaltReason Jit::Step() {
return impl->Step();
}
void Jit::ClearCache() {
impl->ClearCache();
}
void Jit::InvalidateCacheRange(u32 start_address, std::size_t length) {
impl->InvalidateCacheRange(start_address, length);
}
void Jit::Reset() {
impl->Reset();
}
void Jit::HaltExecution(HaltReason hr) {
impl->HaltExecution(hr);
}
void Jit::ClearHalt(HaltReason hr) {
impl->ClearHalt(hr);
}
std::array<u32, 16>& Jit::Regs() {
return impl->Regs();
}
const std::array<u32, 16>& Jit::Regs() const {
return impl->Regs();
}
std::array<u32, 64>& Jit::ExtRegs() {
return impl->ExtRegs();
}
const std::array<u32, 64>& Jit::ExtRegs() const {
return impl->ExtRegs();
}
u32 Jit::Cpsr() const {
return impl->Cpsr();
}
void Jit::SetCpsr(u32 value) {
impl->SetCpsr(value);
}
u32 Jit::Fpscr() const {
return impl->Fpscr();
}
void Jit::SetFpscr(u32 value) {
impl->SetFpscr(value);
}
void Jit::ClearExclusiveState() {
impl->ClearExclusiveState();
}
} // namespace Dynarmic::A32
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#include <ankerl/unordered_dense.h>
#include "dynarmic/backend/ppc64/code_block.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
#include "dynarmic/interface/A64/a64.h"
#include "dynarmic/interface/A64/config.h"
#include "dynarmic/ir/terminal.h"
#include "dynarmic/ir/basic_block.h"
namespace Dynarmic::Backend::PPC64 {
struct A64JitState {
using ProgramCounterType = u32;
std::array<u64, 31> regs{};
u64 pc = 0;
alignas(16) std::array<u64, 64> vec{};
u64 sp = 0;
u32 upper_location_descriptor = 0;
u32 exclusive_state = 0;
u32 pstate = 0;
u32 fpcr = 0;
u32 fpsr = 0;
volatile u32 halt_reason = 0;
u32 check_bit = 0;
IR::LocationDescriptor GetLocationDescriptor() const {
const u64 fpcr_u64 = u64(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
return IR::LocationDescriptor{pc_u64 | fpcr_u64};
}
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,366 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <memory>
#include <mutex>
#include <cstddef>
#include <boost/icl/interval_set.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
#include "dynarmic/frontend/A64/translate/a64_translate.h"
#include "dynarmic/interface/A64/config.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/ir/opt_passes.h"
#include "dynarmic/interface/A64/a64.h"
namespace Dynarmic::A64 {
using namespace Dynarmic::Backend::PPC64;
using CodePtr = std::uint32_t*;
struct A64AddressSpace final {
explicit A64AddressSpace(const A64::UserConfig& conf)
: conf(conf)
, cb(conf.code_cache_size)
, as(cb.ptr<u8*>(), conf.code_cache_size)
{
}
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
return it->second;
auto const get_code = [this](u64 vaddr) {
return conf.callbacks->MemoryReadCode(vaddr);
};
ir_block.Reset(A64::LocationDescriptor{desc});
A64::Translate(ir_block, A64::LocationDescriptor{desc}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
Optimization::Optimize(ir_block, conf, {});
fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
block_infos.insert_or_assign(desc.Value(), block_info);
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
return block_info.entry_point;
}
void ClearCache() {
block_entries.clear();
block_infos.clear();
}
EmittedBlockInfo Emit(IR::Block block) {
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
.enable_cycle_counting = conf.enable_cycle_counting,
.always_little_endian = true,
.a64_variant = true
});
Link(block_info);
return block_info;
}
void Link(EmittedBlockInfo& block_info) {
// TODO(lizzie): Block linking
// UNREACHABLE();
}
IR::Block ir_block = {LocationDescriptor(0, FP::FPCR(0), false)};
const A64::UserConfig conf;
CodeBlock cb;
powah::Context as;
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
};
struct A64Core final {
using CodeFn = HaltReason (*)(A64AddressSpace*, A64JitState*, volatile u32*, void *fn);
static HaltReason Run(A64AddressSpace& process, A64JitState& thread_ctx, volatile u32* halt_reason) {
HaltReason hr{};
do {
const auto loc = thread_ctx.GetLocationDescriptor();
const auto entry = process.GetOrEmit(loc);
hr = (CodeFn(entry))(&process, &thread_ctx, halt_reason, (void*)&A64Core::Run);
} while (hr == HaltReason(0));
return hr;
}
};
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
@@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#include "common/common_types.h"
namespace Dynarmic::Backend::PPC64 {
/*
https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
r0 Volatile register used in function prologs
r1 Stack frame pointer
r2 TOC pointer
r3 Volatile parameter and return value register
r4-r10 Volatile registers used for function parameters
r11 Volatile register used in calls by pointer and as an environment pointer for languages which require one
r12 Volatile register used for exception handling and glink code
r13 Reserved for use as system thread ID
r14-r31 Nonvolatile registers used for local variables
f0 Volatile scratch register
f1-f4 Volatile floating point parameter and return value registers
f5-f13 Volatile floating point parameter registers
f14-f31 Nonvolatile registers
LR Link register (volatile)
CTR Loop counter register (volatile)
XER Fixed point exception register (volatile)
FPSCR Floating point status and control register (volatile)
CR0-CR1 Volatile condition code register fields
CR2-CR4 Nonvolatile condition code register fields
CR5-CR7 Volatile condition code register fields
v0-v1 Volatile scratch registers
v2-v13 Volatile vector parameters registers
v14-v19 Volatile scratch registers
v20-v31 Non-volatile registers
vrsave Non-volatile 32-bit register
*/
// Jit fn signature => (AXXAddressSpace& process, AXXJitState& thread_ctx, volatile u32* halt_reason)
constexpr powah::GPR RPROCESS = powah::R3;
constexpr powah::GPR RJIT = powah::R4;
constexpr powah::GPR RHALTREASON = powah::R5;
constexpr powah::GPR RTOCPTR = powah::R6;
// temporals
constexpr powah::GPR RNZCV = powah::R7;
constexpr powah::GPR ABI_PARAM1 = powah::R3;
constexpr powah::GPR ABI_PARAM2 = powah::R4;
constexpr powah::GPR ABI_PARAM3 = powah::R5;
constexpr powah::GPR ABI_PARAM4 = powah::R6;
// See https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
constexpr std::initializer_list<u32> GPR_ORDER{
// r13 is thread-id
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 //non-volatile
};
constexpr std::initializer_list<powah::GPR> ABI_CALLEER_SAVED{
powah::R3, powah::R4, powah::R5, powah::R6,
powah::R7, powah::R8, powah::R9, powah::R10,
powah::R11, powah::R12
};
constexpr std::initializer_list<powah::GPR> ABI_CALLEE_SAVED{
powah::R14, powah::R15, powah::R16, powah::R17,
powah::R18, powah::R19, powah::R20, powah::R21,
powah::R22, powah::R23, powah::R24, powah::R25,
powah::R26, powah::R27, powah::R28, powah::R29,
powah::R30, powah::R31
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <cstdint>
#include <cstdio>
#include <new>
#include <sys/mman.h>
#include "common/common_types.h"
#include "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
@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
#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;
// label used when returning :)
powah::Label l_return;
};
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,305 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <bit>
#include <cstdint>
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/backend/ppc64/stack_layout.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
#include "stack_layout.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::Void>(powah::Context&, EmitContext&, IR::Inst*) {}
template<>
void EmitIR<IR::Opcode::Identity>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.MR(result, source);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::Breakpoint>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CallHostFunction>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PushRSB>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetCarryFromOp>(powah::Context&, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetOverflowFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetGEFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetNZCVFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
if (ctx.reg_alloc.IsValueLive(inst)) {
ASSERT(false && "unimp value live");
return;
}
// All logical operations whom set (RC) are going to compute as the following:
// 1. Rt <- logical_op (Ra, Rb)
// 2. Compare as Signed(Rt) against 0
// Basically, it's equivalent to say:
//
// add r0, r1, r2 -> add. r0, r1, r2
// cmpli r0, 0
//
// CR0:
// 0 - 0x08 - N/LT, result is negative
// 1 - 0x04 - P/GT, result is positive
// 2 - 0x02 - Z/EQ, result is zero
// 3 - 0x01 - S/SO, summary overflow (carry?)
// XER:
// 32 - SO
// 33 - Overflow
// 34 - Carry
// NZCV -> (CR0.0, CR0.2, XER.34, XER.33)
if (false) {
//code.MFSPR(powah::GPR{1}, tmp4, powah::R0); // From XER
/*uint64_t nzcv(uint64_t cr0, uint64_t xer) {
return ((xer >> 33) & 1)
| (((xer >> 34) & 1) << 1)
| (((cr0 >> (32 + 2)) & 1) << 2)
| (((cr0 >> (32 + 0)) & 1) << 3);
}*/
// auto const tmp9 = ctx.reg_alloc.ScratchGpr();
// auto const tmp10 = ctx.reg_alloc.ScratchGpr();
// code.SRDI(tmp9, tmp3, 34);
// code.RLDICL(tmp10, tmp4, 31, 63);
// code.SRDI(tmp3, tmp3, 32);
// code.RLDIC(tmp9, tmp9, 2, 61);
// code.OR(tmp9, tmp9, tmp10);
// code.RLDIC(tmp3, tmp3, 3, 60);
// code.SRDI(tmp4, tmp4, 34);
// code.OR(tmp3, tmp9, tmp3);
// code.RLDIC(tmp4, tmp4, 1, 62);
// code.OR(tmp3, tmp3, tmp4);
} else {
// MFCR Fills RT 32:63, RT 0:31 left blank
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
code.MR(tmp3, PPC64::RNZCV);
ctx.reg_alloc.DefineValue(inst, tmp3);
}
}
template<>
void EmitIR<IR::Opcode::GetNZFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetUpperFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetLowerFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::GetCFlagFromNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::NZCVFromPackedFlags>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
namespace {
void EmitLeafTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
ASSERT(false && "unimp");
}
// r3 -> process
// r4 -> thread_ctx
// r5 -> halt_reason
// r6 -> test_thunk
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.B(ctx.l_return);
} 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 is_single_step) {
EmitTerminal(code, ctx, IR::Term::LinkBlock{terminal.next}, initial_location, is_single_step);
}
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.LWZ(tmp, PPC64::RJIT, offsetof(A64JitState, check_bit));
code.CMPLDI(tmp, 0);
code.BEQ(powah::CR0, l_else);
// CheckBit == 1
EmitLeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
code.B(l_end);
// CheckBit == 0
code.LABEL(l_else);
EmitLeafTerminal(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 EmitLeafTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
UNREACHABLE();
}
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
return EmitLeafTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::If>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
UNREACHABLE();
}
}
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, code.DefineLabel()};
size_t const stack_size = 112 + ABI_CALLEE_SAVED.size() * 8;
auto const start_offset = code.offset;
ebi.entry_point = &code.base[start_offset];
if (!block.instructions.empty()) {
code.MFLR(powah::R0);
code.STD(powah::R0, powah::R1, 16);
// Non-volatile saves
std::vector<powah::GPR> gp_regs{ABI_CALLEE_SAVED};
for (size_t i = 0; i < gp_regs.size(); ++i)
code.STD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
code.STDU(powah::R1, powah::R1, uint32_t(-stack_size));
code.STD(powah::R2, powah::R1, 40);
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) {
IR::Inst* inst = &*iter;
switch (inst->GetOpcode()) {
#define OPCODE(name, type, ...) \
case IR::Opcode::name: \
EmitIR<IR::Opcode::name>(code, ctx, inst); \
break;
#define A32OPC(name, type, ...) \
case IR::Opcode::A32##name: \
EmitIR<IR::Opcode::A32##name>(code, ctx, inst); \
break;
#define A64OPC(name, type, ...) \
case IR::Opcode::A64##name: \
EmitIR<IR::Opcode::A64##name>(code, ctx, inst); \
break;
#include "dynarmic/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
default:
UNREACHABLE();
}
}
// auto const cycles_to_add = block.CycleCount();
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
code.LABEL(ctx.l_return);
code.ADDI(powah::R1, powah::R1, stack_size);
for (size_t i = 0; i < gp_regs.size(); ++i)
code.LD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
code.LD(powah::R0, powah::R1, 16);
code.MTLR(powah::R0);
code.LI(powah::R3, 0);
} else {
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
code.LABEL(ctx.l_return);
code.LI(powah::R3, 67); //non-zero ret
}
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
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <vector>
#include <powah_emit.hpp>
#include "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
@@ -0,0 +1,318 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/frontend/A32/a32_types.h"
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::A32SetCheckBit>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
auto const result = ctx.reg_alloc.ScratchGpr();
code.ADDI(result, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
code.LWZ(result, result, offsetof(A32JitState, regs));
ctx.reg_alloc.DefineValue(inst, result);
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A32GetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetVector>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
auto const addr = ctx.reg_alloc.ScratchGpr();
code.ADDI(addr, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
code.STW(value, addr, offsetof(A32JitState, regs));
} else {
ASSERT(false && "unimp");
}
}
template<>
void EmitIR<IR::Opcode::A32SetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetVector>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZCVRaw>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZCVQ>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZ>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZC>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetCFlag>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const result = ctx.reg_alloc.ScratchGpr();
code.LD(result, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
code.RLWINM(result, result, 31, 31, 31);
ctx.reg_alloc.DefineValue(inst, result);
}
template<>
void EmitIR<IR::Opcode::A32OrQFlag>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetGEFlagsCompressed>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32BXWritePC>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32UpdateUpperLocationDescriptor>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CallSupervisor>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExceptionRaised>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32DataSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32DataMemoryBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32InstructionSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32GetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32SetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Memory
template<>
void EmitIR<IR::Opcode::A32ClearExclusive>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32WriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Coprocesor
template<>
void EmitIR<IR::Opcode::A32CoprocInternalOperation>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocSendOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocSendTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocGetOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocGetTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocLoadWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::A32CoprocStoreWords>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,341 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/frontend/A64/a64_types.h"
#include "dynarmic/backend/ppc64/a64_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::A64SetCheckBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(0));
code.STW(value, PPC64::RJIT, offsetof(A64JitState, 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...
auto const offs = offsetof(A64JitState, regs)
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
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
@@ -0,0 +1,933 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "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::BitRotateRight32>(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::BitRotateRight64>(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
@@ -0,0 +1,458 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::FPAbs16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAbs32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAbs64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPCompare32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPCompare64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDiv32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDiv64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMax32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMax64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMaxNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMaxNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMin32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMin64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMinNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMinNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMul32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMul64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulSub16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulX32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPMulX64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPNeg16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPNeg32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPNeg64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipExponent16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipExponent32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipExponent64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRecipStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRoundInt16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRoundInt32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRoundInt64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPRSqrtStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSqrt32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSqrt64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPDoubleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPHalfToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPSingleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedU64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::FPFixedS64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,380 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <powah_emit.hpp>
#include <fmt/ostream.h>
#include "dynarmic/backend/ppc64/a32_core.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/emit_context.h"
#include "dynarmic/backend/ppc64/emit_ppc64.h"
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::PPC64 {
template<>
void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSubWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::UnsignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Packed
template<>
void EmitIR<IR::Opcode::PackedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedHalvingSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSaturatedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedAbsDiffSumU8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::PackedSelect>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
// Crypto
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32Castagnoli64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO8>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO16>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO32>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::CRC32ISO64>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESDecryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESEncryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESInverseMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::AESMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SM4AccessSubstitutionBox>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SHA256Hash>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SHA256MessageSchedule0>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
template<>
void EmitIR<IR::Opcode::SHA256MessageSchedule1>(powah::Context&, EmitContext&, IR::Inst*) {
ASSERT(false && "unimp");
}
} // namespace Dynarmic::Backend::RV64
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/interface/exclusive_monitor.h"
#include <algorithm>
#include "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
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "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
@@ -0,0 +1,213 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <array>
#include <ranges>
#include <variant>
#include "dynarmic/backend/ppc64/reg_alloc.h"
#include "dynarmic/backend/ppc64/abi.h"
#include "common/assert.h"
#include "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
@@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <optional>
#include <random>
#include <utility>
#include <vector>
#include <powah_emit.hpp>
#include <ankerl/unordered_dense.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/backend/ppc64/stack_layout.h"
#include "dynarmic/backend/ppc64/hostloc.h"
#include "dynarmic/ir/cond.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/value.h"
namespace Dynarmic::Backend::PPC64 {
struct HostLocInfo final {
std::vector<const IR::Inst*> values;
size_t uses_this_inst = 0;
size_t accumulated_uses = 0;
size_t expected_uses = 0;
/// @brief Lock usage of this register UNTIL a DefineValue() is issued
bool locked = false;
bool realized = false;
bool Contains(const IR::Inst* value) const {
return std::find(values.begin(), values.end(), value) != values.end();
}
void SetupScratchLocation() {
ASSERT(IsCompletelyEmpty());
realized = true;
}
bool IsCompletelyEmpty() const {
return values.empty() && !locked && !realized && !accumulated_uses && !expected_uses && !uses_this_inst;
}
void UpdateUses();
};
struct RegAlloc;
/// @brief Allows to use RAII to denote liveness/locking of a given register
/// this basically means that we can use temporals and not need to go thru
/// any weird deallocation stuffs :)
template<typename T> struct RegLock {
constexpr RegLock(RegAlloc& reg_alloc, T const value) noexcept
: reg_alloc{reg_alloc}
, value{value}
{
SetLock(true);
}
inline ~RegLock() noexcept { SetLock(false); }
constexpr operator T const&() noexcept { return value; }
constexpr operator T() const noexcept { return value; }
inline void SetLock(bool v) noexcept;
RegAlloc& reg_alloc;
const T value;
};
struct RegAlloc {
explicit RegAlloc(powah::Context& code) : code{code} {}
bool IsValueLive(IR::Inst* inst) const;
void DefineAsExisting(IR::Inst* inst, IR::Value arg);
void SpillAll();
void UpdateAllUses();
void AssertNoMoreUses() const;
RegLock<powah::GPR> ScratchGpr();
RegLock<powah::GPR> UseGpr(IR::Value arg);
void DefineValue(IR::Inst* inst, powah::GPR const gpr) noexcept;
void DefineValue(IR::Inst* inst, IR::Value arg) noexcept;
private:
template<typename T>
friend struct RegLock;
std::optional<u32> AllocateRegister(const std::array<HostLocInfo, 32>& regs) const;
void SpillGpr(u32 index);
void SpillFpr(u32 index);
u32 FindFreeSpill() const;
std::optional<HostLoc> ValueLocation(const IR::Inst* value) const;
HostLocInfo& ValueInfo(HostLoc host_loc);
HostLocInfo& ValueInfo(const IR::Inst* value);
powah::Context& code;
std::array<HostLocInfo, 32> gprs;
std::array<HostLocInfo, 32> fprs;
std::array<HostLocInfo, 32> vprs;
std::array<HostLocInfo, SpillCount> spills;
};
template<> inline void RegLock<powah::GPR>::SetLock(bool v) noexcept {
reg_alloc.gprs[value.index].locked = v;
}
template<> inline void RegLock<powah::FPR>::SetLock(bool v) noexcept {
reg_alloc.fprs[value.index].locked = v;
}
template<> inline void RegLock<powah::VPR>::SetLock(bool v) noexcept {
reg_alloc.vprs[value.index].locked = v;
}
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
namespace Dynarmic::Backend::PPC64 {
constexpr size_t SpillCount = 16;
struct alignas(16) StackLayout {
std::array<u64, SpillCount> spill;
};
static_assert(sizeof(StackLayout) % 16 == 0);
} // namespace Dynarmic::Backend::RV64
@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <mutex>
#include <sys/mman.h>
#include <powah_emit.hpp>
#include "dynarmic/backend/ppc64/abi.h"
#include "dynarmic/backend/ppc64/hostloc.h"
#include "dynarmic/common/spin_lock.h"
#include "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
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <powah_emit.hpp>
namespace Dynarmic {
void EmitSpinLockLock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp);
void EmitSpinLockUnlock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp);
} // namespace Dynarmic
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "network/verify_user.h"
namespace Network {
+4 -8
View File
@@ -10,7 +10,7 @@
#include <thread>
#include "common/assert.h"
#include "common/polyfill_thread.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "enet/enet.h"
#include "network/packet.h"
#include "network/room_member.h"
@@ -358,23 +358,19 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
// Parse the ProxyPacket from the packet
u8 local_family;
packet.Read(local_family);
proxy_packet.local_endpoint.len = 16;
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
packet.Read(proxy_packet.local_endpoint.ip);
packet.Read(proxy_packet.local_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 remote_family;
packet.Read(remote_family);
proxy_packet.remote_endpoint.len = 16;
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
packet.Read(proxy_packet.remote_endpoint.ip);
packet.Read(proxy_packet.remote_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 protocol_type;
packet.Read(protocol_type);
proxy_packet.protocol = Network::Protocol(protocol_type);
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
packet.Read(proxy_packet.broadcast);
packet.Read(proxy_packet.data);
+4 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "network/room.h"
namespace Network {
@@ -39,8 +39,8 @@ struct LDNPacket {
/// Information about the received proxy packets.
struct ProxyPacket {
Network::SockAddrIn local_endpoint;
Network::SockAddrIn remote_endpoint;
SockAddrIn local_endpoint;
SockAddrIn remote_endpoint;
Protocol protocol;
bool broadcast;
std::vector<u8> data;
+2 -7
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -19,11 +16,9 @@ TEST_CASE("Network::Errors", "[core]") {
}
Network::SockAddrIn addr{
16,
u8(Network::Domain::INET),
1, // hopefully nobody running this test has something listening on port 1
Network::Domain::INET,
{127, 0, 0, 1},
{},
1, // hopefully nobody running this test has something listening on port 1
};
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
+1
View File
@@ -19,6 +19,7 @@ Tools for Eden and other subprojects. When adding new scripts please use `#!/bin
- `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
+86
View File
@@ -0,0 +1,86 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
die() {
echo "$@" >&2
exit 1
}
help() {
cat << EOF
--arch <name> Specify the target architecture (default: ppc64)
--os <name> Specify the OS sysroot to use (default: freebsd)
--sysroot <path> Specify sysroot to use
EOF
}
TARGET_ARCH="ppc64"
TARGET_OS="freebsd"
TARGET_CMAKE="$TARGET_ARCH-pc-$TARGET_OS"
BASE_DIR="$PWD"
while true; do
case "$1" in
--arch) shift; TARGET_ARCH=$1; [ -z "$TARGET_ARCH" ] && die "Expected argument";;
--os) shift; TARGET_OS=$1; [ -z "$TARGET_OS" ] && die "Expected argument";;
--sysroot) shift; SYSROOT=$1; [ -z "$SYSROOT" ] && die "Expected argument";;
--help) help "$@";;
--*) die "Invalid option $1" ;;
"$0" | "") break;;
esac
shift
done
[ -z "$SYSROOT" ] && SYSROOT="$HOME/opt/$TARGET_ARCH-$TARGET_OS/sysroot"
mkdir -p "$SYSROOT" && cd "$SYSROOT"
case "$TARGET_OS" in
freebsd*)
case "$TARGET_ARCH" in
ppc64pe) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc64pe/14.3-RELEASE/base.txz" break;;
ppc64le) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc64le/14.3-RELEASE/base.txz" break;;
ppc64) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc64/14.3-RELEASE/base.txz" break;;
ppc) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc/14.3-RELEASE/base.txz" break;;
amd64 | x86_64) URL="https://download.freebsd.org/ftp/releases/amd64/14.3-RELEASE/base.txz" break;;
arm64*) URL="https://download.freebsd.org/ftp/releases/arm64/$TARGET_ARCH/14.3-RELEASE/base.txz" break;;
arm*) URL="https://download.freebsd.org/ftp/releases/arm/$TARGET_ARCH/14.3-RELEASE/base.txz" break;;
*) die "Unknown arch $TARGET_ARCH" break;;
esac
[ -z "$TARGET_CMAKE" ] && TARGET_CMAKE="$TARGET_ARCH-pc-$TARGET_OS"
[ -f "base.txz" ] || fetch "$URL" || die "Can't download"
tar -xvzf base.txz
break;;
*) die "Unknown OS $TARGET_OS" break;;
esac
[ -z "$CC" ] && CC=$(which clang)
[ -z "$CXX" ] && CXX=$(which clang++)
TOOLCHAIN_FILE="$BASE_DIR/$TARGET_CMAKE-toolchain.cmake"
cat << EOF >"$TOOLCHAIN_FILE"
# Script to generate .cmake toolchain files :)
# See https://man.freebsd.org/cgi/man.cgi?query=cmake-toolchains&sektion=7&manpath=FreeBSD+13.2-RELEASE+and+Ports
set(CMAKE_SYSROOT "$SYSROOT")
set(CMAKE_STAGING_PREFIX "$SYSROOT")
set(CMAKE_C_COMPILER $CC)
set(CMAKE_CXX_COMPILER $CXX)
set(CMAKE_C_FLAGS "--target=$TARGET_CMAKE")
set(CMAKE_CXX_FLAGS "--target=$TARGET_CMAKE")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
EOF
# cmake \
# -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \
# -DCMAKE_BUILD_TYPE=Release \
# -B "build-$TARGET_CMAKE" \
# -DDYNARMIC_TESTS=ON \
# -DENABLE_QT=OFF \
# -DENABLE_SDL2=OFF \
# -DYUZU_USE_CPM=ON \
# -DYUZU_USE_EXTERNAL_FFMPEG=ON
#cmake --build "build-$TARGET_CMAKE" dynarmic_tests -- -j8